RefactoringRope/CodeAssistServer.py

branch
server_client_variant
changeset 212
f05681349336
parent 209
c1dce8630555
child 217
874115c79ca7
equal deleted inserted replaced
211:891b660bcbda 212:f05681349336
12 import os 12 import os
13 13
14 from PyQt5.QtCore import pyqtSlot, QCoreApplication, QTimer 14 from PyQt5.QtCore import pyqtSlot, QCoreApplication, QTimer
15 15
16 from E5Gui.E5Application import e5App 16 from E5Gui.E5Application import e5App
17 from E5Gui import E5MessageBox
17 18
18 from .JsonServer import JsonServer 19 from .JsonServer import JsonServer
19 20
20 import Globals 21 import Globals
21 import Preferences 22 import Preferences
37 super(CodeAssistServer, self).__init__( 38 super(CodeAssistServer, self).__init__(
38 "CodeAssistServer", multiplex=True, parent=parent) 39 "CodeAssistServer", multiplex=True, parent=parent)
39 40
40 self.__plugin = plugin 41 self.__plugin = plugin
41 self.__ui = parent 42 self.__ui = parent
43 self.__vm = e5App().getObject("ViewManager")
42 44
43 self.__editorLanguageMapping = {} 45 self.__editorLanguageMapping = {}
46 self.__clientConfigs = {}
47 self.__editors = {}
48
49 self.__asyncCompletions = False
44 50
45 # attributes to store the resuls of the client side 51 # attributes to store the resuls of the client side
46 self.__completions = None 52 self.__completions = None
47 self.__calltips = None 53 self.__calltips = None
48 54
49 self.__methodMapping = { 55 self.__methodMapping = {
56 "Config": self.__setConfig,
50 "CompletionsResult": self.__processCompletionsResult, 57 "CompletionsResult": self.__processCompletionsResult,
51 "CallTipsResult": self.__processCallTipsResult, 58 "CallTipsResult": self.__processCallTipsResult,
52 59
53 "ClientException": self.__processClientException, 60 "ClientException": self.__processClientException,
54 } 61 }
75 self.__editorLanguageMapping.update({ 82 self.__editorLanguageMapping.update({
76 "Python3": "Python3", 83 "Python3": "Python3",
77 "Pygments|Python 3": "Python3", 84 "Pygments|Python 3": "Python3",
78 }) 85 })
79 86
87 def __getConfigs(self):
88 """
89 Private method to get the configurations of all connected clients.
90 """
91 for idString in self.connectionNames():
92 self.sendJson("getConfig", {}, idString=idString)
93
94 def __setConfig(self, params):
95 """
96 Private method to set the rope client configuration data.
97
98 @param params dictionary containing the configuration data
99 @type dict
100 """
101 idString = params["Id"]
102 ropeFolder = params["RopeFolderName"]
103
104 self.__clientConfigs[idString] = ropeFolder
105
106 def __ropeConfigFile(self, idString):
107 """
108 Private method to get the name of the rope configuration file.
109
110 @param idString id for which to get the configuration file
111 @type str
112 @return name of the rope configuration file
113 @rtype str
114 """
115 configfile = None
116 if idString in self.__clientConfigs:
117 ropedir = self.__clientConfigs[idString]
118 if ropedir:
119 configfile = os.path.join(ropedir, "config.py")
120 if not os.path.exists(configfile):
121 configfile = None
122 return configfile
123
124 def __configChanged(self, idString):
125 """
126 Private slot called, when the rope config file has changed.
127
128 @param idString id for which to get the configuration file
129 @type str
130 """
131 self.sendJson("configChanged", {}, idString=idString)
132
133 def editConfig(self, idString):
134 """
135 Public slot to open the rope configuration file in an editor.
136
137 @param idString id for which to get the configuration file
138 @type str
139 """
140 configfile = self.__ropeConfigFile(idString)
141 if configfile:
142 if os.path.exists(configfile):
143 from QScintilla.MiniEditor import MiniEditor
144 editor = MiniEditor(configfile)
145 editor.show()
146 editor.editorSaved.connect(
147 lambda: self.__configChanged(idString))
148 self.__editors[idString] = editor
149 return
150 else:
151 E5MessageBox.critical(
152 self.__ui,
153 self.tr("Configure Rope"),
154 self.tr("""The Rope configuration file '{0}' does"""
155 """ not exist.""").format(configfile))
156
80 def isSupportedLanguage(self, language): 157 def isSupportedLanguage(self, language):
81 """ 158 """
82 Public method to check, if the given language is supported. 159 Public method to check, if the given language is supported.
83 160
84 @param language editor programming language to check 161 @param language editor programming language to check
86 @return flag indicating the support status 163 @return flag indicating the support status
87 @rtype bool 164 @rtype bool
88 """ 165 """
89 return language in self.__editorLanguageMapping 166 return language in self.__editorLanguageMapping
90 167
91 def getCompletions(self, editor): 168 def getCompletions(self, editor, context):
92 """ 169 """
93 Public method to calculate the possible completions. 170 Public method to calculate the possible completions.
94 171
172 Note: This is the synchronous variant for eric6 before 17.11.
173
95 @param editor reference to the editor object, that called this method 174 @param editor reference to the editor object, that called this method
96 @type QScintilla.Editor 175 @type QScintilla.Editor
97 @return list of proposals 176 @param context flag indicating to autocomplete a context
177 @type bool
178 @return list of possible completions
98 @rtype list of str 179 @rtype list of str
99 """ 180 """
100 # reset the completions buffer 181 # reset the completions buffer
101 self.__completions = None 182 self.__completions = None
102 183
103 language = editor.getLanguage() 184 language = editor.getLanguage()
104 if language not in self.__editorLanguageMapping: 185 if language not in self.__editorLanguageMapping:
105 return [] 186 return []
187
188 self.requestCompletions(editor, context, "")
189
190 # emulate the synchronous behaviour
191 timer = QTimer()
192 timer.setSingleShot(True)
193 timer.start(5000) # 5s timeout
194 while self.__completions is None and timer.isActive():
195 QCoreApplication.processEvents()
196
197 return [] if self.__completions is None else self.__completions
198
199 def requestCompletions(self, editor, context, acText):
200 """
201 Public method to request a list of possible completions.
202
203 Note: This is part of the asynchronous variant for eric6 17.11 and
204 later.
205
206 @param editor reference to the editor object, that called this method
207 @type QScintilla.Editor
208 @param context flag indicating to autocomplete a context
209 @type bool
210 @param acText text to be completed
211 @type str
212 """
213 language = editor.getLanguage()
214 if language not in self.__editorLanguageMapping:
215 return
106 idString = self.__editorLanguageMapping[language] 216 idString = self.__editorLanguageMapping[language]
107 217
108 filename = editor.getFileName() 218 filename = editor.getFileName()
109 line, index = editor.getCursorPosition() 219 line, index = editor.getCursorPosition()
110 source = editor.text() 220 source = editor.text()
115 self.sendJson("getCompletions", { 225 self.sendJson("getCompletions", {
116 "FileName": filename, 226 "FileName": filename,
117 "Source": source, 227 "Source": source,
118 "Offset": offset, 228 "Offset": offset,
119 "MaxFixes": maxfixes, 229 "MaxFixes": maxfixes,
230 "CompletionText": acText,
120 }, idString=idString) 231 }, idString=idString)
121
122 # emulate the synchronous behaviour
123 timer = QTimer()
124 timer.setSingleShot(True)
125 timer.start(5000) # 5s timeout
126 while self.__completions is None and timer.isActive():
127 QCoreApplication.processEvents()
128
129 return [] if self.__completions is None else self.__completions
130 232
131 def __processCompletionsResult(self, result): 233 def __processCompletionsResult(self, result):
132 """ 234 """
133 Private method to process the completions sent by the client. 235 Private method to process the completions sent by the client.
134 236
135 @param result dictionary containing the result sent by the client 237 @param result dictionary containing the result sent by the client
136 @type dict 238 @type dict
137 """ 239 """
138 if "Error" in result: 240 if self.__asyncCompletions:
139 self.__completions = [] 241 # asynchronous variant for eric6 17.11 and later
242 if "Error" not in result:
243 editor = self.__vm.getOpenEditor(result["FileName"])
244 if editor is not None:
245 editor.completionsListReady(result["Completions"],
246 result["CompletionText"])
140 else: 247 else:
141 self.__completions = result["Completions"] 248 # synchronous variant for eric6 before 17.11
142 249 if "Error" in result:
143 def getCallTips(self, pos, editor): 250 self.__completions = []
251 else:
252 self.__completions = result["Completions"]
253
254 def getCallTips(self, editor, pos, commas):
144 """ 255 """
145 Public method to calculate calltips. 256 Public method to calculate calltips.
146 257
258 @param editor reference to the editor object, that called this method
259 @type QScintilla.Editor
147 @param pos position in the text for the calltip 260 @param pos position in the text for the calltip
148 @type int 261 @type int
149 @param editor reference to the editor object, that called this method 262 @param commas minimum number of commas contained in the calltip
150 @type QScintilla.Editor 263 @type int
151 @return list of possible calltips 264 @return list of possible calltips
152 @rtype list of str 265 @rtype list of str
153 """ 266 """
154 # reset the calltips buffer 267 # reset the calltips buffer
155 self.__calltips = None 268 self.__calltips = None
201 @param filename file name of the changed source 314 @param filename file name of the changed source
202 @type str 315 @type str
203 @param oldSource source code before the change 316 @param oldSource source code before the change
204 @type str 317 @type str
205 """ 318 """
206 editor = e5App().getObject("ViewManager").getOpenEditor(filename) 319 editor = self.__vm.getOpenEditor(filename)
207 if editor is not None: 320 if editor is not None:
208 language = editor.getLanguage() 321 language = editor.getLanguage()
209 if language in self.__editorLanguageMapping: 322 if language in self.__editorLanguageMapping:
210 idString = self.__editorLanguageMapping[language] 323 idString = self.__editorLanguageMapping[language]
211 324
272 @rtype bool 385 @rtype bool
273 """ 386 """
274 ok = False 387 ok = False
275 388
276 if interpreter: 389 if interpreter:
390 configDir = os.path.join(Globals.getConfigDir(), "rope", idString)
391 if not os.path.exists(configDir):
392 os.makedirs(configDir)
393
277 client = os.path.join(os.path.dirname(__file__), 394 client = os.path.join(os.path.dirname(__file__),
278 "CodeAssistClient.py") 395 "CodeAssistClient.py")
279 ok = self.startClient(interpreter, client, 396 ok = self.startClient(interpreter, client, [configDir],
280 [Globals.getConfigDir()],
281 idString=idString) 397 idString=idString)
282 if not ok: 398 if not ok:
283 self.__ui.appendToStderr(self.tr( 399 self.__ui.appendToStderr(self.tr(
284 "'{0}' is not supported because the configured interpreter" 400 "'{0}' is not supported because the configured interpreter"
285 " could not be started.\n" 401 " could not be started.\n"
318 def handleNewConnection(self): 434 def handleNewConnection(self):
319 """ 435 """
320 Public slot for new incoming connections from a client. 436 Public slot for new incoming connections from a client.
321 """ 437 """
322 super(CodeAssistServer, self).handleNewConnection() 438 super(CodeAssistServer, self).handleNewConnection()
439
323 self.__updateEditorLanguageMapping() 440 self.__updateEditorLanguageMapping()
441
442 self.__getConfigs()
324 443
325 def activate(self): 444 def activate(self):
326 """ 445 """
327 Public method to activate the code assist server. 446 Public method to activate the code assist server.
328 447
336 Public method to deactivate the code assist server. 455 Public method to deactivate the code assist server.
337 """ 456 """
338 """ 457 """
339 Public method to shut down the code assist server. 458 Public method to shut down the code assist server.
340 """ 459 """
460 for idString in self.connectionNames():
461 self.sendJson("closeProject", {}, flush=True, idString=idString)
462
341 self.stopAllClients() 463 self.stopAllClients()
464
465 #######################################################################
466 ## Methods below handle setting/unsetting the hook methods
467 #######################################################################
468
469 def connectEditor(self, editor):
470 """
471 Public method to connect an editor.
472
473 @param editor reference to the editor
474 @type QScintilla.Editor
475 """
476 if self.isSupportedLanguage(editor.getLanguage()):
477 if self.__plugin.getPreferences("CodeAssistEnabled") and \
478 editor.getCompletionListHook("rope") is None:
479 self.__setAutoCompletionHook(editor)
480 if self.__plugin.getPreferences("CodeAssistCalltipsEnabled") and \
481 editor.getCallTipHook("rope") is None:
482 self.__setCalltipsHook(editor)
483 else:
484 self.disconnectEditor(editor)
485
486 def disconnectEditor(self, editor):
487 """
488 Public method to disconnect an editor.
489
490 @param editor reference to the editor
491 @type QScintilla.Editor
492 """
493 if editor.getCompletionListHook("rope"):
494 self.__unsetAutoCompletionHook(editor)
495 if editor.getCallTipHook("rope"):
496 self.__unsetCalltipsHook(editor)
497
498 def __setAutoCompletionHook(self, editor):
499 """
500 Private method to set the auto-completion hook.
501
502 @param editor reference to the editor
503 @type QScintilla.Editor
504 """
505 try:
506 editor.addCompletionListHook("rope", self.requestCompletions,
507 async=True)
508 self.__asyncCompletions = True
509 except TypeError:
510 # backward compatibility for eric6 before 17.11
511 editor.addCompletionListHook("rope", self.getCompletions)
512 self.__asyncCompletions = False
513
514 def __unsetAutoCompletionHook(self, editor):
515 """
516 Private method to unset the auto-completion hook.
517
518 @param editor reference to the editor
519 @type QScintilla.Editor
520 """
521 editor.removeCompletionListHook("rope")
522
523 def __setCalltipsHook(self, editor):
524 """
525 Private method to set the calltip hook.
526
527 @param editor reference to the editor
528 @type QScintilla.Editor
529 """
530 editor.addCallTipHook("rope", self.getCallTips)
531
532 def __unsetCalltipsHook(self, editor):
533 """
534 Private method to unset the calltip hook.
535
536 @param editor reference to the editor
537 @type QScintilla.Editor
538 """
539 editor.removeCallTipHook("rope")
540
541 # TODO: add method to edit the codeassist python2 and 3 config files

eric ide

mercurial