diff -r 9f687137a929 -r fc72a5b922a6 RefactoringRope/Refactoring.py --- a/RefactoringRope/Refactoring.py Sun Jan 23 19:55:56 2011 +0100 +++ b/RefactoringRope/Refactoring.py Sat Jan 29 13:38:30 2011 +0100 @@ -16,8 +16,10 @@ import rope.base.project import rope.base.exceptions +import rope.contrib.findit + from PyQt4.QtCore import QObject, SIGNAL -from PyQt4.QtGui import QMenu +from PyQt4.QtGui import QMenu, QApplication, QMessageBox from E5Gui.E5Application import e5App @@ -28,13 +30,17 @@ from FileSystemCommands import e5FileSystemCommands from HelpDialog import HelpDialog +from ProgressHandle import ProgressHandle +from MatchesDialog import MatchesDialog + +import Utilities class Refactoring(QObject): """ Class implementing the refactoring interface to rope. """ - def __init__(self, plugin, newStyle, parent = None): + def __init__(self, plugin, newStyle, parent=None): """ Constructor @@ -65,6 +71,66 @@ """ self.actions = [] + ##################################################### + ## Query actions + ##################################################### + + self.queryReferencesAct = E5Action(self.trUtf8('Find occurrences'), + self.trUtf8('Find &Occurrences'), + 0, 0, + self,'refactoring_find_occurrences') + self.queryReferencesAct.setStatusTip(self.trUtf8( + 'Find occurrences of the highlighted object')) + self.queryReferencesAct.setWhatsThis(self.trUtf8( + """<b>Find occurrences</b>""" + """<p>Find occurrences of the highlighted class, method,""" + """ function or variable.</p>""" + )) + if self.__newStyle: + self.queryReferencesAct.triggered[()].connect( + self.__queryReferences) + else: + self.connect(self.queryReferencesAct, SIGNAL('triggered()'), + self.__queryReferences) + self.actions.append(self.queryReferencesAct) + + self.queryDefinitionAct = E5Action(self.trUtf8('Find definition'), + self.trUtf8('Find &Definition'), + 0, 0, + self,'refactoring_find_definition') + self.queryDefinitionAct.setStatusTip(self.trUtf8( + 'Find definition of the highlighted item')) + self.queryDefinitionAct.setWhatsThis(self.trUtf8( + """<b>Find definition</b>""" + """<p>Find the definition of the highlighted class, method,""" + """ function or variable.</p>""" + )) + if self.__newStyle: + self.queryDefinitionAct.triggered[()].connect( + self.__queryDefinition) + else: + self.connect(self.queryDefinitionAct, SIGNAL('triggered()'), + self.__queryDefinition) + self.actions.append(self.queryDefinitionAct) + + self.queryImplementationsAct = E5Action( + self.trUtf8('Find implementations'), + self.trUtf8('Find &Implementations'), + 0, 0, + self,'refactoring_find_implementations') + self.queryImplementationsAct.setStatusTip(self.trUtf8( + 'Find places where the selected method is overridden')) + self.queryImplementationsAct.setWhatsThis(self.trUtf8( + """<b>Find implementations</b>""" + """<p>Find places where the selected method is overridden.</p>""" + )) + if self.__newStyle: + self.queryImplementationsAct.triggered[()].connect( + self.__queryImplementations) + else: + self.connect(self.queryImplementationsAct, SIGNAL('triggered()'), + self.__queryImplementations) + self.actions.append(self.queryImplementationsAct) ##################################################### ## Various actions @@ -124,6 +190,11 @@ act.setFont(font) menu.addSeparator() + smenu = menu.addMenu(self.trUtf8("&Query")) + smenu.addAction(self.queryReferencesAct) + smenu.addAction(self.queryDefinitionAct) + smenu.addAction(self.queryImplementationsAct) + menu.addSeparator() menu.addAction(self.refactoringEditConfigAct) menu.addAction(self.refactoringHelpAct) @@ -144,6 +215,171 @@ self.trUtf8("{0}\nVersion {1}\n\n{2}".format( rope.INFO, rope.VERSION, rope.COPYRIGHT))) + def handleRopeError(self, err, title, handle=None): + """ + Public slot to handle a rope error. + + @param err rope exception object (Exception) + @param title title to be displayed (string) + @param handle reference to a taskhandle (ProgressHandle) + """ + if handle is not None: + handle.reset() + if str(type(err)).split()[-1][1:-2].split('.')[-1] == \ + 'ModuleSyntaxError': + res = E5MessageBox.warning(self.__ui, title, + self.trUtf8("Rope error: {0}").format(str(err)), + QMessageBox.Ok | QMessageBox.Open) + if res == QMessageBox.Open: + e5App().getObject("ViewManager").openSourceFile( + os.path.join(self.__e4project.getProjectPath(), + err.filename), + err.lineno) + else: + E5MessageBox.warning(self.__ui, title, + self.trUtf8("Rope error: {0}").format(str(err))) + + ##################################################### + ## Find actions + ##################################################### + + def __queryReferences(self): + """ + Private slot to handle the Find References action. + """ + aw = e5App().getObject("ViewManager").activeWindow() + + if aw is None: + return + + title = self.trUtf8("Find Occurrences") + if not aw.hasSelectedText(): + # no selection available + E5MessageBox.warning(self.__ui, title, + self.trUtf8("Highlight the class, method, function or variable" + " to search for and try again.")) + return + + if not self.confirmAllBuffersSaved(): + return + + filename = aw.getFileName() + line, index, line1, index1 = aw.getSelection() + offset = aw.positionFromLineIndex(line, index) + + resource = rope.base.libutils.path_to_resource( + self.__project, filename) + handle = ProgressHandle(title, True, self.__ui) + handle.show() + QApplication.processEvents() + try: + occurrences = rope.contrib.findit.find_occurrences( + self.__project, resource, offset, + unsure = True, in_hierarchy = True, task_handle = handle) + except Exception as err: + self.handleRopeError(err, title, handle) + return + handle.reset() + + if occurrences: + self.dlg = MatchesDialog(self.__ui, True) + self.dlg.show() + for occurrence in occurrences: + self.dlg.addEntry(occurrence.resource, + occurrence.lineno, occurrence.unsure) + else: + E5MessageBox.warning(self.__ui, title, + self.trUtf8("No occurrences found.")) + + def __queryDefinition(self): + """ + Private slot to handle the Find Definition action + """ + aw = e5App().getObject("ViewManager").activeWindow() + + if aw is None: + return + + title = self.trUtf8("Find &Definition") + if not aw.hasSelectedText(): + # no selection available + E5MessageBox.warning(self.__ui, title, + self.trUtf8("Highlight the class, method, function or" + " variable reference to search definition for and" + " try again.")) + return + + if not self.confirmAllBuffersSaved(): + return + + filename = aw.getFileName() + line, index, line1, index1 = aw.getSelection() + offset = aw.positionFromLineIndex(line, index) + + resource = rope.base.libutils.path_to_resource( + self.__project, filename) + try: + location = rope.contrib.findit.find_definition( + self.__project, aw.text(), offset, resource) + except Exception as err: + self.handleRopeError(err, title) + return + + if location is not None: + self.dlg = MatchesDialog(self.__ui, False) + self.dlg.show() + self.dlg.addEntry(location.resource, location.lineno) + else: + E5MessageBox.warning(self.__ui, title, + self.trUtf8("No matching definition found.")) + + def __queryImplementations(self): + """ + Private slot to handle the Find Implementations action. + """ + aw = e5App().getObject("ViewManager").activeWindow() + + if aw is None: + return + + title = self.trUtf8("Find Implementations") + if not aw.hasSelectedText(): + # no selection available + E5MessageBox.warning(self.__ui, title, + self.trUtf8("Highlight the method to search for" + " and try again.")) + return + + if not self.confirmAllBuffersSaved(): + return + + filename = aw.getFileName() + line, index, line1, index1 = aw.getSelection() + offset = aw.positionFromLineIndex(line, index) + + resource = rope.base.libutils.path_to_resource(self.__project, + filename) + handle = ProgressHandle(title, True, self.__ui) + handle.show() + QApplication.processEvents() + try: + occurrences = rope.contrib.findit.find_implementations( + self.__project, resource, offset, task_handle = handle) + except Exception as err: + self.handleRopeError(err, title, handle) + return + handle.reset() + + if occurrences: + self.dlg = MatchesDialog(self.__ui, True) + self.dlg.show() + for occurrence in occurrences: + self.dlg.addEntry(occurrence.resource, + occurrence.lineno, occurrence.unsure) + else: + E5MessageBox.warning(self.__ui, title, + self.trUtf8("No occurrences found.")) + ##################################################### ## Various actions ##################################################### @@ -276,48 +512,54 @@ @return reference to the rope project object (RopeProject) """ return self.__project -## -## def confirmBufferIsSaved(self, editor): -## """ -## Public method to check, if an editor has unsaved changes. -## -## @param editor reference to the editor to be checked -## """ -## res = editor.checkDirty() -## self.__project.validate(self.__project.root) -## return res -## -## def confirmAllBuffersSaved(self): -## """ -## Private method to check, if any editor has unsaved changes. -## """ -## res = e5App().getObject("ViewManager").checkAllDirty() -## self.__project.validate(self.__project.root) -## return res -## -## def refreshEditors(self, changes): -## """ -## Public method to refresh modified editors. -## -## @param reference to the Changes object (rope.base.change.ChangeSet) -## """ -## vm = e5App().getObject("ViewManager") -## -## changedFiles = [] -## for resource in changes.get_changed_resources(): -## if not resource.is_folder(): -## changedFiles.append(resource.real_path) -## -## openFiles = [Utilities.normcasepath(f) for f in vm.getOpenFilenames()] -## -## for file in changedFiles: -## normfile = Utilities.normcasepath(file) -## if normfile in openFiles: -## editor = vm.getEditor(normfile)[1] -## editor.refresh() -## -## aw = vm.activeWindow() -## if aw is not None: -## filename = aw.getFileName() -## if filename is not None: -## vm.openSourceFile(filename, aw.getCursorPosition()[0] + 1) + + def confirmBufferIsSaved(self, editor): + """ + Public method to check, if an editor has unsaved changes. + + @param editor reference to the editor to be checked + @return flag indicating, that the editor doesn't contain + unsaved edits (boolean) + """ + res = editor.checkDirty() + self.__project.validate(self.__project.root) + return res + + def confirmAllBuffersSaved(self): + """ + Private method to check, if any editor has unsaved changes. + + @return flag indicating, that no editor contains unsaved edits + (boolean) + """ + res = e5App().getObject("ViewManager").checkAllDirty() + self.__project.validate(self.__project.root) + return res + + def refreshEditors(self, changes): + """ + Public method to refresh modified editors. + + @param reference to the Changes object (rope.base.change.ChangeSet) + """ + vm = e5App().getObject("ViewManager") + + changedFiles = [] + for resource in changes.get_changed_resources(): + if not resource.is_folder(): + changedFiles.append(resource.real_path) + + openFiles = [Utilities.normcasepath(f) for f in vm.getOpenFilenames()] + + for file in changedFiles: + normfile = Utilities.normcasepath(file) + if normfile in openFiles: + editor = vm.getEditor(normfile)[1] + editor.refresh() + + aw = vm.activeWindow() + if aw is not None: + filename = aw.getFileName() + if filename is not None: + vm.openSourceFile(filename, aw.getCursorPosition()[0] + 1) +