RefactoringRope/Refactoring.py

changeset 3
3be1b4662b48
parent 2
fc72a5b922a6
child 4
2e2463ef1aae
--- a/RefactoringRope/Refactoring.py	Sat Jan 29 13:38:30 2011 +0100
+++ b/RefactoringRope/Refactoring.py	Sat Jan 29 15:10:40 2011 +0100
@@ -16,7 +16,22 @@
 import rope.base.project
 import rope.base.exceptions
 
+import rope.refactor.rename
+##import rope.refactor.extract
+##import rope.refactor.usefunction
+##import rope.refactor.inline
+##import rope.refactor.move
+##import rope.refactor.change_signature
+##import rope.refactor.introduce_factory
+##import rope.refactor.introduce_parameter
+##import rope.refactor.method_object
+##import rope.refactor.encapsulate_field
+##import rope.refactor.localtofield
+##import rope.refactor.topackage
+##from rope.refactor.importutils import ImportOrganizer
+
 import rope.contrib.findit
+##import rope.contrib.finderrors
 
 from PyQt4.QtCore import QObject, SIGNAL
 from PyQt4.QtGui import QMenu, QApplication, QMessageBox
@@ -29,9 +44,11 @@
 from QScintilla.MiniEditor import MiniEditor
 
 from FileSystemCommands import e5FileSystemCommands
+from ProgressHandle import ProgressHandle
 from HelpDialog import HelpDialog
-from ProgressHandle import ProgressHandle
 from MatchesDialog import MatchesDialog
+from RenameDialog import RenameDialog
+from ChangeOccurrencesDialog import ChangeOccurrencesDialog
 
 import Utilities
 
@@ -72,6 +89,85 @@
         self.actions = []
         
         #####################################################
+        ## Rename refactoring actions
+        #####################################################
+        
+        self.refactoringRenameAct = E5Action(self.trUtf8('Rename'),
+                self.trUtf8('&Rename'), 
+                0, 0,
+                self,'refactoring_rename')
+        self.refactoringRenameAct.setStatusTip(self.trUtf8(
+            'Rename the highlighted object'))
+        self.refactoringRenameAct.setWhatsThis(self.trUtf8(
+            """<b>Rename</b>"""
+            """<p>Rename the highlighted Python object.</p>"""
+        ))
+        if self.__newStyle:
+            self.refactoringRenameAct.triggered[()].connect(
+                self.__rename)
+        else:
+            self.connect(self.refactoringRenameAct, SIGNAL('triggered()'), 
+                self.__rename)
+        self.actions.append(self.refactoringRenameAct)
+        
+        self.refactoringRenameLocalAct = E5Action(self.trUtf8('Local Rename'),
+                self.trUtf8('&Local Rename'), 
+                0, 0,
+                self,'refactoring_rename_local')
+        self.refactoringRenameLocalAct.setStatusTip(self.trUtf8(
+            'Rename the highlighted object in the current module only'))
+        self.refactoringRenameLocalAct.setWhatsThis(self.trUtf8(
+            """<b>Local Rename</b>"""
+            """<p>Rename the highlighted Python object in the current"""
+            """ module only.</p>"""
+        ))
+        if self.__newStyle:
+            self.refactoringRenameLocalAct.triggered[()].connect(
+                self.__renameLocal)
+        else:
+            self.connect(self.refactoringRenameLocalAct, 
+                SIGNAL('triggered()'), self.__renameLocal)
+        self.actions.append(self.refactoringRenameLocalAct)
+        
+        self.refactoringRenameModuleAct = E5Action(
+                self.trUtf8('Rename Current Module'),
+                self.trUtf8('Rename Current Module'), 
+                0, 0,
+                self,'refactoring_rename_module')
+        self.refactoringRenameModuleAct.setStatusTip(self.trUtf8(
+            'Rename the current module'))
+        self.refactoringRenameModuleAct.setWhatsThis(self.trUtf8(
+            """<b>Rename Current Module</b>"""
+            """<p>Rename the current module.</p>"""
+        ))
+        if self.__newStyle:
+            self.refactoringRenameModuleAct.triggered[()].connect(
+                self.__renameModule)
+        else:
+            self.connect(self.refactoringRenameModuleAct, 
+                SIGNAL('triggered()'), self.__renameModule)
+        self.actions.append(self.refactoringRenameModuleAct)
+        
+        self.refactoringChangeOccurrencesAct = E5Action(
+                self.trUtf8('Change Occurrences'),
+                self.trUtf8('Change &Occurrences'), 
+                0, 0,
+                self,'refactoring_change_occurrences')
+        self.refactoringChangeOccurrencesAct.setStatusTip(self.trUtf8(
+            'Change all occurrences in the local scope'))
+        self.refactoringChangeOccurrencesAct.setWhatsThis(self.trUtf8(
+            """<b>Change Occurrences</b>"""
+            """<p>Change all occurrences in the local scope.</p>"""
+        ))
+        if self.__newStyle:
+            self.refactoringChangeOccurrencesAct.triggered[()].connect(
+                self.__changeOccurrences)
+        else:
+            self.connect(self.refactoringChangeOccurrencesAct, 
+                SIGNAL('triggered()'), self.__changeOccurrences)
+        self.actions.append(self.refactoringChangeOccurrencesAct)
+        
+        #####################################################
         ## Query actions
         #####################################################
         
