22 |
22 |
23 import rope |
23 import rope |
24 import rope.base.libutils |
24 import rope.base.libutils |
25 import rope.base.exceptions |
25 import rope.base.exceptions |
26 |
26 |
27 from PyQt5.QtCore import pyqtSlot, QProcess |
|
28 from PyQt5.QtWidgets import QMenu, QApplication, QDialog, QAction |
27 from PyQt5.QtWidgets import QMenu, QApplication, QDialog, QAction |
29 from PyQt5.Qsci import QsciScintilla |
28 from PyQt5.Qsci import QsciScintilla |
30 from PyQt5.QtNetwork import QTcpServer, QHostAddress |
|
31 |
29 |
32 from E5Gui.E5Application import e5App |
30 from E5Gui.E5Application import e5App |
33 from E5Gui import E5MessageBox |
31 from E5Gui import E5MessageBox |
34 from E5Gui.E5Action import E5Action |
32 from E5Gui.E5Action import E5Action |
|
33 |
|
34 from .JsonServer import JsonServer |
35 |
35 |
36 import Utilities |
36 import Utilities |
37 import Preferences |
37 import Preferences |
38 |
38 |
39 |
39 |
40 # TODO: rename this (and the module) to RefactoringServer once done |
40 # TODO: rename this (and the module) to RefactoringServer once done |
41 class Refactoring(QTcpServer): |
41 class Refactoring(JsonServer): |
42 """ |
42 """ |
43 Class implementing the refactoring interface to rope. |
43 Class implementing the refactoring interface to rope. |
44 """ |
44 """ |
45 def __init__(self, plugin, parent=None): |
45 def __init__(self, plugin, parent=None): |
46 """ |
46 """ |
59 self.__projectopen = False |
59 self.__projectopen = False |
60 |
60 |
61 self.__mainMenu = None |
61 self.__mainMenu = None |
62 self.__helpDialog = None |
62 self.__helpDialog = None |
63 |
63 |
64 self.__refactoringProcess = None |
|
65 self.__refactoringConnection = None |
|
66 |
|
67 # Rope objects |
|
68 # TODO: move this to RefactoringClient |
|
69 ## self.__project = None |
|
70 |
|
71 from FileSystemCommands import E5FileSystemCommands |
64 from FileSystemCommands import E5FileSystemCommands |
72 self.__fsCommands = E5FileSystemCommands(self.__e5project) |
65 self.__fsCommands = E5FileSystemCommands(self.__e5project) |
73 |
|
74 # setup the network interface |
|
75 networkInterface = Preferences.getDebugger("NetworkInterface") |
|
76 if networkInterface == "all" or '.' in networkInterface: |
|
77 # IPv4 |
|
78 self.__hostAddress = '127.0.0.1' |
|
79 else: |
|
80 # IPv6 |
|
81 self.__hostAddress = '::1' |
|
82 self.listen(QHostAddress(self.__hostAddress)) |
|
83 |
|
84 self.newConnection.connect(self.__handleNewConnection) |
|
85 |
|
86 port = self.serverPort() |
|
87 ## Note: Need the port if started external in debugger: |
|
88 print('Refactoring server listening on: {0:d}'.format(port)) |
|
89 # __IGNORE_WARNING__ |
|
90 |
66 |
91 def initActions(self): |
67 def initActions(self): |
92 """ |
68 """ |
93 Public method to define the refactoring actions. |
69 Public method to define the refactoring actions. |
94 """ |
70 """ |
2240 elif self.__projectLanguage == "Python3": |
2216 elif self.__projectLanguage == "Python3": |
2241 interpreter = Preferences.getDebugger("Python3Interpreter") |
2217 interpreter = Preferences.getDebugger("Python3Interpreter") |
2242 else: |
2218 else: |
2243 interpreter = "" |
2219 interpreter = "" |
2244 if interpreter: |
2220 if interpreter: |
2245 process = self.__startRefactoringClient(interpreter) |
2221 ok = self.__startRefactoringClient(interpreter) |
2246 if process is None: |
2222 if not ok: |
2247 self.__ui.appendToStderr(self.tr( |
2223 self.__ui.appendToStderr(self.tr( |
2248 "Project language '{0}' is not supported because" |
2224 "Project language '{0}' is not supported because" |
2249 " the configured interpreter could not be started." |
2225 " the configured interpreter could not be started." |
2250 " Refactoring is disabled." |
2226 " Refactoring is disabled." |
2251 ).format(self.__projectLanguage)) |
2227 ).format(self.__projectLanguage)) |
2252 else: |
2228 else: |
2253 self.__refactoringProcess = process |
|
2254 ## import rope.base.project |
|
2255 ## self.__project = rope.base.project.Project( |
|
2256 ## self.__projectpath, fscommands=self.__fsCommands) |
|
2257 for act in self.actions: |
2229 for act in self.actions: |
2258 act.setEnabled(True) |
2230 act.setEnabled(True) |
2259 else: |
2231 else: |
2260 self.__ui.appendToStderr(self.tr( |
2232 self.__ui.appendToStderr(self.tr( |
2261 "Project language '{0}' is not supported because no" |
2233 "Project language '{0}' is not supported because no" |
2268 Public slot to handle the projectClosed signal. |
2240 Public slot to handle the projectClosed signal. |
2269 """ |
2241 """ |
2270 for act in self.actions: |
2242 for act in self.actions: |
2271 act.setEnabled(False) |
2243 act.setEnabled(False) |
2272 |
2244 |
2273 self.__stopRefactoringClient() |
2245 self.stopClient() |
2274 ## if self.__project is not None: |
|
2275 ## self.__project.close() |
|
2276 ## self.__project = None |
|
2277 |
2246 |
2278 self.__projectopen = False |
2247 self.__projectopen = False |
2279 self.__projectpath = '' |
2248 self.__projectpath = '' |
2280 self.__projectLanguage = "" |
2249 self.__projectLanguage = "" |
2281 |
2250 |
2357 |
2326 |
2358 ####################################################################### |
2327 ####################################################################### |
2359 ## Methods below handle the network connection |
2328 ## Methods below handle the network connection |
2360 ####################################################################### |
2329 ####################################################################### |
2361 |
2330 |
2362 @pyqtSlot() |
2331 def handleCall(self, method, params): |
2363 def __handleNewConnection(self): |
2332 """ |
2364 """ |
2333 Public method to handle a method call from the client. |
2365 Private slot for new incomming connections from the refactoring client. |
2334 |
2366 """ |
2335 Note: This is an empty implementation that must be overridden in |
2367 if self.__refactoringConnection is not None: |
2336 derived classes. |
2368 self.__refactoringConnection.close() |
2337 |
2369 self.__refactoringConnection = None |
2338 @param method requested method name |
2370 |
|
2371 connection = self.nextPendingConnection() |
|
2372 if not connection.isValid(): |
|
2373 return |
|
2374 |
|
2375 self.__refactoringConnection = connection |
|
2376 connection.readyRead.connect(self.__receiveJson) |
|
2377 connection.disconnected.connect(self.__handleDisconnect) |
|
2378 |
|
2379 self.__sendJson("ping", {}) |
|
2380 |
|
2381 @pyqtSlot() |
|
2382 def __handleDisconnect(self): |
|
2383 """ |
|
2384 Private slot handling a disconnect of the refactoring client. |
|
2385 """ |
|
2386 if self.__refactoringConnection is not None: |
|
2387 self.__refactoringConnection.close() |
|
2388 |
|
2389 self.__refactoringConnection = None |
|
2390 |
|
2391 @pyqtSlot() |
|
2392 def __receiveJson(self): |
|
2393 """ |
|
2394 Private slot handling received data from the refactoring client. |
|
2395 """ |
|
2396 while self.__refactoringConnection and \ |
|
2397 self.__refactoringConnection.canReadLine(): |
|
2398 data = self.__refactoringConnection.readLine() |
|
2399 jsonLine = bytes(data).decode() |
|
2400 |
|
2401 print("Refactoring Server: ", jsonLine) ##debug |
|
2402 |
|
2403 self.__processJson(jsonLine) |
|
2404 continue |
|
2405 |
|
2406 def __processJson(self, jsonStr): |
|
2407 """ |
|
2408 Private method to process the JSON serialized client data. |
|
2409 |
|
2410 @param jsonStr string containing the data structure received |
|
2411 from the refactoring client |
|
2412 @type str |
2339 @type str |
2413 """ |
2340 @param params dictionary with method specific parameters |
2414 import json |
2341 @type dict |
2415 |
2342 """ |
2416 try: |
|
2417 clientDict = json.loads(jsonStr.strip()) |
|
2418 except (TypeError, ValueError) as err: |
|
2419 E5MessageBox.critical( |
|
2420 None, |
|
2421 self.tr("Refactoring Protocol Error"), |
|
2422 self.tr("""<p>The response received from the refactoring""" |
|
2423 """ client could not be decoded. Please report""" |
|
2424 """ this issue with the received data to the""" |
|
2425 """ eric bugs email address.</p>""" |
|
2426 """<p>Error: {0}</p>""" |
|
2427 """<p>Data:<br/>{0}</p>""").format( |
|
2428 str(err), Utilities.html_encode(jsonStr.strip())), |
|
2429 E5MessageBox.StandardButtons( |
|
2430 E5MessageBox.Ok)) |
|
2431 return |
|
2432 |
|
2433 method = clientDict["method"] |
|
2434 params = clientDict["params"] |
|
2435 |
|
2436 print("Method:", method) |
|
2437 print("Params:", params) |
|
2438 |
|
2439 if method == "pong": |
2343 if method == "pong": |
2440 pass |
2344 pass |
2441 |
2345 |
2442 elif method == "ClientException": |
2346 elif method == "ClientException": |
2443 if params["ExceptionType"] == "ProtocolError": |
2347 if params["ExceptionType"] == "ProtocolError": |
2473 E5MessageBox.Ok)) |
2377 E5MessageBox.Ok)) |
2474 |
2378 |
2475 elif method == "FileSystemCommand": |
2379 elif method == "FileSystemCommand": |
2476 self.__fsCommands.processFileSystemCommand(params) |
2380 self.__fsCommands.processFileSystemCommand(params) |
2477 |
2381 |
2478 def __sendJson(self, command, params): |
|
2479 """ |
|
2480 Private method to send a single refactoring command to the client. |
|
2481 |
|
2482 @param command command name to be sent |
|
2483 @type str |
|
2484 @param params dictionary of named parameters for the command |
|
2485 @type dict |
|
2486 """ |
|
2487 import json |
|
2488 |
|
2489 commandDict = { |
|
2490 "jsonrpc": "2.0", |
|
2491 "method": command, |
|
2492 "params": params, |
|
2493 } |
|
2494 cmd = json.dumps(commandDict) + '\n' |
|
2495 if self.__refactoringConnection is not None: |
|
2496 self.__refactoringConnection.write( |
|
2497 cmd.encode('utf8', 'backslashreplace')) |
|
2498 |
|
2499 def __startRefactoringClient(self, interpreter): |
2382 def __startRefactoringClient(self, interpreter): |
2500 """ |
2383 """ |
2501 Private method to start the refactoring client. |
2384 Private method to start the refactoring client. |
2502 |
2385 |
2503 @param interpreter interpreter to be used for the refactoring client |
2386 @param interpreter interpreter to be used for the refactoring client |
2504 @type str |
2387 @type str |
2505 @return reference to the refactoring client process |
2388 @return flag indicating a successful client start |
2506 """ |
2389 @rtype bool |
2507 if interpreter == "" or not Utilities.isinpath(interpreter): |
2390 """ |
2508 return None |
|
2509 |
|
2510 client = os.path.join(os.path.dirname(__file__), |
2391 client = os.path.join(os.path.dirname(__file__), |
2511 "RefactoringClient.py") |
2392 "RefactoringClient.py") |
2512 proc = QProcess() |
2393 ok = self.startClient(interpreter, client, [self.__projectpath]) |
2513 proc.setProcessChannelMode(QProcess.ForwardedChannels) |
2394 return ok |
2514 args = [client, self.__hostAddress, str(self.serverPort()), |
|
2515 self.__projectpath] |
|
2516 proc.start(interpreter, args) |
|
2517 if not proc.waitForStarted(10000): |
|
2518 proc = None |
|
2519 |
|
2520 return proc |
|
2521 |
|
2522 def __stopRefactoringClient(self): |
|
2523 """ |
|
2524 Private method to stop the refactoring client process. |
|
2525 """ |
|
2526 self.__refactoringProcess.close() |
|
2527 self.__refactoringProcess = None |
|
2528 |
|
2529 # |
|
2530 # eflag: noqa = M801 |
|