10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
12 import os |
12 import os |
13 import sys |
13 import sys |
14 |
14 |
15 from PyQt5.QtCore import QObject, QTranslator |
15 from PyQt5.QtCore import QObject, QTranslator, QCoreApplication, QTimer |
16 |
16 |
17 from E5Gui.E5Application import e5App |
17 from E5Gui.E5Application import e5App |
|
18 |
|
19 import Preferences |
|
20 import Utilities |
18 |
21 |
19 from Preferences.Shortcuts import readShortcuts |
22 from Preferences.Shortcuts import readShortcuts |
20 |
23 |
21 # Start-Of-Header |
24 # Start-Of-Header |
22 name = "Refactoring Rope Plugin" |
25 name = "Refactoring Rope Plugin" |
23 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
26 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
24 autoactivate = True |
27 autoactivate = True |
25 deactivateable = True |
28 deactivateable = True |
26 version = "3.1.0" |
29 version = "4.0.0" |
27 className = "RefactoringRopePlugin" |
30 className = "RefactoringRopePlugin" |
28 packageName = "RefactoringRope" |
31 packageName = "RefactoringRope" |
29 internalPackages = "rope" |
32 internalPackages = "rope" |
30 shortDescription = "Refactoring using the Rope library." |
33 shortDescription = "Refactoring using the Rope library." |
31 longDescription = """This plugin implements refactoring functionality""" \ |
34 longDescription = """This plug-in implements refactoring functionality""" \ |
32 """ using the Rope refactoring library. Only refactoring in the same""" \ |
35 """ using the Rope refactoring library. Additonally it implements an """ \ |
33 """ Python version as Eric is running is allowed.""" |
36 """ alternative auto-completion and calltips provider. Only""" \ |
|
37 """ refactoring, completions and calltips in the same Python variant""" \ |
|
38 """ as Eric is running is allowed.""" |
34 pyqtApi = 2 |
39 pyqtApi = 2 |
35 doNotCompile = True |
40 doNotCompile = True |
36 python2Compatible = True |
41 python2Compatible = True |
37 # End-Of-Header |
42 # End-Of-Header |
38 |
43 |
39 error = "" |
44 error = "" |
40 |
45 |
|
46 refactoringRopePluginObject = None |
|
47 |
|
48 |
|
49 def createAutoCompletionPage(configDlg): |
|
50 """ |
|
51 Module function to create the autocompletion configuration page. |
|
52 |
|
53 @param configDlg reference to the configuration dialog |
|
54 @return reference to the configuration page |
|
55 """ |
|
56 global refactoringRopePluginObject |
|
57 from RefactoringRope.ConfigurationPage.AutoCompletionRopePage \ |
|
58 import AutoCompletionRopePage |
|
59 page = AutoCompletionRopePage(refactoringRopePluginObject) |
|
60 return page |
|
61 |
|
62 |
|
63 def createCallTipsPage(configDlg): |
|
64 """ |
|
65 Module function to create the calltips configuration page. |
|
66 |
|
67 @param configDlg reference to the configuration dialog |
|
68 @return reference to the configuration page |
|
69 """ |
|
70 global refactoringRopePluginObject |
|
71 from RefactoringRope.ConfigurationPage.CallTipsRopePage \ |
|
72 import CallTipsRopePage |
|
73 page = CallTipsRopePage(refactoringRopePluginObject) |
|
74 return page |
|
75 |
|
76 |
|
77 def getConfigData(): |
|
78 """ |
|
79 Module function returning data as required by the configuration dialog. |
|
80 |
|
81 @return dictionary containing the relevant data |
|
82 """ |
|
83 return { |
|
84 "ropeAutoCompletionPage" : [ |
|
85 QCoreApplication.translate("RefactoringRopePlugin", "Rope"), |
|
86 os.path.join("RefactoringRope", "ConfigurationPage", |
|
87 "preferences-refactoring.png"), |
|
88 createAutoCompletionPage, "editorAutocompletionPage", None], |
|
89 "ropeCallTipsPage" : \ |
|
90 [QCoreApplication.translate("RefactoringRopePlugin", "Rope"), |
|
91 os.path.join("RefactoringRope", "ConfigurationPage", |
|
92 "preferences-refactoring.png"), |
|
93 createCallTipsPage, "editorCalltipsPage", None], |
|
94 } |
|
95 |
|
96 |
|
97 def prepareUninstall(): |
|
98 """ |
|
99 Module function to prepare for an uninstallation. |
|
100 """ |
|
101 Preferences.Prefs.settings.remove(RefactoringRopePlugin.PreferencesKey) |
|
102 |
41 |
103 |
42 class RefactoringRopePlugin(QObject): |
104 class RefactoringRopePlugin(QObject): |
43 """ |
105 """ |
44 Class implementing the Rope refactoring plugin. |
106 Class implementing the Rope refactoring plugin. |
45 """ |
107 """ |
|
108 PreferencesKey = "RefactoringRope" |
|
109 |
46 def __init__(self, ui): |
110 def __init__(self, ui): |
47 """ |
111 """ |
48 Constructor |
112 Constructor |
49 |
113 |
50 @param ui reference to the user interface object (UI.UserInterface) |
114 @param ui reference to the user interface object (UI.UserInterface) |
51 """ |
115 """ |
52 QObject.__init__(self, ui) |
116 QObject.__init__(self, ui) |
53 self.__ui = ui |
117 self.__ui = ui |
54 self.__initialize() |
118 self.__initialize() |
55 |
119 |
|
120 self.__defaults = { |
|
121 "CodeAssistEnabled" : False, |
|
122 "MaxFixes" : 10, |
|
123 "CodeAssistTimeout" : 100, |
|
124 "ShowQScintillaCompletions": True, |
|
125 |
|
126 "CodeAssistCalltipsEnabled" : False, |
|
127 "CalltipsMaxFixes" : 10, |
|
128 } |
|
129 |
56 self.__translator = None |
130 self.__translator = None |
57 self.__loadTranslator() |
131 self.__loadTranslator() |
|
132 |
|
133 self.__acTimer = QTimer(self) |
|
134 self.__acTimer.setSingleShot(True) |
|
135 self.__acTimer.setInterval(self.getPreferences("CodeAssistTimeout")) |
|
136 self.__acTimer.timeout.connect(self.__codeAssist) |
58 |
137 |
59 def __initialize(self): |
138 def __initialize(self): |
60 """ |
139 """ |
61 Private slot to (re)initialize the plugin. |
140 Private slot to (re)initialize the plugin. |
62 """ |
141 """ |
63 self.__object = None |
142 self.__object = None |
64 |
143 |
65 self.__mainAct = None |
144 self.__mainAct = None |
66 self.__mainMenu = None |
145 self.__mainMenu = None |
67 self.__projectIsOpen = False |
146 self.__projectIsOpen = False |
|
147 |
|
148 self.__editors = [] |
|
149 |
|
150 self.__currentEditor = None |
|
151 self.__savedEditorName = None |
|
152 self.__oldEditorText = "" |
68 |
153 |
69 def __checkUiVersion(self): |
154 def __checkUiVersion(self): |
70 """ |
155 """ |
71 Private method to check, if the IDE has a suitable version. |
156 Private method to check, if the IDE has a suitable version. |
72 |
157 |
180 else: |
285 else: |
181 print("Warning: translation file '{0}' could not" |
286 print("Warning: translation file '{0}' could not" |
182 " be loaded.".format(translation)) |
287 " be loaded.".format(translation)) |
183 print("Using default.") |
288 print("Using default.") |
184 |
289 |
185 def __projectOpened(self): |
290 def getPreferences(self, key): |
186 """ |
291 """ |
187 Private slot to handle the projectOpened signal. |
292 Public method to retrieve the various refactoring settings. |
|
293 |
|
294 @param key the key of the value to get |
|
295 @param prefClass preferences class used as the storage area |
|
296 @return the requested refactoring setting |
|
297 """ |
|
298 if key in ["CodeAssistEnabled", "CodeAssistCalltipsEnabled", |
|
299 "ShowQScintillaCompletions"]: |
|
300 return Preferences.toBool(Preferences.Prefs.settings.value( |
|
301 self.PreferencesKey + "/" + key, self.__defaults[key])) |
|
302 else: |
|
303 return int(Preferences.Prefs.settings.value( |
|
304 self.PreferencesKey + "/" + key, self.__defaults[key])) |
|
305 |
|
306 def setPreferences(self, key, value): |
|
307 """ |
|
308 Public method to store the various refactoring settings. |
|
309 |
|
310 @param key the key of the setting to be set (string) |
|
311 @param value the value to be set |
|
312 @param prefClass preferences class used as the storage area |
|
313 """ |
|
314 Preferences.Prefs.settings.setValue( |
|
315 self.PreferencesKey + "/" + key, value) |
|
316 |
|
317 if key in ["CodeAssistEnabled", "CodeAssistCalltipsEnabled"]: |
|
318 if value: |
|
319 if e5App().getObject("Project").isOpen(): |
|
320 for editor in e5App().getObject("ViewManager")\ |
|
321 .getOpenEditors(): |
|
322 if editor not in self.__editors: |
|
323 self.__editorOpened(editor) |
|
324 else: |
|
325 for editor in self.__editors[:]: |
|
326 self.__editorClosed(editor) |
|
327 elif key == "CodeAssistTimeout": |
|
328 self.__acTimer.setInterval(value) |
|
329 |
|
330 def __determineLanguage(self): |
|
331 """ |
|
332 Private method to determine the valid language strings. |
|
333 |
|
334 @return list of valid language strings (list of string) |
188 """ |
335 """ |
189 if sys.version_info[0] == 3: |
336 if sys.version_info[0] == 3: |
190 lang = ["Python3"] |
337 lang = ["Python3"] |
191 elif sys.version_info[0] == 2: |
338 elif sys.version_info[0] == 2: |
192 lang = ["Python", "Python2"] |
339 lang = ["Python", "Python2"] |
193 else: |
340 else: |
194 lang = [] |
341 lang = [] |
195 |
342 |
|
343 return lang |
|
344 |
|
345 def __projectOpened(self): |
|
346 """ |
|
347 Private slot to handle the projectOpened signal. |
|
348 """ |
|
349 lang = self.__determineLanguage() |
|
350 |
196 enabled = e5App().getObject("Project").getProjectLanguage() in lang |
351 enabled = e5App().getObject("Project").getProjectLanguage() in lang |
197 self.__mainAct.setEnabled(enabled) |
352 self.__mainAct.setEnabled(enabled) |
198 self.__projectIsOpen = enabled |
353 self.__projectIsOpen = enabled |
|
354 |
|
355 for editor in self.__editors: |
|
356 if editor.getLanguage() in lang and \ |
|
357 self.__projectIsOpen: |
|
358 self.__connectEditorSignals(editor) |
|
359 if self.getPreferences("CodeAssistEnabled"): |
|
360 self.__setAutoCompletionHook(editor) |
|
361 if self.getPreferences("CodeAssistCalltipsEnabled"): |
|
362 self.__setCalltipsHook(editor) |
199 |
363 |
200 def __projectClosed(self): |
364 def __projectClosed(self): |
201 """ |
365 """ |
202 Private slot to handle the projectClosed signal. |
366 Private slot to handle the projectClosed signal. |
203 """ |
367 """ |
204 self.__mainAct.setEnabled(False) |
368 self.__mainAct.setEnabled(False) |
205 self.__projectIsOpen = False |
369 self.__projectIsOpen = False |
|
370 |
|
371 for editor in self.__editors: |
|
372 self.__disconnectEditorSignals(editor) |
|
373 if editor.autoCompletionHook() == self.codeAssist: |
|
374 self.__unsetAutoCompletionHook(editor) |
|
375 if editor.callTipHook() == self.codeAssistCallTip: |
|
376 self.__unsetCalltipsHook(editor) |
|
377 |
|
378 def __editorOpened(self, editor): |
|
379 """ |
|
380 Private slot called, when a new editor was opened. |
|
381 |
|
382 @param editor reference to the new editor (QScintilla.Editor) |
|
383 """ |
|
384 lang = self.__determineLanguage() |
|
385 |
|
386 if editor.getLanguage() in lang and self.__projectIsOpen: |
|
387 self.__connectEditorSignals(editor) |
|
388 if self.getPreferences("CodeAssistEnabled"): |
|
389 self.__setAutoCompletionHook(editor) |
|
390 if self.getPreferences("CodeAssistCalltipsEnabled"): |
|
391 self.__setCalltipsHook(editor) |
|
392 |
|
393 editor.languageChanged.connect(self.__editorLanguageChanged) |
|
394 self.__editors.append(editor) |
|
395 |
|
396 def __editorClosed(self, editor): |
|
397 """ |
|
398 Private slot called, when an editor was closed. |
|
399 |
|
400 @param editor reference to the editor (QScintilla.Editor) |
|
401 """ |
|
402 if editor in self.__editors: |
|
403 editor.languageChanged.disconnect(self.__editorLanguageChanged) |
|
404 self.__disconnectEditorSignals(editor) |
|
405 self.__editors.remove(editor) |
|
406 if editor.autoCompletionHook() == self.codeAssist: |
|
407 self.__unsetAutoCompletionHook(editor) |
|
408 if editor.callTipHook() == self.codeAssistCallTip: |
|
409 self.__unsetCalltipsHook(editor) |
|
410 |
|
411 def __editorLanguageChanged(self, language): |
|
412 """ |
|
413 Private slot to handle the language change of an editor. |
|
414 |
|
415 @param language programming language of the editor (string) |
|
416 """ |
|
417 editor = self.sender() |
|
418 lang = self.__determineLanguage() |
|
419 |
|
420 if language in lang and self.__projectIsOpen: |
|
421 self.__connectEditorSignals(editor) |
|
422 if self.getPreferences("CodeAssistEnabled"): |
|
423 self.__setAutoCompletionHook(editor) |
|
424 if self.getPreferences("CodeAssistCalltipsEnabled"): |
|
425 self.__setCalltipsHook(editor) |
|
426 else: |
|
427 self.__disconnectEditorSignals(editor) |
|
428 if editor.autoCompletionHook() == self.codeAssist: |
|
429 self.__unsetAutoCompletionHook(editor) |
|
430 if editor.callTipHook() == self.codeAssistCallTip: |
|
431 self.__unsetCalltipsHook(editor) |
|
432 |
|
433 def __connectEditorSignals(self, editor): |
|
434 """ |
|
435 Private method to connect to some signals of an editor. |
|
436 |
|
437 @param editor reference to the editor (QScintilla.Editor) |
|
438 """ |
|
439 editor.editorAboutToBeSaved.connect(self.__editorAboutToBeSaved) |
|
440 editor.editorSaved.connect(self.__editorSaved) |
|
441 |
|
442 def __disconnectEditorSignals(self, editor): |
|
443 """ |
|
444 Private method to disconnect to some signals of an editor. |
|
445 |
|
446 @param editor reference to the editor (QScintilla.Editor) |
|
447 """ |
|
448 editor.editorAboutToBeSaved.disconnect(self.__editorAboutToBeSaved) |
|
449 editor.editorSaved.disconnect(self.__editorSaved) |
|
450 |
|
451 def __completionListSelected(self, id, txt): |
|
452 """ |
|
453 Private slot to handle the selection from the completion list. |
|
454 |
|
455 @param id the ID of the user list (should be 1) (integer) |
|
456 @param txt the selected text (QString) |
|
457 """ |
|
458 from QScintilla.Editor import EditorAutoCompletionListID |
|
459 |
|
460 editor = self.sender() |
|
461 if id == EditorAutoCompletionListID: |
|
462 lst = txt.split() |
|
463 if len(lst) > 1: |
|
464 txt = lst[0] |
|
465 |
|
466 if Preferences.getEditor("AutoCompletionReplaceWord"): |
|
467 editor.selectCurrentWord() |
|
468 editor.removeSelectedText() |
|
469 line, col = editor.getCursorPosition() |
|
470 else: |
|
471 line, col = editor.getCursorPosition() |
|
472 wLeft = editor.getWordLeft(line, col) |
|
473 if not txt.startswith(wLeft): |
|
474 editor.selectCurrentWord() |
|
475 editor.removeSelectedText() |
|
476 line, col = editor.getCursorPosition() |
|
477 elif wLeft: |
|
478 txt = txt[len(wLeft):] |
|
479 editor.insert(txt) |
|
480 editor.setCursorPosition(line, col + len(txt)) |
|
481 |
|
482 def __setAutoCompletionHook(self, editor): |
|
483 """ |
|
484 Private method to set the autocompletion hook. |
|
485 |
|
486 @param editor reference to the editor (QScintilla.Editor) |
|
487 """ |
|
488 editor.userListActivated.connect(self.__completionListSelected) |
|
489 editor.setAutoCompletionHook(self.codeAssist) |
|
490 |
|
491 def __unsetAutoCompletionHook(self, editor): |
|
492 """ |
|
493 Private method to unset the autocompletion hook. |
|
494 |
|
495 @param editor reference to the editor (QScintilla.Editor) |
|
496 """ |
|
497 editor.unsetAutoCompletionHook() |
|
498 editor.userListActivated.disconnect(self.__completionListSelected) |
|
499 |
|
500 def codeAssist(self, editor, context = False): |
|
501 """ |
|
502 Public method to determine the autocompletion proposals. |
|
503 |
|
504 @param editor reference to the editor object, that called this method |
|
505 QScintilla.Editor) |
|
506 @param context flag indicating to autocomplete a context (boolean) |
|
507 """ |
|
508 self.__currentEditor = editor |
|
509 if self.getPreferences("CodeAssistTimeout"): |
|
510 self.__acTimer.stop() |
|
511 self.__acTimer.start() |
|
512 else: |
|
513 self.__codeAssist() |
|
514 |
|
515 def __codeAssist(self): |
|
516 """ |
|
517 Private slot to show a list with completion proposals. |
|
518 """ |
|
519 from RefactoringRope.CodeAssist import CodeAssist |
|
520 from QScintilla.Editor import EditorAutoCompletionListID |
|
521 |
|
522 if self.__currentEditor is not None: |
|
523 if self.__currentEditor.isListActive(): |
|
524 self.__currentEditor.cancelList() |
|
525 ca = CodeAssist(self.__object, self.__currentEditor, self, self) |
|
526 completions = ca.getCompletions() |
|
527 if len(completions) == 0 and \ |
|
528 self.getPreferences("ShowQScintillaCompletions"): |
|
529 # try QScintilla autocompletion |
|
530 self.__currentEditor.autoCompleteQScintilla() |
|
531 else: |
|
532 completions.sort() |
|
533 self.__currentEditor.showUserList(EditorAutoCompletionListID, |
|
534 completions) |
|
535 |
|
536 def __editorAboutToBeSaved(self, filename): |
|
537 """ |
|
538 Private slot to get the old contents of the named file. |
|
539 |
|
540 @param filename name of the file about to be saved (string) |
|
541 """ |
|
542 if filename and os.path.exists(filename): |
|
543 try: |
|
544 self.__oldEditorText = Utilities.readEncodedFile(filename)[0] |
|
545 except IOError: |
|
546 self.__oldEditorText = "" |
|
547 self.__savedEditorName = filename |
|
548 else: |
|
549 self.__savedEditorName = "" |
|
550 self.__oldEditorText = "" |
|
551 |
|
552 def __editorSaved(self, filename): |
|
553 """ |
|
554 Private slot to activate SOA. |
|
555 |
|
556 @param filename name of the file that was saved (string) |
|
557 """ |
|
558 import rope.base.libutils |
|
559 |
|
560 if filename == self.__savedEditorName and self.__oldEditorText: |
|
561 rope.base.libutils.report_change(self.__object.getProject(), |
|
562 self.__savedEditorName, self.__oldEditorText) |
|
563 elif self.__savedEditorName == "": |
|
564 rope.base.libutils.report_change(self.__object.getProject(), |
|
565 filename, "") |
|
566 |
|
567 def __setCalltipsHook(self, editor): |
|
568 """ |
|
569 Private method to set the calltip hook. |
|
570 |
|
571 @param editor reference to the editor (QScintilla.Editor) |
|
572 """ |
|
573 editor.setCallTipHook(self.codeAssistCallTip) |
|
574 |
|
575 def __unsetCalltipsHook(self, editor): |
|
576 """ |
|
577 Private method to unset the calltip hook. |
|
578 |
|
579 @param editor reference to the editor (QScintilla.Editor) |
|
580 """ |
|
581 editor.unsetCallTipHook() |
|
582 |
|
583 def codeAssistCallTip(self, editor, pos, commas): |
|
584 """ |
|
585 Public method to return a list of calltips. |
|
586 |
|
587 @param editor reference to the editor (QScintilla.Editor) |
|
588 @param pos position in the text for the calltip (integer) |
|
589 @param commas minimum number of commas contained in the calltip |
|
590 (integer) |
|
591 @return list of possible calltips (list of strings) |
|
592 """ |
|
593 from RefactoringRope.CodeAssist import CodeAssist |
|
594 ca = CodeAssist(self.__object, editor, self, self) |
|
595 cts = ca.getCallTips(pos) |
|
596 return cts |