@@ -195,6 +291,19 @@
         smenu.addAction(self.queryDefinitionAct)
         smenu.addAction(self.queryImplementationsAct)
         
+        smenu = menu.addMenu(self.trUtf8("&Refactoring"))
+        if self.__newStyle:
+            smenu.aboutToShow.connect(self.__showRefactoringMenu)
+        else:
+            self.connect(smenu, SIGNAL("aboutToShow()"), 
+                         self.__showRefactoringMenu)
+        smenu.addAction(self.refactoringRenameAct)
+        smenu.addAction(self.refactoringRenameLocalAct)
+        smenu.addAction(self.refactoringChangeOccurrencesAct)
+        smenu.addSeparator()
+        smenu.addAction(self.refactoringRenameModuleAct)
+        smenu.addSeparator()
+        
         menu.addSeparator()
         menu.addAction(self.refactoringEditConfigAct)
         menu.addAction(self.refactoringHelpAct)
@@ -215,6 +324,80 @@
             self.trUtf8("{0}\nVersion {1}\n\n{2}".format(
                 rope.INFO, rope.VERSION, rope.COPYRIGHT)))
     
+    def __canUndo(self):
+        """
+        Private slot to check, if there are changes to be undone.
+        
+        @return flag indicating, that undoable changes are available (boolean)
+        """
+        return self.__project is not None and \
+               len(self.__project.history.undo_list) > 0
+    
+    def __canRedo(self):
+        """
+        Private slot to check, if there are changes to be redone.
+        
+        @return flag indicating, that redoable changes are available (boolean)
+        """
+        return self.__project is not None and \
+               len(self.__project.history.redo_list) > 0
+    
+    def __getFileUndoList(self, resource):
+        """
+        Private slot to get a list of undoable changes.
+        
+        @param resource file resource to filter against
+            (rope.base.resources.File)
+        @return list of change objects (list of rope.base.change.Change)
+        """
+        undoList = []
+        for change in self.__project.history.undo_list:
+            if resource in change.get_changed_resources():
+                undoList.append(change)
+        return undoList
+    
+    def __getFileRedoList(self, resource):
+        """
+        Private slot to get a list of redoable changes.
+        
+        @param resource file resource to filter against
+            (rope.base.resources.File)
+        @return list of change objects (list of rope.base.change.Change)
+        """
+        redoList = []
+        for change in self.__project.history.redo_list:
+            if resource in change.get_changed_resources():
+                redoList.append(change)
+        return redoList
+    
+    def __canUndoFile(self, resource):
+        """
+        Private slot to check, if there are undoable changes for a resource.
+        
+        @param resource file resource to check against
+            (rope.base.resources.File)
+        @return flag indicating, that undoable changes are available (boolean)
+        """
+        return self.__canUndo() and len(self.__getFileUndoList(resource)) > 0
+    
+    def __canRedoFile(self, resource):
+        """
+        Private slot to check, if there are redoable changes for a resource.
+        
+        @param resource file resource to check against
+            (rope.base.resources.File)
+        @return flag indicating, that redoable changes are available (boolean)
+        """
+        return self.__canRedo() and len(self.__getFileRedoList(resource)) > 0
+    
+    def __showRefactoringMenu(self):
+        """
+        Private slot called before the refactoring menu is shown.
+        """
+        # TODO: enable these once undo/redo has been implemented
+##        self.refactoringUndoAct.setEnabled(self.__canUndo())
+##        self.refactoringRedoAct.setEnabled(self.__canRedo())
+    
     def handleRopeError(self, err, title, handle=None):
         """
         Public slot to handle a rope error.
@@ -239,6 +422,131 @@
             E5MessageBox.warning(self.__ui, title,
                 self.trUtf8("Rope error: {0}").format(str(err)))
 
+    ##################################################################
+    ## slots below implement the various refactorings
+    ##################################################################
+    
+    #####################################################
+    ## Rename refactorings
+    #####################################################
+    
+    def __rename(self):
+        """
+        Private slot to handle the Rename action.
+        """
+        self.__doRename(self.trUtf8('Rename'))
+    
+    def __renameLocal(self):
+        """
+        Private slot to handle the Local Rename action.
+        """
+        self.__doRename(self.trUtf8('Local Rename'), isLocal=True)
+    
+    def __renameModule(self):
+        """
+        Private slot to handle the Rename Current Module action.
+        """
+        self.__doRename(self.trUtf8('Rename Current Module'), 
+            renameModule=True)
+    
+    def __doRename(self, title, isLocal=False, renameModule=False):
+        """
+        Private method to perform the various renaming refactorings.
+        
+        @param title title of the refactoring (string)
+        @param isLocal flag indicating to restrict refactoring to 
+            the local file (boolean)
+        @param renameModule flag indicating a module rename refactoring
+            (boolean)
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        
+        if aw is None:
+            return
+        
+        if not renameModule and not aw.hasSelectedText():
+            # no selection available
+            E5MessageBox.warning(self.__ui, title,
+                self.trUtf8("Highlight the declaration you want to rename"
+                    " and try again."))
+            return 
+        
+        if isLocal:
+            if not self.confirmBufferIsSaved(aw):
+                return
+        else:
+            if not self.confirmAllBuffersSaved():
+                return
+        
+        filename = aw.getFileName()
+        if renameModule:
+            offset = None
+        else:
+            line, index, line1, index1 = aw.getSelection()
+            if line != line1:
+                # selection span more than one line
+                E5MessageBox.warning(self.__ui, title,
+                    self.trUtf8("The selection must not extend beyond"
+                                " one line."))
+                return 
+            index = int(index  + (index1 - index) / 2)
+            # keep it inside the object
+            offset = aw.positionFromLineIndex(line, index)
+        
+        resource = rope.base.libutils.path_to_resource(
+            self.__project, filename)
+        try:
+            renamer = rope.refactor.rename.Rename(
+                self.__project, resource, offset)
+        except Exception as err:
+            self.handleRopeError(err, title)
+            return 
+        
+        if isLocal:
+            localResource = resource
+        else:
+            localResource = None
+        self.dlg = RenameDialog(self, title, renamer, resource=localResource,
+                                parent=self.__ui)
+        self.dlg.show()
+    
+    def __changeOccurrences(self):
+        """
+        Private slot to perform the Change Occurrences refactoring.
+        """
+        aw = e5App().getObject("ViewManager").activeWindow()
+        
+        if aw is None:
+            return
+        
+        title = self.trUtf8("Change Occurrences")
+        if not aw.hasSelectedText():
+            # no selection available
+            E5MessageBox.warning(self.__ui, title,
+                self.trUtf8("Highlight an occurrence to be changed"
+                    " and try again."))
+            return 
+        
+        if not self.confirmBufferIsSaved(aw):
+            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:
+            renamer = rope.refactor.rename.ChangeOccurrences(
+                        self.__project, resource, offset)
+        except Exception as err:
+            self.handleRopeError(err, title)
+            return 
+        
+        self.dlg = ChangeOccurrencesDialog(self, title, renamer, 
+                                           parent=self.__ui)
+        self.dlg.show()
+    
     #####################################################
     ## Find actions
     #####################################################

eric ide

mercurial