9 |
9 |
10 import os |
10 import os |
11 import sys |
11 import sys |
12 import contextlib |
12 import contextlib |
13 |
13 |
14 from PyQt5.QtCore import ( |
14 from PyQt6.QtCore import ( |
15 pyqtSlot, QCoreApplication, QTimer |
15 pyqtSlot, QCoreApplication, QTimer |
16 ) |
16 ) |
17 |
17 |
18 from E5Gui.E5Application import e5App |
18 from EricWidgets.EricApplication import ericApp |
19 from E5Gui import E5MessageBox |
19 from EricWidgets import EricMessageBox |
|
20 |
|
21 from EricNetwork.EricJsonServer import EricJsonServer |
20 |
22 |
21 from QScintilla.Editor import Editor |
23 from QScintilla.Editor import Editor |
22 |
|
23 try: |
|
24 from E5Network.E5JsonServer import E5JsonServer |
|
25 except ImportError: |
|
26 # TODO: delete JsonServer once ported to eric7 |
|
27 from .JsonServer import JsonServer as E5JsonServer |
|
28 |
24 |
29 import Globals |
25 import Globals |
30 import Preferences |
26 import Preferences |
31 |
27 |
32 |
28 |
33 class CodeAssistServer(E5JsonServer): |
29 class CodeAssistServer(EricJsonServer): |
34 """ |
30 """ |
35 Class implementing the autocompletion interface to rope. |
31 Class implementing the autocompletion interface to rope. |
36 """ |
32 """ |
37 IdProject = "Project" |
33 IdProject = "Project" |
38 |
34 |
64 super().__init__( |
60 super().__init__( |
65 "CodeAssistServer", multiplex=True, parent=parent) |
61 "CodeAssistServer", multiplex=True, parent=parent) |
66 |
62 |
67 self.__plugin = plugin |
63 self.__plugin = plugin |
68 self.__ui = parent |
64 self.__ui = parent |
69 self.__vm = e5App().getObject("ViewManager") |
65 self.__vm = ericApp().getObject("ViewManager") |
70 self.__e5project = e5App().getObject("Project") |
66 self.__e5project = ericApp().getObject("Project") |
71 |
67 |
72 self.__editorLanguageMapping = {} |
68 self.__editorLanguageMapping = {} |
73 self.__clientConfigs = {} |
69 self.__clientConfigs = {} |
74 self.__editors = {} |
70 self.__editors = {} |
75 |
|
76 self.__asyncCompletions = False |
|
77 |
71 |
78 self.__documentationViewer = None |
72 self.__documentationViewer = None |
79 |
73 |
80 # attributes to store the resuls of the client side |
74 # attributes to store the resuls of the client side |
81 self.__completions = None |
75 self.__completions = None |
104 } |
98 } |
105 |
99 |
106 # Python 3 |
100 # Python 3 |
107 self.__ensureActive("Python3") |
101 self.__ensureActive("Python3") |
108 |
102 |
109 def setAsyncCompletions(self, asynchronous): |
|
110 """ |
|
111 Public method to set the asynchronous completions flag. |
|
112 |
|
113 @param asynchronous flag indicating asynchronous completions |
|
114 @type bool |
|
115 """ |
|
116 self.__asyncCompletions = asynchronous |
|
117 |
|
118 def __updateEditorLanguageMapping(self): |
103 def __updateEditorLanguageMapping(self): |
119 """ |
104 """ |
120 Private method to update the editor language to connection mapping. |
105 Private method to update the editor language to connection mapping. |
121 """ |
106 """ |
122 self.__editorLanguageMapping = {} |
107 self.__editorLanguageMapping = {} |
125 self.__editorLanguageMapping.update({ |
110 self.__editorLanguageMapping.update({ |
126 "Python3": "Python3", |
111 "Python3": "Python3", |
127 "MicroPython": "Python3", |
112 "MicroPython": "Python3", |
128 "Pygments|Python": "Python3", |
113 "Pygments|Python": "Python3", |
129 "Pygments|Python 2.x": "Python3", |
114 "Pygments|Python 2.x": "Python3", |
|
115 "Cython": "Python3", |
130 }) |
116 }) |
131 |
117 |
132 def isSupportedLanguage(self, language): |
118 def isSupportedLanguage(self, language): |
133 """ |
119 """ |
134 Public method to check, if the given language is supported. |
120 Public method to check, if the given language is supported. |
227 editor.editorSaved.connect( |
213 editor.editorSaved.connect( |
228 lambda: self.__configChanged(idString)) |
214 lambda: self.__configChanged(idString)) |
229 self.__editors[idString] = editor |
215 self.__editors[idString] = editor |
230 return |
216 return |
231 else: |
217 else: |
232 E5MessageBox.critical( |
218 EricMessageBox.critical( |
233 self.__ui, |
219 self.__ui, |
234 self.tr("Configure Rope"), |
220 self.tr("Configure Rope"), |
235 self.tr("""The Rope configuration file '{0}' does""" |
221 self.tr("""The Rope configuration file '{0}' does""" |
236 """ not exist.""").format(configfile)) |
222 """ not exist.""").format(configfile)) |
237 |
223 |
238 def getCompletions(self, editor, context): |
224 def requestCompletions(self, editor, context, acText): |
239 """ |
225 """ |
240 Public method to calculate the possible completions. |
226 Public method to request a list of possible completions. |
241 |
|
242 Note: This is the synchronous variant for eric6 before 17.11. |
|
243 |
227 |
244 @param editor reference to the editor object, that called this method |
228 @param editor reference to the editor object, that called this method |
245 @type QScintilla.Editor.Editor |
229 @type Editor |
246 @param context flag indicating to autocomplete a context |
|
247 @type bool |
|
248 @return list of possible completions |
|
249 @rtype list of str |
|
250 """ |
|
251 if not self.__plugin.getPreferences("CodeAssistEnabled"): |
|
252 return [] |
|
253 |
|
254 # reset the completions buffer |
|
255 self.__completions = None |
|
256 |
|
257 if not self.__idString(editor): |
|
258 return [] |
|
259 |
|
260 self.requestCompletions(editor, context, "") |
|
261 |
|
262 # emulate the synchronous behaviour |
|
263 timer = QTimer() |
|
264 timer.setSingleShot(True) |
|
265 timer.start(5000) # 5s timeout |
|
266 while self.__completions is None and timer.isActive(): |
|
267 QCoreApplication.processEvents() |
|
268 |
|
269 return [] if self.__completions is None else self.__completions |
|
270 |
|
271 def requestCompletions(self, editor, context, acText): |
|
272 """ |
|
273 Public method to request a list of possible completions. |
|
274 |
|
275 Note: This is part of the asynchronous variant for eric6 17.11 and |
|
276 later. |
|
277 |
|
278 @param editor reference to the editor object, that called this method |
|
279 @type QScintilla.Editor.Editor |
|
280 @param context flag indicating to autocomplete a context |
230 @param context flag indicating to autocomplete a context |
281 @type bool |
231 @type bool |
282 @param acText text to be completed |
232 @param acText text to be completed |
283 @type str |
233 @type str |
284 """ |
234 """ |
318 |
268 |
319 name += CodeAssistServer.PictureIDs.get( |
269 name += CodeAssistServer.PictureIDs.get( |
320 completion['CompletionType'], '') |
270 completion['CompletionType'], '') |
321 names.append(name) |
271 names.append(name) |
322 |
272 |
323 if self.__asyncCompletions: |
273 if "Error" not in result: |
324 # asynchronous variant for eric6 17.11 and later |
274 editor = self.__vm.getOpenEditor(result["FileName"]) |
325 if "Error" not in result: |
275 if editor is not None: |
326 editor = self.__vm.getOpenEditor(result["FileName"]) |
276 editor.completionsListReady(names, |
327 if editor is not None: |
277 result["CompletionText"]) |
328 editor.completionsListReady(names, |
|
329 result["CompletionText"]) |
|
330 else: |
|
331 # synchronous variant for eric6 before 17.11 |
|
332 if "Error" in result: |
|
333 self.__completions = [] |
|
334 else: |
|
335 self.__completions = result["Completions"] |
|
336 |
278 |
337 def getCallTips(self, editor, pos, commas): |
279 def getCallTips(self, editor, pos, commas): |
338 """ |
280 """ |
339 Public method to calculate calltips. |
281 Public method to calculate calltips. |
340 |
282 |
341 @param editor reference to the editor object, that called this method |
283 @param editor reference to the editor object, that called this method |
342 @type QScintilla.Editor.Editor |
284 @type Editor |
343 @param pos position in the text for the calltip |
285 @param pos position in the text for the calltip |
344 @type int |
286 @type int |
345 @param commas minimum number of commas contained in the calltip |
287 @param commas minimum number of commas contained in the calltip |
346 @type int |
288 @type int |
347 @return list of possible calltips |
289 @return list of possible calltips |
549 location = result["Location"] |
491 location = result["Location"] |
550 self.__vm.openSourceFile(location["ModulePath"], |
492 self.__vm.openSourceFile(location["ModulePath"], |
551 location["Line"], |
493 location["Line"], |
552 addNext=True) |
494 addNext=True) |
553 else: |
495 else: |
554 e5App().getObject("UserInterface").statusBar().showMessage( |
496 ericApp().getObject("UserInterface").statusBar().showMessage( |
555 self.tr('Code Assist: No definition found'), 5000) |
497 self.tr('Code Assist: No definition found'), 5000) |
556 |
498 |
557 ####################################################################### |
499 ####################################################################### |
558 ## Methods below handle the network connection |
500 ## Methods below handle the network connection |
559 ####################################################################### |
501 ####################################################################### |
626 |
568 |
627 client = os.path.join(os.path.dirname(__file__), |
569 client = os.path.join(os.path.dirname(__file__), |
628 "CodeAssistClient.py") |
570 "CodeAssistClient.py") |
629 ok, exitCode = self.startClient( |
571 ok, exitCode = self.startClient( |
630 interpreter, client, |
572 interpreter, client, |
631 clientArgs=[configDir, Globals.getPythonModulesDirectory()], |
573 clientArgs=[configDir, Globals.getPythonLibraryDirectory()], |
632 idString=idString, environment=clientEnv) |
574 idString=idString, environment=clientEnv) |
633 if not ok: |
575 if not ok: |
634 if exitCode == 42: |
576 if exitCode == 42: |
635 self.__ui.appendToStderr("CodeAssistServer: " + self.tr( |
577 self.__ui.appendToStderr("CodeAssistServer: " + self.tr( |
636 "The rope refactoring library is not installed.\n" |
578 "The rope refactoring library is not installed.\n" |
668 interpreter = "" |
610 interpreter = "" |
669 venvName = "" |
611 venvName = "" |
670 clientEnv = os.environ.copy() |
612 clientEnv = os.environ.copy() |
671 if "PATH" in clientEnv: |
613 if "PATH" in clientEnv: |
672 clientEnv["PATH"] = self.__ui.getOriginalPathString() |
614 clientEnv["PATH"] = self.__ui.getOriginalPathString() |
673 venvManager = e5App().getObject("VirtualEnvManager") |
615 venvManager = ericApp().getObject("VirtualEnvManager") |
674 if idString == "Python3": |
616 if idString == "Python3": |
675 venvName = Preferences.getDebugger("Python3VirtualEnv") |
617 venvName = Preferences.getDebugger("Python3VirtualEnv") |
676 if not venvName: |
618 if not venvName: |
677 venvName, _ = venvManager.getDefaultEnvironment() |
619 venvName, _ = venvManager.getDefaultEnvironment() |
678 if venvName: |
620 if venvName: |
709 clientEnv = os.environ.copy() |
651 clientEnv = os.environ.copy() |
710 if "PATH" in clientEnv: |
652 if "PATH" in clientEnv: |
711 clientEnv["PATH"] = self.__ui.getOriginalPathString() |
653 clientEnv["PATH"] = self.__ui.getOriginalPathString() |
712 |
654 |
713 if projectLanguage.startswith("Python"): |
655 if projectLanguage.startswith("Python"): |
714 venvManager = e5App().getObject("VirtualEnvManager") |
656 venvManager = ericApp().getObject("VirtualEnvManager") |
715 |
657 |
716 # get virtual environment from project first |
658 # get virtual environment from project first |
717 venvName = self.__e5project.getDebugProperty("VIRTUALENV") |
659 venvName = self.__e5project.getDebugProperty("VIRTUALENV") |
718 if not venvName: |
660 if not venvName: |
719 # get it from debugger settings next |
661 # get it from debugger settings next |