RefactoringRope/RefactoringServer.py

Wed, 23 Oct 2024 17:45:37 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 23 Oct 2024 17:45:37 +0200
branch
eric7
changeset 420
fa31c3a0df1d
parent 416
45159308166f
child 426
7592a1c052e8
permissions
-rw-r--r--

Adjusted code for eric7 24.10 and newer.

# -*- coding: utf-8 -*-

# Copyright (c) 2010 - 2024 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the refactoring interface to rope.
"""

import contextlib
import os

from PyQt6.Qsci import QsciScintilla
from PyQt6.QtCore import pyqtSlot
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QApplication, QMenu

from eric7 import Preferences, Utilities
from eric7.EricGui.EricAction import EricAction
from eric7.EricNetwork.EricJsonServer import EricJsonServer
from eric7.EricWidgets import EricMessageBox
from eric7.EricWidgets.EricApplication import ericApp
from eric7.Preferences.Shortcuts import readShortcuts

try:
    from eric7.SystemUtilities.FileSystemUtilities import normcasepath
except ImportError:
    # backward compatibility for eric < 23.1
    from eric7.Utilities import normcasepath
try:
    from eric7.SystemUtilities.PythonUtilities import (
        getPythonExecutable,
        getPythonLibraryDirectory,
    )
except ImportError:
    # backward compatibility for eric < 23.1
    from eric7.Globals import getPythonExecutable, getPythonLibraryDirectory
try:
    from eric7.SystemUtilities.FileSystemUtilities import isRemoteFileName
except ImportError:
    # backward compatibility for eric < 24.1
    from .RefactoringRope.RopeUtilities import isRemoteFileName

from .RopeProgressDialog import RopeProgressDialog


class RefactoringServer(EricJsonServer):
    """
    Class implementing the refactoring interface to rope.
    """

    def __init__(self, plugin, parent=None):
        """
        Constructor

        @param plugin reference to the plugin object
        @type RefactoringRopePlugin
        @param parent parent
        @type QObject
        """
        from .FileSystemCommands import EricFileSystemCommands

        try:
            super().__init__(
                name="RefactoringServer",
                interface=Preferences.getDebugger("NetworkInterface"),
                parent=parent,
            )
        except TypeError:
            # backward compatibility for eric < 24.10
            super().__init__(
                name="RefactoringServer",
                parent=parent,
            )

        self.__plugin = plugin
        self.__ui = parent
        self.__vm = ericApp().getObject("ViewManager")
        self.__ericProject = ericApp().getObject("Project")
        self.__projectpath = ""
        self.__projectLanguage = ""
        self.__projectopen = False
        self.__ropeConfig = {}

        self.__mainMenu = None
        self.__mainAct = None
        self.__separatorAct = None

        self.__progressDialog = None
        self.__helpDialog = None
        self.__historyDialog = None
        self.__refactoringDialogs = {}

        self.__fsCommands = EricFileSystemCommands(self.__ericProject)

        self.__methodMapping = {
            "Config": self.__setConfig,
            "Progress": self.__processProgress,
            "QueryReferencesResult": self.__queryReferencesResult,
            "QueryDefinitionResult": self.__queryDefinitionResult,
            "QueryImplementationsResult": self.__queryImplementationsResult,
            "SoaFinished": self.__soaFinished,
            "FileSystemCommand": self.__fsCommands.processFileSystemCommand,
            "ClientException": self.__processClientException,
            "HistoryResult": self.__processHistoryResult,
            "Changes": self.__processChanges,
        }

    @pyqtSlot()
    def handleNewConnection(self):
        """
        Public slot for new incoming connections from a client.
        """
        super().handleNewConnection()

        self.sendJson("GetConfig", {})

    def activate(self):
        """
        Public method to activate the refactoring server.

        This is performed when the rope plug-in is activated.
        """
        self.__initActions()
        ericApp().registerPluginObject("RefactoringRope", self)
        readShortcuts(pluginName="RefactoringRope")

        self.__mainMenu = self.__initMenu()
        projectToolsMenu = self.__ui.getMenu("project_tools")
        if projectToolsMenu is not None:
            insertBeforeAct = projectToolsMenu.actions()[0]
            self.__mainAct = projectToolsMenu.insertMenu(
                insertBeforeAct, self.__mainMenu
            )
            self.__separatorAct = projectToolsMenu.insertSeparator(insertBeforeAct)
        else:
            projectAct = self.__ui.getMenuBarAction("project")
            actions = self.__ui.menuBar().actions()
            insertBeforeAct = actions[actions.index(projectAct) + 1]
            self.__mainAct = self.__ui.menuBar().insertMenu(
                insertBeforeAct, self.__mainMenu
            )
        self.__mainAct.setEnabled(False)

        if ericApp().getObject("Project").isOpen():
            self.projectOpened()

        ericApp().getObject("Project").projectOpened.connect(self.projectOpened)
        ericApp().getObject("Project").projectPropertiesChanged.connect(
            self.projectOpened
        )
        ericApp().getObject("Project").projectClosed.connect(self.projectClosed)
        ericApp().getObject("Project").newProject.connect(self.projectOpened)

    def deactivate(self):
        """
        Public method to deactivate the refactoring server.
        """
        ericApp().unregisterPluginObject("RefactoringRope")

        ericApp().getObject("Project").projectOpened.disconnect(self.projectOpened)
        ericApp().getObject("Project").projectPropertiesChanged.disconnect(
            self.projectOpened
        )
        ericApp().getObject("Project").projectClosed.disconnect(self.projectClosed)
        ericApp().getObject("Project").newProject.disconnect(self.projectOpened)

        projectToolsMenu = self.__ui.getMenu("project_tools")
        if projectToolsMenu is not None:
            projectToolsMenu.removeAction(self.__separatorAct)
            projectToolsMenu.removeAction(self.__mainAct)
        else:
            self.__ui.menuBar().removeAction(self.__mainAct)

        self.projectClosed()

    def getMainWindow(self):
        """
        Public method to get a reference to the IDE main window.

        @return reference to the IDE main window
        @rtype UserInterface
        """
        return self.__ui

    def __initActions(self):
        """
        Private method to define the refactoring actions.
        """
        self.actions = []

        #####################################################
        ## Rename refactoring actions
        #####################################################

        self.refactoringRenameAct = EricAction(
            self.tr("Rename"), self.tr("&Rename"), 0, 0, self, "refactoring_rename"
        )
        self.refactoringRenameAct.setStatusTip(self.tr("Rename the highlighted object"))
        self.refactoringRenameAct.setWhatsThis(
            self.tr("""<b>Rename</b><p>Rename the highlighted Python object.</p>""")
        )
        self.refactoringRenameAct.triggered.connect(self.__rename)
        self.actions.append(self.refactoringRenameAct)

        self.refactoringRenameLocalAct = EricAction(
            self.tr("Local Rename"),
            self.tr("&Local Rename"),
            0,
            0,
            self,
            "refactoring_rename_local",
        )
        self.refactoringRenameLocalAct.setStatusTip(
            self.tr("Rename the highlighted object in the current module only")
        )
        self.refactoringRenameLocalAct.setWhatsThis(
            self.tr(
                """<b>Local Rename</b>"""
                """<p>Rename the highlighted Python object in the current"""
                """ module only.</p>"""
            )
        )
        self.refactoringRenameLocalAct.triggered.connect(self.__renameLocal)
        self.actions.append(self.refactoringRenameLocalAct)

        self.refactoringRenameModuleAct = EricAction(
            self.tr("Rename Current Module"),
            self.tr("Rename Current Module"),
            0,
            0,
            self,
            "refactoring_rename_module",
        )
        self.refactoringRenameModuleAct.setStatusTip(
            self.tr("Rename the current module")
        )
        self.refactoringRenameModuleAct.setWhatsThis(
            self.tr(
                """<b>Rename Current Module</b>"""
                """<p>Rename the current module.</p>"""
            )
        )
        self.refactoringRenameModuleAct.triggered.connect(self.__renameModule)
        self.actions.append(self.refactoringRenameModuleAct)

        self.refactoringChangeOccurrencesAct = EricAction(
            self.tr("Change Occurrences"),
            self.tr("Change &Occurrences"),
            0,
            0,
            self,
            "refactoring_change_occurrences",
        )
        self.refactoringChangeOccurrencesAct.setStatusTip(
            self.tr("Change all occurrences in the local scope")
        )
        self.refactoringChangeOccurrencesAct.setWhatsThis(
            self.tr(
                """<b>Change Occurrences</b>"""
                """<p>Change all occurrences in the local scope.</p>"""
            )
        )
        self.refactoringChangeOccurrencesAct.triggered.connect(self.__changeOccurrences)
        self.actions.append(self.refactoringChangeOccurrencesAct)

        #####################################################
        ## Extract refactoring actions
        #####################################################

        self.refactoringExtractMethodAct = EricAction(
            self.tr("Extract method"),
            self.tr("Extract &Method"),
            0,
            0,
            self,
            "refactoring_extract_method",
        )
        self.refactoringExtractMethodAct.setStatusTip(
            self.tr("Extract the highlighted area as a method")
        )
        self.refactoringExtractMethodAct.setWhatsThis(
            self.tr(
                """<b>Extract method</b>"""
                """<p>Extract the highlighted area as a method or function.</p>"""
            )
        )
        self.refactoringExtractMethodAct.triggered.connect(self.__extractMethod)
        self.actions.append(self.refactoringExtractMethodAct)

        self.refactoringExtractLocalVariableAct = EricAction(
            self.tr("Extract local variable"),
            self.tr("&Extract Local Variable"),
            0,
            0,
            self,
            "refactoring_extract_variable",
        )
        self.refactoringExtractLocalVariableAct.setStatusTip(
            self.tr("Extract the highlighted area as a local variable")
        )
        self.refactoringExtractLocalVariableAct.setWhatsThis(
            self.tr(
                """<b>Extract local variable</b>"""
                """<p>Extract the highlighted area as a local variable.</p>"""
            )
        )
        self.refactoringExtractLocalVariableAct.triggered.connect(
            self.__extractLocalVariable
        )
        self.actions.append(self.refactoringExtractLocalVariableAct)

        #####################################################
        ## Inline refactoring actions
        #####################################################

        self.refactoringInlineAct = EricAction(
            self.tr("Inline"), self.tr("&Inline"), 0, 0, self, "refactoring_inline"
        )
        self.refactoringInlineAct.setStatusTip(
            self.tr("Inlines the selected local variable or method")
        )
        self.refactoringInlineAct.setWhatsThis(
            self.tr(
                """<b>Inline</b>"""
                """<p>Inlines the selected local variable or method.</p>"""
            )
        )
        self.refactoringInlineAct.triggered.connect(self.__inline)
        self.actions.append(self.refactoringInlineAct)

        #####################################################
        ## Move refactoring actions
        #####################################################

        self.refactoringMoveMethodAct = EricAction(
            self.tr("Move method"),
            self.tr("Mo&ve Method"),
            0,
            0,
            self,
            "refactoring_move_method",
        )
        self.refactoringMoveMethodAct.setStatusTip(
            self.tr("Move the highlighted method to another class")
        )
        self.refactoringMoveMethodAct.setWhatsThis(
            self.tr(
                """<b>Move method</b>"""
                """<p>Move the highlighted method to another class.</p>"""
            )
        )
        self.refactoringMoveMethodAct.triggered.connect(
            lambda: self.__move("move_method")
        )
        self.actions.append(self.refactoringMoveMethodAct)

        self.refactoringMoveModuleAct = EricAction(
            self.tr("Move current module"),
            self.tr("Move Current Module"),
            0,
            0,
            self,
            "refactoring_move_module",
        )
        self.refactoringMoveModuleAct.setStatusTip(
            self.tr("Move the current module to another package")
        )
        self.refactoringMoveModuleAct.setWhatsThis(
            self.tr(
                """<b>Move current module</b>"""
                """<p>Move the current module to another package.</p>"""
            )
        )
        self.refactoringMoveModuleAct.triggered.connect(
            lambda: self.__move("move_module")
        )
        self.actions.append(self.refactoringMoveModuleAct)

        #####################################################
        ## Use function refactoring action
        #####################################################

        self.refactoringUseFunctionAct = EricAction(
            self.tr("Use Function"),
            self.tr("Use Function"),
            0,
            0,
            self,
            "refactoring_use_function",
        )
        self.refactoringUseFunctionAct.setStatusTip(
            self.tr("Use a function wherever possible.")
        )
        self.refactoringUseFunctionAct.setWhatsThis(
            self.tr(
                """<b>Use function</b>"""
                """<p>Tries to use a function wherever possible.</p>"""
            )
        )
        self.refactoringUseFunctionAct.triggered.connect(self.__useFunction)
        self.actions.append(self.refactoringUseFunctionAct)

        #####################################################
        ## Introduce refactorings actions
        #####################################################

        self.refactoringIntroduceFactoryAct = EricAction(
            self.tr("Introduce Factory Method"),
            self.tr("Introduce &Factory Method"),
            0,
            0,
            self,
            "refactoring_introduce_factory_method",
        )
        self.refactoringIntroduceFactoryAct.setStatusTip(
            self.tr("Introduce a factory method or function")
        )
        self.refactoringIntroduceFactoryAct.setWhatsThis(
            self.tr(
                """<b>Introduce Factory Method</b>"""
                """<p>Introduce a factory method or function.</p>"""
            )
        )
        self.refactoringIntroduceFactoryAct.triggered.connect(
            self.__introduceFactoryMethod
        )
        self.actions.append(self.refactoringIntroduceFactoryAct)

        self.refactoringIntroduceParameterAct = EricAction(
            self.tr("Introduce Parameter"),
            self.tr("Introduce &Parameter"),
            0,
            0,
            self,
            "refactoring_introduce_parameter_method",
        )
        self.refactoringIntroduceParameterAct.setStatusTip(
            self.tr("Introduce a parameter in a function")
        )
        self.refactoringIntroduceParameterAct.setWhatsThis(
            self.tr(
                """<b>Introduce Parameter</b>"""
                """<p>Introduce a parameter in a function.</p>"""
            )
        )
        self.refactoringIntroduceParameterAct.triggered.connect(
            self.__introduceParameter
        )
        self.actions.append(self.refactoringIntroduceParameterAct)

        #####################################################
        ## Import refactorings actions
        #####################################################

        self.refactoringImportsOrganizeAct = EricAction(
            self.tr("Organize Imports"),
            self.tr("&Organize Imports"),
            0,
            0,
            self,
            "refactoring_organize_imports",
        )
        self.refactoringImportsOrganizeAct.setStatusTip(
            self.tr("Sort imports according to PEP-8")
        )
        self.refactoringImportsOrganizeAct.setWhatsThis(
            self.tr(
                """<b>Organize Imports</b>"""
                """<p>Sort imports according to PEP-8.</p>"""
            )
        )
        self.refactoringImportsOrganizeAct.triggered.connect(self.__importsOrganize)
        self.actions.append(self.refactoringImportsOrganizeAct)

        self.refactoringImportsStarExpandAct = EricAction(
            self.tr("Expand Star Imports"),
            self.tr("E&xpand Star Imports"),
            0,
            0,
            self,
            "refactoring_expand_star_imports",
        )
        self.refactoringImportsStarExpandAct.setStatusTip(
            self.tr('Expand imports like "from xxx import *"')
        )
        self.refactoringImportsStarExpandAct.setWhatsThis(
            self.tr(
                """<b>Expand Star Imports</b>"""
                """<p>Expand imports like "from xxx import *".</p>"""
                """<p>Select the import to act on or none to do all."""
                """ Unused imports are deleted.</p>"""
            )
        )
        self.refactoringImportsStarExpandAct.triggered.connect(self.__importsExpandStar)
        self.actions.append(self.refactoringImportsStarExpandAct)

        self.refactoringImportsRelativeToAbsoluteAct = EricAction(
            self.tr("Relative to Absolute"),
            self.tr("Relative to &Absolute"),
            0,
            0,
            self,
            "refactoring_relative_to_absolute_imports",
        )
        self.refactoringImportsRelativeToAbsoluteAct.setStatusTip(
            self.tr("Transform relative imports to absolute ones")
        )
        self.refactoringImportsRelativeToAbsoluteAct.setWhatsThis(
            self.tr(
                """<b>Relative to Absolute</b>"""
                """<p>Transform relative imports to absolute ones.</p>"""
                """<p>Select the import to act on or none to do all."""
                """ Unused imports are deleted.</p>"""
            )
        )
        self.refactoringImportsRelativeToAbsoluteAct.triggered.connect(
            self.__importsRelativeToAbsolute
        )
        self.actions.append(self.refactoringImportsRelativeToAbsoluteAct)

        self.refactoringImportsFromsToImportsAct = EricAction(
            self.tr("Froms to Imports"),
            self.tr("Froms to &Imports"),
            0,
            0,
            self,
            "refactoring_froms_to_imports",
        )
        self.refactoringImportsFromsToImportsAct.setStatusTip(
            self.tr("Transform From imports to plain imports")
        )
        self.refactoringImportsFromsToImportsAct.setWhatsThis(
            self.tr(
                """<b>Froms to Imports</b>"""
                """<p>Transform From imports to plain imports.</p>"""
                """<p>Select the import to act on or none to do all."""
                """ Unused imports are deleted.</p>"""
            )
        )
        self.refactoringImportsFromsToImportsAct.triggered.connect(
            self.__importsFromToImport
        )
        self.actions.append(self.refactoringImportsFromsToImportsAct)

        self.refactoringImportsHandleLongAct = EricAction(
            self.tr("Handle Long Imports"),
            self.tr("Handle &Long Imports"),
            0,
            0,
            self,
            "refactoring_organize_imports",
        )
        self.refactoringImportsHandleLongAct.setStatusTip(
            self.tr("Transform long import statements to look better")
        )
        self.refactoringImportsHandleLongAct.setWhatsThis(
            self.tr(
                """<b>Handle Long Imports</b>"""
                """<p>Transform long import statements to look better.</p>"""
                """<p>Select the import to act on or none to do all."""
                """ Unused imports are deleted.</p>"""
            )
        )
        self.refactoringImportsHandleLongAct.triggered.connect(self.__importsHandleLong)
        self.actions.append(self.refactoringImportsHandleLongAct)

        #####################################################
        ## Various refactorings actions
        #####################################################

        self.refactoringRestructureAct = EricAction(
            self.tr("Restructure"),
            self.tr("Res&tructure"),
            0,
            0,
            self,
            "refactoring_restructure",
        )
        self.refactoringRestructureAct.setStatusTip(self.tr("Restructure code"))
        self.refactoringRestructureAct.setWhatsThis(
            self.tr(
                """<b>Restructure</b>"""
                """<p>Restructure code. See "Rope Help" for examples.</p>"""
            )
        )
        self.refactoringRestructureAct.triggered.connect(self.__restructure)
        self.actions.append(self.refactoringRestructureAct)

        self.refactoringChangeSignatureAct = EricAction(
            self.tr("Change Method Signature"),
            self.tr("&Change Method Signature"),
            0,
            0,
            self,
            "refactoring_change_method_signature",
        )
        self.refactoringChangeSignatureAct.setStatusTip(
            self.tr("Change the signature of the selected method or function")
        )
        self.refactoringChangeSignatureAct.setWhatsThis(
            self.tr(
                """<b>Change Method Signature</b>"""
                """<p>Change the signature of the selected method"""
                """ or function.</p>"""
            )
        )
        self.refactoringChangeSignatureAct.triggered.connect(self.__changeSignature)
        self.actions.append(self.refactoringChangeSignatureAct)

        self.refactoringInlineArgumentDefaultAct = EricAction(
            self.tr("Inline Argument Default"),
            self.tr("Inline &Argument Default"),
            0,
            0,
            self,
            "refactoring_inline_argument_default",
        )
        self.refactoringInlineArgumentDefaultAct.setStatusTip(
            self.tr("Inline a parameters default value")
        )
        self.refactoringInlineArgumentDefaultAct.setWhatsThis(
            self.tr(
                """<b>Inline Argument Default</b>"""
                """<p>Inline a parameters default value.</p>"""
            )
        )
        self.refactoringInlineArgumentDefaultAct.triggered.connect(
            self.__inlineArgumentDefault
        )
        self.actions.append(self.refactoringInlineArgumentDefaultAct)

        self.refactoringTransformModuleAct = EricAction(
            self.tr("Transform Module to Package"),
            self.tr("Transform Module to Package"),
            0,
            0,
            self,
            "refactoring_transform_module_to_package",
        )
        self.refactoringTransformModuleAct.setStatusTip(
            self.tr("Transform the current module to a package")
        )
        self.refactoringTransformModuleAct.setWhatsThis(
            self.tr(
                """<b>Transform Module to Package</b>"""
                """<p>Transform the current module to a package.</p>"""
            )
        )
        self.refactoringTransformModuleAct.triggered.connect(
            self.__transformModuleToPackage
        )
        self.actions.append(self.refactoringTransformModuleAct)

        self.refactoringEncapsulateAttributeAct = EricAction(
            self.tr("Encapsulate Attribute"),
            self.tr("Encap&sulate Attribute"),
            0,
            0,
            self,
            "refactoring_encapsulate_attribute",
        )
        self.refactoringEncapsulateAttributeAct.setStatusTip(
            self.tr("Generate a getter/setter for an attribute")
        )
        self.refactoringEncapsulateAttributeAct.setWhatsThis(
            self.tr(
                """<b>Encapsulate Attribute</b>"""
                """<p>Generate a getter/setter for an attribute and changes"""
                """ its occurrences to use them.</p>"""
            )
        )
        self.refactoringEncapsulateAttributeAct.triggered.connect(
            self.__encapsulateAttribute
        )
        self.actions.append(self.refactoringEncapsulateAttributeAct)

        self.refactoringLocalVariableToAttributeAct = EricAction(
            self.tr("Local Variable to Attribute"),
            self.tr("Local Varia&ble to Attribute"),
            0,
            0,
            self,
            "refactoring_local_variable_to_attribute",
        )
        self.refactoringLocalVariableToAttributeAct.setStatusTip(
            self.tr("Change a local variable to an attribute")
        )
        self.refactoringLocalVariableToAttributeAct.setWhatsThis(
            self.tr(
                """<b>Local Variable to Attribute</b>"""
                """<p>Change a local variable to an attribute.</p>"""
            )
        )
        self.refactoringLocalVariableToAttributeAct.triggered.connect(
            self.__convertLocalToAttribute
        )
        self.actions.append(self.refactoringLocalVariableToAttributeAct)

        self.refactoringMethodToMethodObjectAct = EricAction(
            self.tr("Method To Method Object"),
            self.tr("Method To Method Ob&ject"),
            0,
            0,
            self,
            "refactoring_method_to_methodobject",
        )
        self.refactoringMethodToMethodObjectAct.setStatusTip(
            self.tr("Transform a function or a method to a method object")
        )
        self.refactoringMethodToMethodObjectAct.setWhatsThis(
            self.tr(
                """<b>Method To Method Object</b>"""
                """<p>Transform a function or a method to a method object.</p>"""
            )
        )
        self.refactoringMethodToMethodObjectAct.triggered.connect(
            self.__methodToMethodObject
        )
        self.actions.append(self.refactoringMethodToMethodObjectAct)

        #####################################################
        ## History actions
        #####################################################

        self.refactoringProjectHistoryAct = EricAction(
            self.tr("Show Project History"),
            self.tr("Show Project History..."),
            0,
            0,
            self,
            "refactoring_show_project_history",
        )
        self.refactoringProjectHistoryAct.setStatusTip(
            self.tr("Show the refactoring history of the project")
        )
        self.refactoringProjectHistoryAct.setWhatsThis(
            self.tr(
                """<b>Show Project History</b>"""
                """<p>This opens a dialog to show the refactoring history of"""
                """ the project.</p>"""
            )
        )
        self.refactoringProjectHistoryAct.triggered.connect(self.__showProjectHistory)
        self.actions.append(self.refactoringProjectHistoryAct)

        self.refactoringFileHistoryAct = EricAction(
            self.tr("Show Current File History"),
            self.tr("Show Current File History..."),
            0,
            0,
            self,
            "refactoring_show_file_history",
        )
        self.refactoringFileHistoryAct.setStatusTip(
            self.tr("Show the refactoring history of the current file")
        )
        self.refactoringFileHistoryAct.setWhatsThis(
            self.tr(
                """<b>Show Current File History</b>"""
                """<p>This opens a dialog to show the refactoring history of"""
                """ the current file.</p>"""
            )
        )
        self.refactoringFileHistoryAct.triggered.connect(self.__showFileHistory)
        self.actions.append(self.refactoringFileHistoryAct)

        self.refactoringClearHistoryAct = EricAction(
            self.tr("Clear History"),
            self.tr("Clear History"),
            0,
            0,
            self,
            "refactoring_clear_history",
        )
        self.refactoringClearHistoryAct.setStatusTip(
            self.tr("Clear the refactoring history")
        )
        self.refactoringClearHistoryAct.setWhatsThis(
            self.tr("""<b>Clear History</b><p>Clears the refactoring history.</p>""")
        )
        self.refactoringClearHistoryAct.triggered.connect(self.__clearHistory)
        self.actions.append(self.refactoringClearHistoryAct)

        #####################################################
        ## Query actions
        #####################################################

        self.queryReferencesAct = EricAction(
            self.tr("Find occurrences"),
            self.tr("Find &Occurrences"),
            0,
            0,
            self,
            "refactoring_find_occurrences",
        )
        self.queryReferencesAct.setStatusTip(
            self.tr("Find occurrences of the highlighted object")
        )
        self.queryReferencesAct.setWhatsThis(
            self.tr(
                """<b>Find occurrences</b>"""
                """<p>Find occurrences of the highlighted class, method,"""
                """ function or variable.</p>"""
            )
        )
        self.queryReferencesAct.triggered.connect(self.__queryReferences)
        self.actions.append(self.queryReferencesAct)

        self.queryDefinitionAct = EricAction(
            self.tr("Find definition"),
            self.tr("Find &Definition"),
            0,
            0,
            self,
            "refactoring_find_definition",
        )
        self.queryDefinitionAct.setStatusTip(
            self.tr("Find definition of the highlighted item")
        )
        self.queryDefinitionAct.setWhatsThis(
            self.tr(
                """<b>Find definition</b>"""
                """<p>Find the definition of the highlighted class, method,"""
                """ function or variable.</p>"""
            )
        )
        self.queryDefinitionAct.triggered.connect(self.__queryDefinition)
        self.actions.append(self.queryDefinitionAct)

        self.queryImplementationsAct = EricAction(
            self.tr("Find implementations"),
            self.tr("Find &Implementations"),
            0,
            0,
            self,
            "refactoring_find_implementations",
        )
        self.queryImplementationsAct.setStatusTip(
            self.tr("Find places where the selected method is overridden")
        )
        self.queryImplementationsAct.setWhatsThis(
            self.tr(
                """<b>Find implementations</b>"""
                """<p>Find places where the selected method is overridden.</p>"""
            )
        )
        self.queryImplementationsAct.triggered.connect(self.__queryImplementations)
        self.actions.append(self.queryImplementationsAct)

        #####################################################
        ## Various actions
        #####################################################

        self.refactoringEditConfigAct = EricAction(
            self.tr("Configure Rope"),
            self.tr("&Configure Rope"),
            0,
            0,
            self,
            "refactoring_edit_config",
        )
        self.refactoringEditConfigAct.setStatusTip(
            self.tr("Open the rope configuration file")
        )
        self.refactoringEditConfigAct.setWhatsThis(
            self.tr(
                """<b>Configure Rope</b>"""
                """<p>Opens the rope configuration file in an editor.</p>"""
            )
        )
        self.refactoringEditConfigAct.triggered.connect(self.__editConfig)
        self.refactoringEditConfigAct.setMenuRole(QAction.MenuRole.NoRole)
        self.actions.append(self.refactoringEditConfigAct)

        self.refactoringHelpAct = EricAction(
            self.tr("Rope Help"), self.tr("Rope &Help"), 0, 0, self, "refactoring_help"
        )
        self.refactoringHelpAct.setStatusTip(
            self.tr("Show help about the rope refactorings")
        )
        self.refactoringHelpAct.setWhatsThis(
            self.tr(
                """<b>Rope help</b>"""
                """<p>Show some help text about the rope refactorings.</p>"""
            )
        )
        self.refactoringHelpAct.triggered.connect(self.__showRopeHelp)
        self.actions.append(self.refactoringHelpAct)

        self.refactoringAllSoaAct = EricAction(
            self.tr("Analyse all modules"),
            self.tr("&Analyse all modules"),
            0,
            0,
            self,
            "refactoring_analyze_all",
        )
        self.refactoringAllSoaAct.setStatusTip(
            self.tr("Perform static object analysis on all modules")
        )
        self.refactoringAllSoaAct.setWhatsThis(
            self.tr(
                """<b>Analyse all modules</b>"""
                """<p>Perform static object analysis (SOA) on all modules. """
                """This might be time consuming. Analysis of all modules """
                """should only be neccessary, if the project was created """
                """with the rope plugin disabled or if files were added.</p>"""
            )
        )
        self.refactoringAllSoaAct.triggered.connect(self.__performSOA)
        self.actions.append(self.refactoringAllSoaAct)

        self.updateConfigAct = EricAction(
            self.tr("Update Configuration"),
            self.tr("&Update Configuration"),
            0,
            0,
            self,
            "refactoring_update_configuration",
        )
        self.updateConfigAct.setStatusTip(
            self.tr("Generates a new configuration file overwriting the current one.")
        )
        self.updateConfigAct.setWhatsThis(
            self.tr(
                """<b>Update Configuration</b>"""
                """<p>Generates a new configuration file overwriting"""
                """ the current one.</p>"""
            )
        )
        self.updateConfigAct.triggered.connect(self.__updateConfig)
        self.actions.append(self.updateConfigAct)

        for act in self.actions:
            act.setEnabled(False)

    def __initMenu(self):
        """
        Private slot to initialize the refactoring menu.

        @return the menu generated
        @rtype QMenu
        """
        menu = QMenu(self.tr("&Refactoring"), self.__ui)
        menu.setTearOffEnabled(True)

        act = menu.addAction("rope", self.__ropeInfo)
        font = act.font()
        font.setBold(True)
        act.setFont(font)
        menu.addSeparator()

        smenu = menu.addMenu(self.tr("&Query"))
        smenu.addAction(self.queryReferencesAct)
        smenu.addAction(self.queryDefinitionAct)
        smenu.addAction(self.queryImplementationsAct)

        smenu = menu.addMenu(self.tr("&Refactoring"))
        smenu.addAction(self.refactoringRenameAct)
        smenu.addAction(self.refactoringRenameLocalAct)
        smenu.addAction(self.refactoringChangeOccurrencesAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringExtractMethodAct)
        smenu.addAction(self.refactoringMoveMethodAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringInlineAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringExtractLocalVariableAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringUseFunctionAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringChangeSignatureAct)
        smenu.addAction(self.refactoringInlineArgumentDefaultAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringRestructureAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringIntroduceFactoryAct)
        smenu.addAction(self.refactoringIntroduceParameterAct)
        smenu.addAction(self.refactoringMethodToMethodObjectAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringEncapsulateAttributeAct)
        smenu.addAction(self.refactoringLocalVariableToAttributeAct)
        smenu.addSeparator()
        smenu.addAction(self.refactoringRenameModuleAct)
        smenu.addAction(self.refactoringMoveModuleAct)
        smenu.addAction(self.refactoringTransformModuleAct)
        smenu.addSeparator()

        imenu = smenu.addMenu(self.tr("Im&ports"))
        imenu.addAction(self.refactoringImportsOrganizeAct)
        imenu.addAction(self.refactoringImportsStarExpandAct)
        imenu.addAction(self.refactoringImportsRelativeToAbsoluteAct)
        imenu.addAction(self.refactoringImportsFromsToImportsAct)
        imenu.addAction(self.refactoringImportsHandleLongAct)

        smenu.addSeparator()

        hmenu = smenu.addMenu(self.tr("History"))
        hmenu.aboutToShow.connect(self.__showRefactoringHistoryMenu)
        hmenu.addAction(self.refactoringProjectHistoryAct)
        hmenu.addAction(self.refactoringFileHistoryAct)
        hmenu.addSeparator()
        hmenu.addAction(self.refactoringClearHistoryAct)

        smenu = menu.addMenu(self.tr("&Utilities"))
        smenu.addAction(self.refactoringAllSoaAct)
        smenu.addSeparator()
        smenu.addAction(self.updateConfigAct)

        menu.addSeparator()
        menu.addAction(self.refactoringEditConfigAct)
        menu.addAction(self.refactoringHelpAct)

        return menu

    ##################################################################
    ## slots below implement general functionality
    ##################################################################

    def __ropeInfo(self):
        """
        Private slot to show some info about rope.
        """
        if self.__ropeConfig:
            EricMessageBox.about(
                self.__ui,
                self.tr("About rope"),
                self.tr(
                    "{0}\nVersion {1}\n\n{2}".format(
                        self.__ropeConfig["RopeInfo"],
                        self.__ropeConfig["RopeVersion"],
                        self.__ropeConfig["RopeCopyright"],
                    )
                ),
            )

    def __showRefactoringHistoryMenu(self):
        """
        Private slot called before the refactoring history menu is shown.
        """
        aw = self.__vm.activeWindow()
        enable = aw is not None and bool(aw.getFileName())

        self.refactoringFileHistoryAct.setEnabled(enable)

    def handleRopeError(self, result):
        """
        Public method to handle a rope error.

        @param result dictionary containing the error information
        @type dict
        @return flag indicating, that the error is to be ignored
        @rtype bool
        """
        from .ErrorDialog import ErrorDialog

        if "Error" not in result:
            return True

        title = result.get("Title", self.tr("Rope Error"))

        if result["Error"] == "ModuleSyntaxError":
            res = EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("Rope error: {0}").format(result["ErrorString"]),
                EricMessageBox.Ok | EricMessageBox.Open,
            )
            if res == EricMessageBox.Open:
                self.__vm.openSourceFile(
                    os.path.join(
                        self.__ericProject.getProjectPath(), result["ErrorFile"]
                    ),
                    result["ErrorLine"],
                )
        elif result["Error"] == "InterruptedTaskError":
            return True
        else:
            ErrorDialog(
                title,
                self.tr("Rope error: {0}").format(result["ErrorString"]),
                traceback=result["Traceback"],
                parent=self.__ui,
            ).exec()

        return False

    def __getOffset(self, editor, line, index):
        r"""
        Private method to get the offset into the text treating CRLF as ONE
        character.

        Note: rope seems to convert all EOL styles to just \n.

        @param editor reference to the editor
        @type Editor
        @param line line for the offset
        @type int
        @param index index into line for the offset
        @type int
        @return rope compliant offset into the file
        @rtype int
        """
        source = editor.text()
        offset = len("".join(source.splitlines(True)[:line])) + index
        if editor.eolMode() == QsciScintilla.EolMode.EolWindows:
            offset -= line
        return offset

    ##################################################################
    ## slots below implement the various refactorings
    ##################################################################

    def __processChanges(self, result):
        """
        Private method to process the changes data sent by the refactoring
        client.

        @param result dictionary containing the changes data
        @type dict
        """
        if self.handleRopeError(result):
            changeGroup = result["ChangeGroup"]
            with contextlib.suppress(KeyError):
                self.__refactoringDialogs[changeGroup].processChangeData(result)

    def __refactoringDialogClosed(self, changeGroup):
        """
        Private slot handling the closing of a refactoring dialog.

        @param changeGroup name of the refactoring change group the dialog
            belonged to
        @type str
        """
        with contextlib.suppress(KeyError):
            del self.__refactoringDialogs[changeGroup]

    #####################################################
    ## Rename refactorings
    #####################################################

    def __rename(self):
        """
        Private slot to handle the Rename action.
        """
        self.__doRename(self.tr("Rename"))

    def __renameLocal(self):
        """
        Private slot to handle the Local Rename action.
        """
        self.__doRename(self.tr("Local Rename"), isLocal=True)

    def __renameModule(self):
        """
        Private slot to handle the Rename Current Module action.
        """
        self.__doRename(self.tr("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
        @type str
        @param isLocal flag indicating to restrict refactoring to
            the local file
        @type bool
        @param renameModule flag indicating a module rename refactoring
        @type bool
        """
        from .RenameDialog import RenameDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        if not renameModule and not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("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
            if os.path.basename(filename) == "__init__.py":
                filename = os.path.dirname(filename)
            selectedText, _ = os.path.splitext(os.path.basename(filename))
        else:
            line, index, line1, index1 = aw.getSelection()
            if line != line1:
                # selection span more than one line
                EricMessageBox.warning(
                    self.__ui,
                    title,
                    self.tr("The selection must not extend beyond one line."),
                )
                return
            index = int(index + (index1 - index) / 2)
            # keep it inside the object
            offset = self.__getOffset(aw, line, index)
            selectedText = aw.selectedText()

        dlg = RenameDialog(
            self,
            title,
            filename,
            offset,
            isLocal,
            selectedText=selectedText,
            parent=self.__ui,
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __changeOccurrences(self):
        """
        Private slot to perform the Change Occurrences refactoring.
        """
        from .ChangeOccurrencesDialog import ChangeOccurrencesDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Change Occurrences")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("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 = self.__getOffset(aw, line, index)

        dlg = ChangeOccurrencesDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Extract refactorings
    #####################################################

    def __extractMethod(self):
        """
        Private slot to handle the Extract Method action.
        """
        self.__doExtract(self.tr("Extract Method"), "method")

    def __extractLocalVariable(self):
        """
        Private slot to handle the Extract Local Variable action.
        """
        self.__doExtract(self.tr("Extract Local Variable"), "variable")

    def __doExtract(self, title, kind):
        """
        Private method to perform the extract refactoring.

        @param title title of the refactoring
        @type str
        @param kind kind of extraction to be done
        @type str ("method" or "variable")
        """
        from .ExtractDialog import ExtractDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr(
                    "Highlight the region of code you want to extract and try again."
                ),
            )
            return

        if not self.confirmBufferIsSaved(aw):
            return

        filename = aw.getFileName()
        startline, startcolumn, endline, endcolumn = aw.getSelection()
        startOffset = self.__getOffset(aw, startline, startcolumn)
        endOffset = self.__getOffset(aw, endline, endcolumn)

        dlg = ExtractDialog(
            self, title, filename, startOffset, endOffset, kind, parent=self.__ui
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Inline refactorings
    #####################################################

    def __inline(self):
        """
        Private slot to handle the Inline Local Variable action.
        """
        from .InlineDialog import InlineDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Inline")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr(
                    "Highlight the local variable, method or parameter"
                    " you want to inline and try again."
                ),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = InlineDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Move refactorings
    #####################################################

    def __move(self, moveKind):
        """
        Private slot to handle the Move Method action.

        @param moveKind kind of move to be performed
        @type str (one of 'move_method' or 'move_module')
        """
        from .MoveDialog import MoveDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        if moveKind == "move_method":
            title = self.tr("Move Method")
            if not aw.hasSelectedText():
                # no selection available
                EricMessageBox.warning(
                    self.__ui,
                    title,
                    self.tr("Highlight the method to move and try again."),
                )
                return
        else:
            title = self.tr("Move Current Module")

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        if moveKind == "move_method":
            line, index, line1, index1 = aw.getSelection()
            offset = self.__getOffset(aw, line, index)
        else:
            offset = None

        dlg = MoveDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Use function refactoring
    #####################################################

    def __useFunction(self):
        """
        Private slot to use a function wherever possible.
        """
        from .UseFunctionDialog import UseFunctionDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Use Function")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui, title, self.tr("Highlight a global function and try again.")
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = UseFunctionDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Introduce refactorings
    #####################################################

    def __introduceFactoryMethod(self):
        """
        Private slot to introduce a factory method or global function.
        """
        from .IntroduceFactoryDialog import IntroduceFactoryDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Introduce Factory Method")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr(
                    "Highlight the class to introduce a factory"
                    " method for and try again."
                ),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = IntroduceFactoryDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __introduceParameter(self):
        """
        Private slot to introduce a parameter in a function.
        """
        from .IntroduceParameterDialog import IntroduceParameterDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Introduce Parameter")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("Highlight the code for the new parameter and try again."),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = IntroduceParameterDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Import refactorings
    #####################################################

    def __importsOrganize(self):
        """
        Private slot to organize imports.
        """
        self.__doImports(self.tr("Organize Imports"), "organize_imports")

    def __importsExpandStar(self):
        """
        Private slot to expand star imports.
        """
        self.__doImports(self.tr("Expand Star Imports"), "expand_star_imports")

    def __importsRelativeToAbsolute(self):
        """
        Private slot to transform relative to absolute imports.
        """
        self.__doImports(self.tr("Relative to Absolute"), "relatives_to_absolutes")

    def __importsFromToImport(self):
        """
        Private slot to transform from imports to plain imports.
        """
        self.__doImports(self.tr("Froms to Imports"), "froms_to_imports")

    def __importsHandleLong(self):
        """
        Private slot to handle long imports.
        """
        self.__doImports(self.tr("Handle Long Imports"), "handle_long_imports")

    def __doImports(self, title, methodName):
        """
        Private method to perform the various imports refactorings.

        @param title title to be used for the import refactoring
        @type str
        @param methodName name of the method performing the import refactoring
        @type str
        """
        from .ConfirmationDialog import ConfirmationDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        if not self.confirmBufferIsSaved(aw):
            return

        filename = aw.getFileName()
        if aw.hasSelectedText():
            line, index, line1, index1 = aw.getSelection()
            offset = self.__getOffset(aw, line, index)
        else:
            offset = None

        dlg = ConfirmationDialog(
            self,
            title,
            "Imports",
            "CalculateImportsChanges",
            {
                "MethodName": methodName,
                "FileName": filename,
                "Offset": offset,
            },
            parent=self.__ui,
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Various refactorings
    #####################################################

    def __restructure(self):
        """
        Private slot to restructure code.
        """
        from .RestructureDialog import RestructureDialog

        title = self.tr("Restructure")
        dlg = RestructureDialog(self, title, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __changeSignature(self):
        """
        Private slot to change the signature of a method or function.
        """
        from .ChangeSignatureDialog import ChangeSignatureDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Change Method Signature")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("Highlight the method or function to change and try again."),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = ChangeSignatureDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __inlineArgumentDefault(self):
        """
        Private slot to inline the default value of a parameter of a
        method or function.
        """
        from .InlineArgumentDefaultDialog import InlineArgumentDefaultDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Inline Argument Default")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr(
                    "Highlight the method or function to inline"
                    " a parameter's default and try again."
                ),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = InlineArgumentDefaultDialog(
            self, title, filename, offset, parent=self.__ui
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __transformModuleToPackage(self):
        """
        Private slot to transform a module to a package.
        """
        from .ConfirmationDialog import ConfirmationDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Transform Module to Package")

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()

        dlg = ConfirmationDialog(
            self,
            title,
            "ModuleToPackage",
            "CalculateModuleToPackageChanges",
            {
                "FileName": filename,
            },
            parent=self.__ui,
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __encapsulateAttribute(self):
        """
        Private slot to encapsulate an attribute.
        """
        from .GetterSetterDialog import GetterSetterDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Encapsulate Attribute")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("Highlight the attribute to encapsulate and try again."),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = GetterSetterDialog(self, title, filename, offset, parent=self.__ui)
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __convertLocalToAttribute(self):
        """
        Private slot to convert a local variable to an attribute.
        """
        from .ConfirmationDialog import ConfirmationDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Local Variable to Attribute")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr(
                    "Highlight the local variable to make an attribute"
                    " and try again."
                ),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = ConfirmationDialog(
            self,
            title,
            "LocalToAttribute",
            "CalculateLocalToAttributeChanges",
            {
                "FileName": filename,
                "Offset": offset,
            },
            parent=self.__ui,
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    def __methodToMethodObject(self):
        """
        Private slot to change the signature of a method or function.
        """
        from .MethodToMethodObjectDialog import MethodToMethodObjectDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Replace Method With Method Object")
        if not aw.hasSelectedText():
            # no selection available
            EricMessageBox.warning(
                self.__ui,
                title,
                self.tr("Highlight the method or function to convert and try again."),
            )
            return

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index, line1, index1 = aw.getSelection()
        offset = self.__getOffset(aw, line, index)

        dlg = MethodToMethodObjectDialog(
            self, title, filename, offset, parent=self.__ui
        )
        changeGroup = dlg.getChangeGroupName()
        self.__refactoringDialogs[changeGroup] = dlg
        dlg.finished.connect(lambda: self.__refactoringDialogClosed(changeGroup))
        dlg.show()

    #####################################################
    ## Refactoring History
    #####################################################

    def __showProjectHistory(self):
        """
        Private method to show the project refactoring history.
        """
        from .HistoryDialog import HistoryDialog

        if self.__historyDialog is not None:
            self.__historyDialog.close()

        self.__historyDialog = HistoryDialog(self, parent=self.__ui)
        self.__historyDialog.finished.connect(self.__historyDialogClosed)
        self.__historyDialog.show()

    def __showFileHistory(self):
        """
        Private method to show the refactoring history of the current file.
        """
        from .HistoryDialog import HistoryDialog

        aw = self.__vm.activeWindow()

        if aw is None:
            return

        if self.__historyDialog is not None:
            self.__historyDialog.close()

        filename = aw.getFileName()
        if filename:
            self.__historyDialog = HistoryDialog(
                self, filename=filename, parent=self.__ui
            )
            self.__historyDialog.show()

    def __clearHistory(self):
        """
        Private slot to clear the redo and undo lists.
        """
        res = EricMessageBox.yesNo(
            None,
            self.tr("Clear History"),
            self.tr("Do you really want to clear the refactoring history?"),
        )
        if res:
            self.sendJson(
                "History",
                {
                    "Subcommand": "Clear",
                },
            )

            if self.__historyDialog is not None:
                self.__historyDialog.historyCleared()

    def __processHistoryResult(self, result):
        """
        Private method to process the history data sent by the refactoring
        client.

        @param result dictionary containing the history data
        @type dict
        """
        if self.handleRopeError(result) and self.__historyDialog is not None:
            self.__historyDialog.processHistoryCommand(result)

    def __historyDialogClosed(self):
        """
        Private slot handling the closing of the history dialog.
        """
        self.__historyDialog = None

    #####################################################
    ## Find actions including mouse click handler
    #####################################################

    def __queryReferences(self):
        """
        Private slot to handle the Find References action.
        """
        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Find Occurrences")

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index = aw.getCursorPosition()
        offset = self.__getOffset(aw, line, index)

        self.sendJson(
            "QueryReferences",
            {
                "Title": title,
                "FileName": filename,
                "Offset": offset,
            },
        )

    def __queryReferencesResult(self, result):
        """
        Private method to handle the "Query References" result sent by
        the client.

        @param result dictionary containing the result data
        @type dict
        """
        from .MatchesDialog import MatchesDialog

        if self.handleRopeError(result):
            title = result["Title"]
            if result["EntriesCount"] > 0:
                self.dlg = MatchesDialog(self.__ui, True)
                self.dlg.show()
                for occurrence in result["Entries"]:
                    self.dlg.addEntry(
                        # file name, lineno, unsure
                        occurrence[0],
                        occurrence[1],
                        occurrence[2],
                    )
            else:
                EricMessageBox.warning(
                    self.__ui, title, self.tr("No occurrences found.")
                )

    def __queryDefinition(self):
        """
        Private slot to handle the Find Definition action.
        """
        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Find Definition")

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index = aw.getCursorPosition()
        offset = self.__getOffset(aw, line, index)

        self.sendJson(
            "QueryDefinition",
            {
                "Title": title,
                "FileName": filename,
                "Offset": offset,
                "Source": aw.text(),
            },
        )

    def __queryDefinitionResult(self, result):
        """
        Private method to handle the "Query Definition" result sent by
        the client.

        @param result dictionary containing the result data
        @type dict
        """
        from .MatchesDialog import MatchesDialog

        if self.handleRopeError(result):
            title = result["Title"]
            if "Location" in result:
                location = result["Location"]

                self.dlg = MatchesDialog(self.__ui, False)
                self.dlg.show()
                self.dlg.addEntry(location[0], location[1])
                # file name, lineno
            else:
                EricMessageBox.warning(
                    self.__ui, title, self.tr("No matching definition found.")
                )

    def __queryImplementations(self):
        """
        Private slot to handle the Find Implementations action.
        """
        aw = self.__vm.activeWindow()

        if aw is None:
            return

        title = self.tr("Find Implementations")

        if not self.confirmAllBuffersSaved():
            return

        filename = aw.getFileName()
        line, index = aw.getCursorPosition()
        offset = self.__getOffset(aw, line, index)

        self.sendJson(
            "QueryImplementations",
            {
                "Title": title,
                "FileName": filename,
                "Offset": offset,
            },
        )

    def __queryImplementationsResult(self, result):
        """
        Private method to handle the "Query Implementations" result sent by
        the client.

        @param result dictionary containing the result data
        @type dict
        """
        from .MatchesDialog import MatchesDialog

        if self.handleRopeError(result):
            title = result["Title"]
            if result["EntriesCount"] > 0:
                self.dlg = MatchesDialog(self.__ui, True)
                self.dlg.show()
                for occurrence in result["Entries"]:
                    self.dlg.addEntry(
                        # file name, lineno, unsure
                        occurrence[0],
                        occurrence[1],
                        occurrence[2],
                    )
            else:
                EricMessageBox.warning(
                    self.__ui, title, self.tr("No implementations found.")
                )

    #####################################################
    ## Various actions
    #####################################################

    def __editConfig(self):
        """
        Private slot to open the rope configuration file in an editor.
        """
        from eric7.QScintilla.MiniEditor import MiniEditor

        ropedir = self.__ropeConfig["RopeFolderName"]
        configfile = ""
        if ropedir and os.path.exists(ropedir):
            configfile = os.path.join(ropedir, "config.py")
            if os.path.exists(configfile):
                self.__editor = MiniEditor(configfile)
                self.__editor.show()
                self.__editor.editorSaved.connect(self.__configChanged)
            else:
                EricMessageBox.critical(
                    self.__ui,
                    self.tr("Configure Rope"),
                    self.tr(
                        """The Rope configuration file '{0}' does not exist."""
                    ).format(configfile),
                )
        else:
            EricMessageBox.critical(
                self.__ui,
                self.tr("Configure Rope"),
                self.tr("""The Rope admin directory does not exist."""),
            )

    def __updateConfig(self):
        """
        Private slot to update the configuration file.
        """
        res = EricMessageBox.yesNo(
            self.__ui,
            self.tr("Update Configuration"),
            self.tr(
                """Shall rope's current configuration be replaced """
                """by a new default configuration?"""
            ),
        )
        if res:
            src = self.__defaultConfig()
            cname = self.__ropeConfigFile(writing=True)
            if src != "" and cname is not None:
                try:
                    with open(cname, "w") as f:
                        f.write(src)
                    self.__configChanged()
                    self.__editConfig()
                except OSError as err:
                    EricMessageBox.critical(
                        None,
                        self.tr("Update Configuration"),
                        self.tr(
                            """<p>The configuration could not be"""
                            """ updated.</p><p>Reason: {0}</p>"""
                        ).format(str(err)),
                    )

    def __showRopeHelp(self):
        """
        Private slot to show help about the refactorings offered by Rope.
        """
        from .HelpDialog import HelpDialog

        if self.__helpDialog is None:
            self.__helpDialog = HelpDialog(
                self.tr("Help about rope refactorings"),
                self.__ropeConfig["RopeHelpFile"],
            )
        self.__helpDialog.show()

    def __performSOA(self):
        """
        Private slot to perform SOA on all modules.
        """
        title = self.tr("Analyse all modules")
        res = EricMessageBox.yesNo(
            self.__ui,
            title,
            self.tr(
                """This action might take some time. """
                """Do you really want to perform SOA?"""
            ),
        )
        if res:
            self.sendJson(
                "PerformSoa",
                {
                    "Title": title,
                },
            )

    def __soaFinished(self, result):
        """
        Private method to handle the "Soa Finished" result sent by
        the client.

        @param result dictionary containing the result data
        @type dict
        """
        if self.handleRopeError(result):
            title = result["Title"]

            EricMessageBox.information(
                self.__ui,
                title,
                self.tr(
                    """Static object analysis (SOA) done. """
                    """SOA database updated."""
                ),
            )

    ##################################################################
    ## methods below are private utility methods
    ##################################################################

    def __processProgress(self, params):
        """
        Private method to handle Progress commands.

        @param params dictionary containing the progress data
        @type dict
        """
        subcommand = params["Subcommand"]
        if subcommand == "Init":
            if self.__progressDialog is not None:
                self.__progressDialog.reset()

            progressDialog = RopeProgressDialog(
                self, params["Title"], params["Interruptable"]
            )
            progressDialog.show()
            progressDialog.raise_()
            self.__progressDialog = progressDialog
            QApplication.processEvents()

        elif subcommand == "Progress" and self.__progressDialog is not None:
            self.__progressDialog.updateProgress(params)
            self.__progressDialog.raise_()

        elif subcommand == "Reset" and self.__progressDialog is not None:
            self.__progressDialog.reset()

    def __setConfig(self, params):
        """
        Private method to set the rope client configuration data.

        @param params dictionary containing the configuration data
        @type dict
        """
        self.__ropeConfig = params
        # keys: RopeFolderName, DefaultConfig, RopeHelpFile,
        #       RopeInfo, RopeVersion, RopeCopyright, PythonVersion

    def __ropeConfigFile(self, writing=False):
        """
        Private method to get the name of the rope configuration file.

        @param writing flag indicating to get the name for writing (defaults to False)
        @type bool (optional)
        @return name of the rope configuration file
        @rtype str
        """
        configfile = None
        if self.__ropeConfig:
            ropedir = self.__ropeConfig["RopeFolderName"]
            if ropedir:
                configfile = os.path.join(ropedir, "config.py")
                if not writing and not os.path.exists(configfile):
                    configfile = None
        return configfile

    def __configChanged(self):
        """
        Private slot called, when the rope config file has changed.
        """
        self.sendJson("ConfigChanged", {})

    def __defaultConfig(self):
        """
        Private slot to return the contents of rope's default configuration.

        @return string containing the source of rope's default
            configuration
        @rtype str
        """
        if self.__ropeConfig and "DefaultConfig" in self.__ropeConfig:
            return self.__ropeConfig["DefaultConfig"]
        else:
            return ""

    ##################################################################
    ## methods below are public utility methods
    ##################################################################

    def getActions(self):
        """
        Public method to get a list of all actions.

        @return list of all actions
        @rtype list of EricAction
        """
        return self.actions[:]

    def projectOpened(self):
        """
        Public slot to handle the projectOpened signal.
        """
        if self.__projectopen:
            self.projectClosed()

        if isRemoteFileName(self.__ericProject.getProjectFile()):
            # refactoring for eric-ide server projects not supported
            return

        self.__projectopen = True
        self.__projectpath = self.__ericProject.getProjectPath()
        self.__projectLanguage = self.__ericProject.getProjectLanguage()

        ok = False

        if self.__projectLanguage in ("Python3", "MicroPython", "Cython"):
            clientEnv = os.environ.copy()
            if "PATH" in clientEnv:
                clientEnv["PATH"] = self.__ui.getOriginalPathString()

            venvManager = ericApp().getObject("VirtualEnvManager")

            # get virtual environment from project first
            venvName = self.__ericProject.getDebugProperty("VIRTUALENV")
            if venvName:
                try:
                    isRemote = venvManager.isRemoteEnvironment(venvName)
                except AttributeError:
                    isRemote = False
            else:
                isRemote = False
            if (not venvName) or isRemote:
                # get it from debugger settings next
                if self.__projectLanguage in ("Python3", "MicroPython", "Cython"):
                    venvName = Preferences.getDebugger("Python3VirtualEnv")
                    if not venvName:
                        venvName, _ = venvManager.getDefaultEnvironment()
                else:
                    venvName = ""
            if venvName:
                interpreter = venvManager.getVirtualenvInterpreter(venvName)
                if not interpreter:
                    interpreter = getPythonExecutable()
                execPath = venvManager.getVirtualenvExecPath(venvName)

                # build a suitable environment
                if execPath:
                    if "PATH" in clientEnv:
                        clientEnv["PATH"] = os.pathsep.join(
                            [execPath, clientEnv["PATH"]]
                        )
                    else:
                        clientEnv["PATH"] = execPath
            else:
                interpreter = getPythonExecutable()
            if interpreter:
                if isRemote:
                    self.__ui.appendToStderr(
                        self.tr(
                            "The project is configured for remote access."
                            " Using local interpreter instead."
                        )
                    )
                ok = self.__startRefactoringClient(interpreter, clientEnv)
                if not ok:
                    self.__ui.appendToStderr(
                        self.tr(
                            "Project language '{0}' is not supported because"
                            " the configured interpreter could not be started."
                            " Refactoring is disabled."
                        ).format(self.__projectLanguage)
                    )
                else:
                    for act in self.actions:
                        act.setEnabled(True)
            else:
                self.__ui.appendToStderr(
                    self.tr(
                        "Project language '{0}' is not supported because no"
                        " suitable interpreter is configured. Refactoring is"
                        " disabled."
                    ).format(self.__projectLanguage)
                )
        else:
            self.__ui.appendToStderr(
                self.tr(
                    "Refactoring for project language '{0}' is not supported."
                ).format(self.__projectLanguage)
            )

        self.__mainMenu.menuAction().setEnabled(ok)

    def projectClosed(self):
        """
        Public slot to handle the projectClosed signal.
        """
        for act in self.actions:
            act.setEnabled(False)
        self.__mainMenu.menuAction().setEnabled(False)

        if self.__helpDialog is not None:
            self.__helpDialog.close()
            self.__helpDialog = None
        if self.__historyDialog is not None:
            self.__historyDialog.close()
            self.__historyDialog = None
        for dlg in self.__refactoringDialogs.values():
            dlg.close()
        self.__refactoringDialogs = {}

        self.sendJson("CloseProject", {}, flush=True)

        self.__projectopen = False
        self.__projectpath = ""
        self.__projectLanguage = ""
        self.__ropeConfig = {}

        self.stopClient()

    def confirmBufferIsSaved(self, editor):
        """
        Public method to check, if an editor has unsaved changes.

        @param editor reference to the editor to be checked
        @type Editor
        @return flag indicating, that the editor doesn't contain
            unsaved edits
        @rtype bool
        """
        res = editor.checkDirty()
        self.sendJson("Validate", {})
        return res

    def confirmAllBuffersSaved(self):
        """
        Public method to check, if any editor has unsaved changes.

        @return flag indicating, that no editor contains unsaved edits
        @rtype bool
        """
        res = self.__vm.checkAllDirty()
        self.sendJson("Validate", {})
        return res

    def refreshEditors(self, changedFiles):
        """
        Public method to refresh modified editors.

        @param changedFiles list of changed files
        @type list of str
        """
        openFiles = [normcasepath(f) for f in self.__vm.getOpenFilenames()]

        for fileName in changedFiles:
            normfile = normcasepath(fileName)
            if normfile in openFiles:
                editor = self.__vm.getEditor(normfile)[1]
                editor.refresh()

        aw = self.__vm.activeWindow()
        if aw is not None:
            filename = aw.getFileName()
            if filename is not None:
                self.__vm.openSourceFile(filename, aw.getCursorPosition()[0] + 1)

    def reportChanged(self, filename, oldSource):
        """
        Public slot to report some changed sources.

        @param filename file name of the changed source
        @type str
        @param oldSource source code before the change
        @type str
        """
        if self.__ericProject.isOpen() and self.__ericProject.isProjectFile(filename):
            editor = self.__vm.getOpenEditor(filename)
            if (
                self.__ropeConfig
                and editor is not None
                and editor.getLanguage() == self.__ropeConfig["PythonVersion"]
            ):
                self.sendJson(
                    "ReportChanged",
                    {
                        "FileName": filename,
                        "OldSource": oldSource,
                    },
                )

    #######################################################################
    ## Methods below handle the network connection
    #######################################################################

    def handleCall(self, method, params):
        """
        Public method to handle a method call from the client.

        @param method requested method name
        @type str
        @param params dictionary with method specific parameters
        @type dict
        """
        self.__methodMapping[method](params)

    def __processClientException(self, params):
        """
        Private method to handle exceptions of the refactoring client.

        @param params dictionary containing the exception data
        @type dict
        """
        if params["ExceptionType"] == "ProtocolError":
            EricMessageBox.critical(
                None,
                self.tr("Refactoring Protocol Error"),
                self.tr(
                    """<p>The data received from the refactoring"""
                    """ server could not be decoded. Please report"""
                    """ this issue with the received data to the"""
                    """ eric bugs email address.</p>"""
                    """<p>Error: {0}</p>"""
                    """<p>Data:<br/>{1}</p>"""
                ).format(
                    params["ExceptionValue"],
                    Utilities.html_encode(params["ProtocolData"]),
                ),
                EricMessageBox.Ok,
            )
        else:
            EricMessageBox.critical(
                None,
                self.tr("Refactoring Client Error"),
                self.tr(
                    "<p>An exception happened in the refactoring"
                    " client. Please report it to the eric bugs"
                    " email address.</p>"
                    "<p>Exception: {0}</p>"
                    "<p>Value: {1}</p>"
                    "<p>Traceback: {2}</p>"
                ).format(
                    Utilities.html_encode(params["ExceptionType"]),
                    params["ExceptionValue"],
                    params["Traceback"]
                    .replace("\r\n", "<br/>")
                    .replace("\n", "<br/>")
                    .replace("\r", "<br/>"),
                ),
                EricMessageBox.Ok,
            )

    def __startRefactoringClient(self, interpreter, clientEnv):
        """
        Private method to start the refactoring client.

        @param interpreter interpreter to be used for the refactoring client
        @type str
        @param clientEnv dictionary with environment variables to run the
            interpreter with
        @type dict
        @return flag indicating a successful client start
        @rtype bool
        """
        if interpreter:
            client = os.path.join(os.path.dirname(__file__), "RefactoringClient.py")
            ok, exitCode = self.startClient(
                interpreter,
                client,
                [self.__projectpath, getPythonLibraryDirectory()],
                environment=clientEnv,
            )
            if not ok and exitCode == 42:
                self.__ui.appendToStderr(
                    "RefactoringServer: "
                    + self.tr("The rope refactoring library is not installed.\n")
                )
        else:
            ok = False
        return ok

eric ide

mercurial