Sat, 07 Mar 2015 19:52:36 +0100
Made the code completion work in case no project is open.
# -*- coding: utf-8 -*- # Copyright (c) 2010 - 2015 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the Rope refactoring plugin. """ from __future__ import unicode_literals import os import sys from PyQt5.QtCore import QObject, QTranslator, QCoreApplication, QTimer from E5Gui.E5Application import e5App import Preferences import Utilities from Preferences.Shortcuts import readShortcuts # Start-Of-Header name = "Refactoring Rope Plugin" author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True version = "4.0.1" className = "RefactoringRopePlugin" packageName = "RefactoringRope" internalPackages = "rope" shortDescription = "Refactoring using the Rope library." longDescription = """This plug-in implements refactoring functionality""" \ """ using the Rope refactoring library. Additonally it implements an """ \ """ alternative auto-completion and calltips provider. Only""" \ """ refactoring, completions and calltips in the same Python variant""" \ """ as Eric is running is allowed.""" pyqtApi = 2 doNotCompile = True python2Compatible = True # End-Of-Header error = "" refactoringRopePluginObject = None def createAutoCompletionPage(configDlg): """ Module function to create the autocompletion configuration page. @param configDlg reference to the configuration dialog @return reference to the configuration page """ global refactoringRopePluginObject from RefactoringRope.ConfigurationPage.AutoCompletionRopePage \ import AutoCompletionRopePage page = AutoCompletionRopePage(refactoringRopePluginObject) return page def createCallTipsPage(configDlg): """ Module function to create the calltips configuration page. @param configDlg reference to the configuration dialog @return reference to the configuration page """ global refactoringRopePluginObject from RefactoringRope.ConfigurationPage.CallTipsRopePage \ import CallTipsRopePage page = CallTipsRopePage(refactoringRopePluginObject) return page def getConfigData(): """ Module function returning data as required by the configuration dialog. @return dictionary containing the relevant data """ return { "ropeAutoCompletionPage" : [ QCoreApplication.translate("RefactoringRopePlugin", "Rope"), os.path.join("RefactoringRope", "ConfigurationPage", "preferences-refactoring.png"), createAutoCompletionPage, "editorAutocompletionPage", None], "ropeCallTipsPage" : \ [QCoreApplication.translate("RefactoringRopePlugin", "Rope"), os.path.join("RefactoringRope", "ConfigurationPage", "preferences-refactoring.png"), createCallTipsPage, "editorCalltipsPage", None], } def prepareUninstall(): """ Module function to prepare for an uninstallation. """ Preferences.Prefs.settings.remove(RefactoringRopePlugin.PreferencesKey) class RefactoringRopePlugin(QObject): """ Class implementing the Rope refactoring plugin. """ PreferencesKey = "RefactoringRope" def __init__(self, ui): """ Constructor @param ui reference to the user interface object (UI.UserInterface) """ QObject.__init__(self, ui) self.__ui = ui self.__initialize() self.__defaults = { "CodeAssistEnabled" : False, "MaxFixes" : 10, "CodeAssistTimeout" : 100, "ShowQScintillaCompletions": True, "CodeAssistCalltipsEnabled" : False, "CalltipsMaxFixes" : 10, } self.__translator = None self.__loadTranslator() self.__acTimer = QTimer(self) self.__acTimer.setSingleShot(True) self.__acTimer.setInterval(self.getPreferences("CodeAssistTimeout")) self.__acTimer.timeout.connect(self.__codeAssist) def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__object = None self.__caObject = None self.__mainAct = None self.__mainMenu = None self.__projectIsOpen = False self.__editors = [] self.__currentEditor = None self.__savedEditorName = None self.__oldEditorText = "" def __checkUiVersion(self): """ Private method to check, if the IDE has a suitable version. @return flag indicating a suitable version (boolean) """ global error suitable = self.__ui.versionIsNewer("5.99.99", "20140701") if not suitable: error = self.tr("Your version of eric6 is not supported.") return suitable def activate(self): """ Public method to activate this plugin. @return tuple of None and activation status (boolean) """ global error error = "" # clear previous error if not self.__checkUiVersion(): return None, False global refactoringRopePluginObject refactoringRopePluginObject = self from RefactoringRope.Refactoring import Refactoring from RefactoringRope.CodeAssist import CodeAssist self.__caObject = CodeAssist(self, self) self.__object = Refactoring(self, self.__ui) self.__object.initActions() e5App().registerPluginObject("RefactoringRope", self.__object) try: readShortcuts(pluginName="RefactoringRope") except TypeError: # backwards comaytibility, ignore pass self.__mainMenu = self.__object.initMenu() extrasAct = self.__ui.getMenuBarAction("extras") self.__mainAct = self.__ui.menuBar().insertMenu( extrasAct, self.__mainMenu) if e5App().getObject("Project").isOpen(): self.__projectOpened() if self.__projectIsOpen: self.__object.projectOpened() e5App().getObject("ViewManager").editorOpenedEd.connect( self.__editorOpened) e5App().getObject("ViewManager").editorClosedEd.connect( self.__editorClosed) e5App().getObject("Project").projectOpened.connect( self.__object.projectOpened) e5App().getObject("Project").projectPropertiesChanged.connect( self.__object.projectOpened) e5App().getObject("Project").projectClosed.connect( self.__object.projectClosed) e5App().getObject("Project").newProject.connect( self.__object.projectOpened) e5App().getObject("Project").projectOpened.connect( self.__projectOpened) e5App().getObject("Project").projectPropertiesChanged.connect( self.__projectOpened) e5App().getObject("Project").projectClosed.connect( self.__projectClosed) e5App().getObject("Project").newProject.connect( self.__projectOpened) if e5App().getObject("Project").isOpen(): for editor in e5App().getObject("ViewManager").getOpenEditors(): self.__editorOpened(editor) return None, True def deactivate(self): """ Public method to deactivate this plugin. """ e5App().unregisterPluginObject("RefactoringRope") e5App().getObject("ViewManager").editorOpenedEd.disconnect( self.__editorOpened) e5App().getObject("ViewManager").editorClosedEd.disconnect( self.__editorClosed) e5App().getObject("Project").projectOpened.disconnect( self.__object.projectOpened) e5App().getObject("Project").projectPropertiesChanged.disconnect( self.__object.projectOpened) e5App().getObject("Project").projectClosed.disconnect( self.__object.projectClosed) e5App().getObject("Project").newProject.disconnect( self.__object.projectOpened) e5App().getObject("Project").projectOpened.disconnect( self.__projectOpened) e5App().getObject("Project").projectPropertiesChanged.disconnect( self.__projectOpened) e5App().getObject("Project").projectClosed.disconnect( self.__projectClosed) e5App().getObject("Project").newProject.disconnect( self.__projectOpened) self.__ui.menuBar().removeAction(self.__mainAct) for editor in self.__editors[:]: self.__editorClosed(editor) self.__initialize() def __loadTranslator(self): """ Private method to load the translation file. """ if self.__ui is not None: loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = \ os.path.join(os.path.dirname(__file__), "RefactoringRope", "i18n") translation = "rope_%s" % loc translator = QTranslator(None) loaded = translator.load(translation, locale_dir) if loaded: self.__translator = translator e5App().installTranslator(self.__translator) else: print("Warning: translation file '{0}' could not" " be loaded.".format(translation)) print("Using default.") def getPreferences(self, key): """ Public method to retrieve the various refactoring settings. @param key the key of the value to get @param prefClass preferences class used as the storage area @return the requested refactoring setting """ if key in ["CodeAssistEnabled", "CodeAssistCalltipsEnabled", "ShowQScintillaCompletions"]: return Preferences.toBool(Preferences.Prefs.settings.value( self.PreferencesKey + "/" + key, self.__defaults[key])) else: return int(Preferences.Prefs.settings.value( self.PreferencesKey + "/" + key, self.__defaults[key])) def setPreferences(self, key, value): """ Public method to store the various refactoring settings. @param key the key of the setting to be set (string) @param value the value to be set @param prefClass preferences class used as the storage area """ Preferences.Prefs.settings.setValue( self.PreferencesKey + "/" + key, value) if key in ["CodeAssistEnabled", "CodeAssistCalltipsEnabled"]: if value: if e5App().getObject("Project").isOpen(): for editor in e5App().getObject("ViewManager")\ .getOpenEditors(): if editor not in self.__editors: self.__editorOpened(editor) else: for editor in self.__editors[:]: self.__editorClosed(editor) elif key == "CodeAssistTimeout": self.__acTimer.setInterval(value) def __determineLanguage(self): """ Private method to determine the valid language strings. @return list of valid language strings (list of string) """ if sys.version_info[0] == 3: lang = ["Python3"] elif sys.version_info[0] == 2: lang = ["Python", "Python2"] else: lang = [] return lang def __projectOpened(self): """ Private slot to handle the projectOpened signal. """ lang = self.__determineLanguage() enabled = e5App().getObject("Project").getProjectLanguage() in lang self.__mainAct.setEnabled(enabled) self.__projectIsOpen = enabled def __projectClosed(self): """ Private slot to handle the projectClosed signal. """ self.__mainAct.setEnabled(False) self.__projectIsOpen = False def __editorOpened(self, editor): """ Private slot called, when a new editor was opened. @param editor reference to the new editor (QScintilla.Editor) """ lang = self.__determineLanguage() if editor.getLanguage() in lang: self.__connectEditor(editor) editor.languageChanged.connect(self.__editorLanguageChanged) self.__editors.append(editor) def __editorClosed(self, editor): """ Private slot called, when an editor was closed. @param editor reference to the editor (QScintilla.Editor) """ if editor in self.__editors: editor.languageChanged.disconnect(self.__editorLanguageChanged) self.__disconnectEditor(editor) self.__editors.remove(editor) def __editorLanguageChanged(self, language): """ Private slot to handle the language change of an editor. @param language programming language of the editor (string) """ editor = self.sender() lang = self.__determineLanguage() if language in lang: self.__connectEditor(editor) else: self.__disconnectEditor(editor) def __connectEditor(self, editor): """ Private method to connect an editor. @param editor reference to the editor (QScintilla.Editor) """ editor.editorAboutToBeSaved.connect(self.__editorAboutToBeSaved) editor.editorSaved.connect(self.__editorSaved) if self.getPreferences("CodeAssistEnabled"): self.__setAutoCompletionHook(editor) if self.getPreferences("CodeAssistCalltipsEnabled"): self.__setCalltipsHook(editor) def __disconnectEditor(self, editor): """ Private method to disconnect an editor. @param editor reference to the editor (QScintilla.Editor) """ try: editor.editorAboutToBeSaved.disconnect(self.__editorAboutToBeSaved) editor.editorSaved.disconnect(self.__editorSaved) except TypeError: # just ignore it pass if editor.autoCompletionHook() == self.codeAssist: self.__unsetAutoCompletionHook(editor) if editor.callTipHook() == self.codeAssistCallTip: self.__unsetCalltipsHook(editor) def __completionListSelected(self, id, txt): """ Private slot to handle the selection from the completion list. @param id the ID of the user list (should be 1) (integer) @param txt the selected text (QString) """ from QScintilla.Editor import EditorAutoCompletionListID editor = self.sender() if id == EditorAutoCompletionListID: lst = txt.split() if len(lst) > 1: txt = lst[0] if Preferences.getEditor("AutoCompletionReplaceWord"): editor.selectCurrentWord() editor.removeSelectedText() line, col = editor.getCursorPosition() else: line, col = editor.getCursorPosition() wLeft = editor.getWordLeft(line, col) if not txt.startswith(wLeft): editor.selectCurrentWord() editor.removeSelectedText() line, col = editor.getCursorPosition() elif wLeft: txt = txt[len(wLeft):] editor.insert(txt) editor.setCursorPosition(line, col + len(txt)) def __setAutoCompletionHook(self, editor): """ Private method to set the autocompletion hook. @param editor reference to the editor (QScintilla.Editor) """ editor.userListActivated.connect(self.__completionListSelected) editor.setAutoCompletionHook(self.codeAssist) def __unsetAutoCompletionHook(self, editor): """ Private method to unset the autocompletion hook. @param editor reference to the editor (QScintilla.Editor) """ editor.unsetAutoCompletionHook() editor.userListActivated.disconnect(self.__completionListSelected) def codeAssist(self, editor, context = False): """ Public method to determine the autocompletion proposals. @param editor reference to the editor object, that called this method QScintilla.Editor) @param context flag indicating to autocomplete a context (boolean) """ self.__currentEditor = editor if self.getPreferences("CodeAssistTimeout"): self.__acTimer.stop() self.__acTimer.start() else: self.__codeAssist() def __codeAssist(self): """ Private slot to show a list with completion proposals. """ from QScintilla.Editor import EditorAutoCompletionListID if self.__currentEditor is not None: if self.__currentEditor.isListActive(): self.__currentEditor.cancelList() completions = self.__caObject.getCompletions(self.__currentEditor) if len(completions) == 0 and \ self.getPreferences("ShowQScintillaCompletions"): # try QScintilla autocompletion self.__currentEditor.autoCompleteQScintilla() else: completions.sort() self.__currentEditor.showUserList(EditorAutoCompletionListID, completions) def __editorAboutToBeSaved(self, filename): """ Private slot to get the old contents of the named file. @param filename name of the file about to be saved (string) """ if filename and os.path.exists(filename): try: self.__oldEditorText = Utilities.readEncodedFile(filename)[0] except IOError: self.__oldEditorText = "" self.__savedEditorName = filename else: self.__savedEditorName = "" self.__oldEditorText = "" def __editorSaved(self, filename): """ Private slot to activate SOA. @param filename name of the file that was saved (string) """ # TODO: report change to the refactoring object as well if filename == self.__savedEditorName and self.__oldEditorText: self.__caObject.reportChanged(self.__savedEditorName, self.__oldEditorText) else: self.__caObject.reportChanged(filename, "") def __setCalltipsHook(self, editor): """ Private method to set the calltip hook. @param editor reference to the editor (QScintilla.Editor) """ editor.setCallTipHook(self.codeAssistCallTip) def __unsetCalltipsHook(self, editor): """ Private method to unset the calltip hook. @param editor reference to the editor (QScintilla.Editor) """ editor.unsetCallTipHook() def codeAssistCallTip(self, editor, pos, commas): """ Public method to return a list of calltips. @param editor reference to the editor (QScintilla.Editor) @param pos position in the text for the calltip (integer) @param commas minimum number of commas contained in the calltip (integer) @return list of possible calltips (list of strings) """ cts = self.__caObject.getCallTips(pos, editor) return cts