예제 #1
0
def main(config_file=None):
    """ """

    if not config_file:
        print "You need to provide a valid config file"
        sys.exit(1)

    config = Config(config_file)
    print "Tying the rope..."
    myproject = Project(config.buildout_dir+'/src', python_path=sys.path)
    handle.add_observer(update_progress)

    # analyze_modules(myproject, task_handle=handle)


    print "Done."

    print "Going to hurricane:" + PATH

    os.chdir(PATH)


    from thread import start_new_thread
    start_new_thread(temp_tornado_thread, ())

    httpd = PloneIDEServer(config, PloneIDEHandler, myproject, handle)

    try:
        httpd.run()
    except KeyboardInterrupt:
        print "Closing the project..."
        myproject.close()
        httpd.stop()
예제 #2
0
파일: rope.py 프로젝트: smiled0g/vy
    def static_analysis(self, event):
        path    = self.get_root_path()
        project = Project(path)
        mod     = path_to_resource(project, self.area.filename)

        libutils.analyze_module(project, mod)
        project.close()
예제 #3
0
class RopeComplete(object):
	def __init__(self, project_path, source_code, resource_path, code_point):
		self.project = Project(project_path)
		self.project.pycore._init_python_files()
		
		self.resource = self.project.get_resource(resource_path)
		self.source_code = source_code
		self.code_point = code_point

	def __del__(self):
		self.project.close()

	def get_proposals(self):
		ret = []
		proposals = codeassist.code_assist(self.project, self.source_code, self.code_point, resource=self.resource, maxfixes=10)
		proposals = codeassist.sorted_proposals(proposals)

		if V(ROPE_VERSION) <= V('0.9.2'):
			for proposal in proposals:
				ret.append(new_completion_item(name=proposal.name, scope=proposal.kind, type=proposal.type))
		else:
			for proposal in proposals:
				ret.append(new_completion_item(name=proposal.name, scope=proposal.scope, type=proposal.type))

		return ret

	def get_calltip(self):
		calltip = codeassist.get_doc(self.project, self.source_code, self.code_point, resource=self.resource, maxfixes=10)
		return calltip
예제 #4
0
def _generate_wheel_file(
    pipeline_name: str,
    destination: Path,
    source_paths: Tuple[_SourcePathType, ...],
    version: str,
    metadata: ProjectMetadata,
    alias: str = None,
) -> None:
    package_name = alias or pipeline_name
    package_source, tests_source, conf_source = source_paths

    with tempfile.TemporaryDirectory() as temp_dir:
        temp_dir_path = Path(temp_dir).resolve()

        project = Project(temp_dir_path)  # project where to do refactoring
        _refactor_code_for_package(  # type: ignore
            project, package_source, tests_source, alias, metadata
        )
        project.close()

        # Copy & "refactor" config
        _, _, conf_target = _get_package_artifacts(temp_dir_path, package_name)
        _sync_path_list(conf_source, conf_target)  # type: ignore
        if conf_target.is_dir() and alias:
            _rename_files(conf_target, pipeline_name, alias)

        # Build a setup.py on the fly
        try:
            install_requires = _make_install_requires(
                package_source / "requirements.txt"  # type: ignore
            )
        except Exception as exc:
            click.secho("FAILED", fg="red")
            cls = exc.__class__
            raise KedroCliError(f"{cls.__module__}.{cls.__qualname__}: {exc}") from exc

        config_files = [str(file) for file in conf_target.rglob("*") if file.is_file()]
        setup_file = _generate_setup_file(
            package_name, version, install_requires, temp_dir_path, config_files
        )

        package_file = destination / _get_wheel_name(name=package_name, version=version)
        if package_file.is_file():
            click.secho(
                f"Package file {package_file} will be overwritten!", fg="yellow"
            )

        # python setup.py bdist_wheel --dist-dir <destination>
        call(
            [
                sys.executable,
                str(setup_file.resolve()),
                "bdist_wheel",
                "--dist-dir",
                str(destination),
            ],
            cwd=temp_dir,
        )
예제 #5
0
파일: rename.py 프로젝트: teneen/blackmamba
def main():
    from rope.base.project import Project
    from rope.base import libutils
    from rope.refactor.rename import Rename
    from rope.base.exceptions import RopeError

    path = editor.get_path()
    selection = editor.get_selection()

    if not path or not selection:
        console.hud_alert('Not a Python file', 'error')
        return

    tab.save()

    project = None
    try:
        project = Project(os.path.dirname(path), ropefolder=None)
        resource = libutils.path_to_resource(project, path)
        if not libutils.is_python_file(project, resource):
            console.hud_alert('Not a Python file', 'error')
            return

        renamer = Rename(project, resource, selection[0])
        old_name = renamer.get_old_name()

        if not old_name:
            console.hud_alert('Unable to get identifier name', 'error')
            return

        new_name = _ask_for_new_name(old_name)

        change_set = renamer.get_changes(new_name,
                                         docs=True,
                                         resources=[resource])
        if not change_set:
            console.hud_alert('No changes required')
            return

        if refactoring.ask_if_apply_change_set(change_set):
            refactoring.apply_change_set(change_set, path, selection)
            console.hud_alert('Identifier renamed')

    except RopeError as e:
        console.hud_alert(str(e), 'error')

    except KeyboardInterrupt:
        pass

    finally:
        if project:
            project.close()
예제 #6
0
파일: rope.py 프로젝트: smiled0g/vy
    def rename(self, name):
        tmp0    = self.area.get('1.0', 'insert')
        offset  = len(tmp0)
        path    = self.get_root_path()
        project = Project(path)
        mod     = path_to_resource(project, self.area.filename)
        renamer = Rename(project, mod, offset)
        changes = renamer.get_changes(name)
        project.do(changes)

        self.update_instances(changes)

        print('\nRope - Renamed resource ..\n')
        print(changes.get_description())
        self.area.chmode('NORMAL')
        root.status.set_msg('Resources renamed!')
        project.close()
예제 #7
0
def main():
    from rope.base.project import Project
    from rope.base import libutils
    from rope.refactor.importutils import ImportOrganizer
    from rope.base.exceptions import RopeError

    path = editor.get_path()
    selection = editor.get_selection()

    if not path or not selection:
        console.hud_alert('Not a Python file', 'error')
        return

    tab.save()

    project = None
    try:
        project = Project(os.path.dirname(path), ropefolder=None)
        resource = libutils.path_to_resource(project, path)
        if not libutils.is_python_file(project, resource):
            console.hud_alert('Not a Python file', 'error')
            return

        organizer = ImportOrganizer(project)
        change_set = organizer.organize_imports(resource)

        if not change_set:
            console.hud_alert('No changes required')
            return

        if refactoring.ask_if_apply_change_set(change_set):
            refactoring.apply_change_set(change_set, path, selection)
            console.hud_alert('Imports organized')

    except RopeError as e:
        console.hud_alert(str(e), 'error')

    except KeyboardInterrupt:
        pass

    finally:
        if project:
            project.close()
예제 #8
0
class RopeComplete(object):
    def __init__(self, project_path, source_code, resource_path, code_point):
        self.project = Project(project_path)
        self.project.pycore._init_python_files()

        self.resource = self.project.get_resource(resource_path)
        self.source_code = source_code
        self.code_point = code_point

    def __del__(self):
        self.project.close()

    def get_proposals(self):
        ret = []
        proposals = codeassist.code_assist(self.project,
                                           self.source_code,
                                           self.code_point,
                                           resource=self.resource,
                                           maxfixes=10)
        proposals = codeassist.sorted_proposals(proposals)

        if V(ROPE_VERSION) <= V('0.9.2'):
            for proposal in proposals:
                ret.append(
                    new_completion_item(name=proposal.name,
                                        scope=proposal.kind,
                                        type=proposal.type))
        else:
            for proposal in proposals:
                ret.append(
                    new_completion_item(name=proposal.name,
                                        scope=proposal.scope,
                                        type=proposal.type))

        return ret

    def get_calltip(self):
        calltip = codeassist.get_doc(self.project,
                                     self.source_code,
                                     self.code_point,
                                     resource=self.resource,
                                     maxfixes=10)
        return calltip
예제 #9
0
def _install_files(
    project_metadata: ProjectMetadata,
    package_name: str,
    source_path: Path,
    env: str = None,
    alias: str = None,
):
    env = env or "base"

    package_source, test_source, conf_source = _get_package_artifacts(
        source_path, package_name
    )

    if conf_source.is_dir() and alias:
        _rename_files(conf_source, package_name, alias)

    pipeline_name = alias or package_name
    package_dest, test_dest, conf_dest = _get_pipeline_artifacts(
        project_metadata, pipeline_name=pipeline_name, env=env
    )

    if conf_source.is_dir():
        _sync_dirs(conf_source, conf_dest)
        # `config` dir was packaged under `package_name` directory with
        # `kedro pipeline package`. Since `config` was already synced,
        # we don't want to copy it again when syncing the package, so we remove it.
        shutil.rmtree(str(conf_source))

    project = Project(source_path)
    refactored_package_source, refactored_test_source = _refactor_code_for_unpacking(
        project, package_source, test_source, alias, project_metadata
    )
    project.close()

    if refactored_test_source.is_dir():
        _sync_dirs(refactored_test_source, test_dest)

    # Sync everything under package directory, except `config`
    # since it has already been copied.
    if refactored_package_source.is_dir():
        _sync_dirs(refactored_package_source, package_dest)
예제 #10
0
파일: rope.py 프로젝트: smiled0g/vy
    def move(self, name):
        """
        """
        tmp0    = self.area.get('1.0', 'insert')
        offset  = len(tmp0)

        path    = self.get_root_path()
        project = Project(path)

        project = Project(path)
        mod     = path_to_resource(project, self.area.filename)
        mover   = create_move(project, mod, offset)
        destin  = path_to_resource(project, name)
        changes = mover.get_changes(destin)
        project.do(changes)

        self.update_instances(changes)
        project.close()

        self.area.chmode('NORMAL')
        root.status.set_msg('Resources moved!')
예제 #11
0
def compile_with_top(source_path, api_path):
    # parse the inputs to get the path subcomponents
    source_path = op.abspath(source_path)
    api_path = op.abspath(api_path)
    api_root, api_name = op.split(api_path)
    api_module = op.basename(op.normpath(api_root))
    source_root, source_name = op.split(source_path)

    # Create the two projects that we will be using
    from rope.base.project import Project
    project_const = Project(source_root, **prefs)  # original project, used as a source and will not be modified
    project_swap = Project(op.join(source_root, 'top_compiled'), **prefs)

    if(op.isdir(source_path)):
        pass

    # copy over the files that will be compiled
    source_mod = project_const.root.get_child(source_name)
    try:
        mod_code = project_swap.root.get_child(source_name)
    except ResourceNotFoundError:
        mod_code = project_swap.root.create_file(source_name)

    mod_code.write(source_mod.read())

    # Generate the api library package and copy it over
    from rope.contrib import generate
    api_pkg = None
    if api_root != source_root: # If the api is in its own module, reproduce it
        try:
            api_pkg = project_swap.root.get_child(api_module)
        except ResourceNotFoundError:
            api_pkg = generate.create_package(project_swap, api_module)
    try:
        mod_api = project_swap.root.get_child('{}/{}'.format(api_module, api_name))
    except ResourceNotFoundError:
        mod_api = generate.create_module(project_swap, 'api', api_pkg)
    # copy over the contents of the api so that rope can modify it
    with open(api_path, 'r') as a:
        api_content = a.read()

    # mod_api.truncate().write(api_content)
    mod_api.write(api_content)

    # inline each API call
    # first, get the list of all API function calls possible
    import imp, types
    api = imp.load_source(api_name, api_path)
    api_calls = [a for a in dir(api) if isinstance(api.__dict__.get(a), types.FunctionType)]
    # perform the replacement
    for a in api_calls:
        # We did not use this API call in the code, so skip replacing it
        if re.search(r'\b{}\b'.format(a), mod_code.read()) is None:
            continue
        # print re.search(r'\b{}\b'.format(a), mod_code.read())
        ind = re.search(r'\b{}\b'.format(a), mod_code.read()).start()
        try:
            inl = inline.create_inline(project_swap, mod_code, ind)
            change = inl.get_changes()
            project_const.do(change)
        except RefactoringError:
            print "The api cannot be properly connected from the code. Please ensure that you are importing the file with the API commands directly."

    # Cleaning up
    api_pkg.remove() # remove the API library from the compiled files
    project_const.close()
    project_swap.close()
예제 #12
0
파일: Refactor.py 프로젝트: takluyver/Pcode
class Refactor(QtGui.QWidget):

    def __init__(self, editorTabWidget, busyWidget, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.editorTabWidget = editorTabWidget
        self.busyWidget = busyWidget
        self.root = editorTabWidget.pathDict["sourcedir"]
        ropeFolder = editorTabWidget.pathDict["ropeFolder"]

        prefs = {
            'ignored_resources': ['*.pyc', '*~', '.ropeproject',
                                  '.hg', '.svn', '_svn', '.git',
                                  '__pycache__'],
            'python_files': ['*.py'],
            'save_objectdb': True,
            'compress_objectdb': False,
            'automatic_soa': True,
            'soa_followed_calls': 0,
            'perform_doa': True,
            'validate_objectdb': True,
            'max_history_items': 32,
            'save_history': True,
            'compress_history': False,
            'indent_size': 4,
            'extension_modules': [
                "PyQt4", "PyQt4.QtGui", "QtGui", "PyQt4.QtCore", "QtCore",
                "PyQt4.QtScript", "QtScript", "os.path", "numpy", "scipy", "PIL",
                "OpenGL", "array", "audioop", "binascii", "cPickle", "cStringIO",
                "cmath", "collections", "datetime", "errno", "exceptions", "gc",
                "imageop", "imp", "itertools", "marshal", "math", "mmap", "msvcrt",
                "nt", "operator", "os", "parser", "rgbimg", "signal", "strop", "sys",
                "thread", "time", "wx", "wxPython", "xxsubtype", "zipimport", "zlib"
            ],
            'import_dynload_stdmods': True,
            'ignore_syntax_errors': True,
            'ignore_bad_imports': True
        }

        self.ropeProject = Project(
            projectroot=self.root, ropefolder=ropeFolder, **prefs)
        self.ropeProject.prefs.add('python_path', 'c:/Python33')
        self.ropeProject.prefs.add('source_folders', 'c:/Python33/Lib')
        self.ropeProject.validate()

        self.noProject = Project(projectroot="temp", ropefolder=None)

        self.findThread = FindUsageThread()
        self.findThread.finished.connect(self.findOccurrencesFinished)

        self.renameThread = RenameThread()
        self.renameThread.finished.connect(self.renameFinished)

        self.inlineThread = InlineThread()
        self.inlineThread.finished.connect(self.inlineFinished)

        self.localToFieldThread = LocalToFieldThread()
        self.localToFieldThread.finished.connect(self.localToFieldFinished)

        self.moduleToPackageThread = ModuleToPackageThread()
        self.moduleToPackageThread.finished.connect(
            self.moduleToPackageFinished)

        self.createActions()

        self.refactorMenu = QtGui.QMenu("Refactor")
        self.refactorMenu.addAction(self.renameAttributeAct)
        self.refactorMenu.addAction(self.inlineAct)
        self.refactorMenu.addAction(self.localToFieldAct)

    def close(self):
        self.ropeProject.close()

    def createActions(self):
        self.findDefAct = \
            QtGui.QAction(
                QtGui.QIcon(os.path.join("Resources", "images", "map_marker")),
                "Go-to Definition", self, statusTip="Go-to Definition",
                triggered=self.findDefinition)

        self.findOccurrencesAct = \
            QtGui.QAction("Usages", self, statusTip="Usages",
                          triggered=self.findOccurrences)

        self.moduleToPackageAct = \
            QtGui.QAction(
                "Convert to Package", self, statusTip="Convert to Package",
                triggered=self.moduleToPackage)

        self.renameModuleAct = \
            QtGui.QAction("Rename", self, statusTip="Rename",
                          triggered=self.renameModule)

        self.renameAttributeAct = \
            QtGui.QAction("Rename", self, statusTip="Rename",
                          triggered=self.renameAttribute)

        self.inlineAct = \
            QtGui.QAction("Inline", self, statusTip="Inline",
                          triggered=self.inline)

        self.localToFieldAct = \
            QtGui.QAction("Local-to-Field", self, statusTip="Local-to-Field",
                          triggered=self.localToField)

        self.getDocAct = \
            QtGui.QAction("Documentation", self, statusTip="Documentation",
                          triggered=self.getDoc)

        self.getCallTipAct = \
            QtGui.QAction("CallTip", self, statusTip="CallTip",
                          triggered=self.getCallTip)

    def renameModule(self):
        index = self.editorTabWidget.currentIndex()
        moduleName = self.editorTabWidget.tabText(index)
        moduleName = os.path.splitext(moduleName)[0]
        newName = GetName("Rename", moduleName, self)
        project = self.getProject()
        if newName.accepted:
            saved = self.editorTabWidget.saveProject()
            if saved:
                path = self.editorTabWidget.getEditorData("filePath")
                self.renameThread.rename(newName.text, path, project, None)
                self.busyWidget.showBusy(True, "Renaming... please wait!")

    def renameAttribute(self):
        objectName = self.editorTabWidget.get_current_word()
        newName = GetName("Rename", objectName, self)
        if newName.accepted:
            project = self.getProject()
            saved = self.editorTabWidget.saveProject()
            if saved:
                offset = self.getOffset()
                path = self.editorTabWidget.getEditorData("filePath")
                self.renameThread.rename(newName.text, path, project, offset)
                self.busyWidget.showBusy(True, "Renaming... please wait!")

    def renameFinished(self):
        self.busyWidget.showBusy(False)
        if self.renameThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed Rename",
                                                self.renameThread.error)
            return
        if self.renameThread.offset is None:
            # filename has been changed
            oldPath = self.editorTabWidget.getEditorData("filePath")
            ext = os.path.splitext(oldPath)[1]
            newPath = os.path.join(os.path.dirname(oldPath),
                                   self.renameThread.new_name + ext)
            self.editorTabWidget.updateEditorData("filePath", newPath)
        else:
            if len(self.renameThread.changedFiles) > 0:
                self.editorTabWidget.reloadModules(
                    self.renameThread.changedFiles)

    def inline(self):
        offset = self.getOffset()
        path = self.editorTabWidget.getEditorData("filePath")
        project = self.getProject()
        resource = project.get_file(path)
        saved = self.editorTabWidget.saveProject()
        if saved:
            self.inlineThread.inline(project, resource, offset)
            self.busyWidget.showBusy(True, "Inlining... please wait!")

    def inlineFinished(self):
        self.busyWidget.showBusy(False)
        if self.inlineThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed Inline",
                                                self.inlineThread.error)
            return
        if len(self.inlineThread.changedFiles) > 0:
            self.editorTabWidget.reloadModules(self.inlineThread.changedFiles)

    def localToField(self):
        offset = self.getOffset()
        path = self.editorTabWidget.getEditorData("filePath")
        project = self.getProject()
        resource = project.get_file(path)
        saved = self.editorTabWidget.saveProject()
        if saved:
            self.localToFieldThread.convert(project, resource, offset)
            self.busyWidget.showBusy(
                True, "Converting Local to Field... please wait!")

    def localToFieldFinished(self):
        self.busyWidget.showBusy(False)
        if self.localToFieldThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed Local-to-Field",
                                                self.localToFieldThread.error)
            return
        if len(self.localToFieldThread.changedFiles) > 0:
            self.editorTabWidget.reloadModules(
                self.localToFieldThread.changedFiles)

    def findDefinition(self):
        saved = self.editorTabWidget.saveProject()
        if saved:
            offset = self.getOffset()
            path = self.editorTabWidget.getEditorData("filePath")
            project = self.getProject()
            resource = project.get_file(path)
            try:
                result = find_definition(project,
                                         self.editorTabWidget.getSource(), offset, resource)
                if result is None:
                    self.editorTabWidget.showNotification(
                        "No definition found.")
                else:
                    start, end = result.region
                    offset = result.offset
                    line = result.lineno
                    result_path = result.resource.path
                    sourcePath = self.editorTabWidget.pathDict["sourcedir"]
                    if not os.path.isabs(result_path):
                        result_path = os.path.join(sourcePath, result_path)
                    if os.path.samefile(result_path, path):
                        pass
                    else:
                        self.editorTabWidget.loadfile(result_path)
                    editor = self.editorTabWidget.focusedEditor()
                    start = editor.lineIndexFromPosition(start)
                    end = editor.lineIndexFromPosition(end)
                    editor.setSelection(start[0], start[1], end[0], end[1])
                    editor.ensureLineVisible(line - 1)
            except Exception as err:
                self.editorTabWidget.showNotification(str(err))

    def moduleToPackage(self):
        path = self.editorTabWidget.getEditorData("filePath")
        project = self.getProject()
        saved = self.editorTabWidget.saveProject()
        if saved:
            self.moduleToPackageThread.convert(path, project)
            self.busyWidget.showBusy(True, "Converting... please wait!")

    def moduleToPackageFinished(self):
        self.busyWidget.showBusy(False)
        if self.moduleToPackageThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed to convert",
                                                self.moduleToPackageThread.error)

    def findOccurrences(self):
        offset = self.getOffset()
        project = self.getProject()
        saved = self.editorTabWidget.saveProject()
        if saved:
            path = self.editorTabWidget.getEditorData("filePath")
            self.objectName = self.editorTabWidget.get_current_word()
            self.findThread.find(path, project, offset)
            self.busyWidget.showBusy(True, "Finding usages... please wait!")

    def findOccurrencesFinished(self):
        self.busyWidget.showBusy(False)
        if self.findThread.error is not None:
            self.editorTabWidget.showNotification(self.findThread.error)
            return
        if len(self.findThread.itemsDict) > 0:
            foundList = []
            for parent, lines in self.findThread.itemsDict.items():
                parentItem = QtGui.QTreeWidgetItem()
                parentItem.setForeground(0, QtGui.QBrush(
                    QtGui.QColor("#003366")))
                parentItem.setText(0, parent)
                for line in lines:
                    childItem = QtGui.QTreeWidgetItem()
                    childItem.setText(0, str(line))
                    childItem.setFirstColumnSpanned(True)
                    parentItem.addChild(childItem)
                    foundList.append(parentItem)
            usageDialog = UsageDialog(
                self.editorTabWidget, "Usages: " + self.objectName, foundList, self)
        else:
            self.editorTabWidget.showNotification("No usages found.")

    def getOffset(self):
        return self.editorTabWidget.getOffset()

    def get_absolute_coordinates(self):
        editor = self.editorTabWidget.focusedEditor()
        point = editor.get_absolute_coordinates()
        return point

    def getCompletions(self):
        offset = self.getOffset()
        project = self.getProject()
        proposals = codeassist.code_assist(project,
                                           self.editorTabWidget.getSource(), offset)
        proposals = codeassist.sorted_proposals(proposals)
        if len(proposals) > 0:
            cmpl = []
            for i in proposals:
                cmpl.append(str(i))

            return cmpl
        else:
            return []

    def getDoc(self, offset=None):
        if offset is None:
            offset = self.getOffset()
        project = self.getProject()
        try:
            doc = codeassist.get_doc(project,
                                     self.editorTabWidget.getSource(), offset)
            return doc
        except Exception as err:
            return None

    def getCallTip(self, offset=None):
        if offset is None:
            offset = self.getOffset()
        project = self.getProject()
        try:
            calltip = codeassist.get_calltip(project,
                                             self.editorTabWidget.getSource(), offset)

            return calltip
        except Exception as err:
            return None

    def getProject(self):
        path = self.editorTabWidget.getEditorData("filePath")
        if path is None:
            return self.noProject
        if path.startswith(self.editorTabWidget.pathDict["sourcedir"]):
            return self.ropeProject
        else:
            return self.noProject
예제 #13
0
class Refactor(QtGui.QWidget):
    def __init__(self, editorTabWidget, busyWidget, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.editorTabWidget = editorTabWidget
        self.busyWidget = busyWidget
        self.root = editorTabWidget.pathDict["sourcedir"]
        ropeFolder = editorTabWidget.pathDict["ropeFolder"]

        prefs = {
            'ignored_resources': [
                '*.pyc', '*~', '.ropeproject', '.hg', '.svn', '_svn', '.git',
                '__pycache__'
            ],
            'python_files': ['*.py'],
            'save_objectdb':
            True,
            'compress_objectdb':
            False,
            'automatic_soa':
            True,
            'soa_followed_calls':
            0,
            'perform_doa':
            True,
            'validate_objectdb':
            True,
            'max_history_items':
            32,
            'save_history':
            True,
            'compress_history':
            False,
            'indent_size':
            4,
            'extension_modules': [
                "PyQt4", "PyQt4.QtGui", "QtGui", "PyQt4.QtCore", "QtCore",
                "PyQt4.QtScript", "QtScript", "os.path", "numpy", "scipy",
                "PIL", "OpenGL", "array", "audioop", "binascii", "cPickle",
                "cStringIO", "cmath", "collections", "datetime", "errno",
                "exceptions", "gc", "imageop", "imp", "itertools", "marshal",
                "math", "mmap", "msvcrt", "nt", "operator", "os", "parser",
                "rgbimg", "signal", "strop", "sys", "thread", "time", "wx",
                "wxPython", "xxsubtype", "zipimport", "zlib"
            ],
            'import_dynload_stdmods':
            True,
            'ignore_syntax_errors':
            True,
            'ignore_bad_imports':
            True
        }

        self.ropeProject = Project(projectroot=self.root,
                                   ropefolder=ropeFolder,
                                   **prefs)
        self.ropeProject.prefs.add('python_path', 'c:/Python33')
        self.ropeProject.prefs.add('source_folders', 'c:/Python33/Lib')
        self.ropeProject.validate()

        self.noProject = Project(projectroot="temp", ropefolder=None)

        self.findThread = FindUsageThread()
        self.findThread.finished.connect(self.findOccurrencesFinished)

        self.renameThread = RenameThread()
        self.renameThread.finished.connect(self.renameFinished)

        self.inlineThread = InlineThread()
        self.inlineThread.finished.connect(self.inlineFinished)

        self.localToFieldThread = LocalToFieldThread()
        self.localToFieldThread.finished.connect(self.localToFieldFinished)

        self.moduleToPackageThread = ModuleToPackageThread()
        self.moduleToPackageThread.finished.connect(
            self.moduleToPackageFinished)

        self.createActions()

        self.refactorMenu = QtGui.QMenu("Refactor")
        self.refactorMenu.addAction(self.renameAttributeAct)
        self.refactorMenu.addAction(self.inlineAct)
        self.refactorMenu.addAction(self.localToFieldAct)

    def close(self):
        self.ropeProject.close()

    def createActions(self):
        self.findDefAct = \
            QtGui.QAction(
                QtGui.QIcon(os.path.join("Resources", "images", "map_marker")),
                "Go-to Definition", self, statusTip="Go-to Definition",
                triggered=self.findDefinition)

        self.findOccurrencesAct = \
            QtGui.QAction("Usages", self, statusTip="Usages",
                          triggered=self.findOccurrences)

        self.moduleToPackageAct = \
            QtGui.QAction(
                "Convert to Package", self, statusTip="Convert to Package",
                triggered=self.moduleToPackage)

        self.renameModuleAct = \
            QtGui.QAction("Rename", self, statusTip="Rename",
                          triggered=self.renameModule)

        self.renameAttributeAct = \
            QtGui.QAction("Rename", self, statusTip="Rename",
                          triggered=self.renameAttribute)

        self.inlineAct = \
            QtGui.QAction("Inline", self, statusTip="Inline",
                          triggered=self.inline)

        self.localToFieldAct = \
            QtGui.QAction("Local-to-Field", self, statusTip="Local-to-Field",
                          triggered=self.localToField)

        self.getDocAct = \
            QtGui.QAction("Documentation", self, statusTip="Documentation",
                          triggered=self.getDoc)

        self.getCallTipAct = \
            QtGui.QAction("CallTip", self, statusTip="CallTip",
                          triggered=self.getCallTip)

    def renameModule(self):
        index = self.editorTabWidget.currentIndex()
        moduleName = self.editorTabWidget.tabText(index)
        moduleName = os.path.splitext(moduleName)[0]
        newName = GetName("Rename", moduleName, self)
        project = self.getProject()
        if newName.accepted:
            saved = self.editorTabWidget.saveProject()
            if saved:
                path = self.editorTabWidget.getEditorData("filePath")
                self.renameThread.rename(newName.text, path, project, None)
                self.busyWidget.showBusy(True, "Renaming... please wait!")

    def renameAttribute(self):
        objectName = self.editorTabWidget.get_current_word()
        newName = GetName("Rename", objectName, self)
        if newName.accepted:
            project = self.getProject()
            saved = self.editorTabWidget.saveProject()
            if saved:
                offset = self.getOffset()
                path = self.editorTabWidget.getEditorData("filePath")
                self.renameThread.rename(newName.text, path, project, offset)
                self.busyWidget.showBusy(True, "Renaming... please wait!")

    def renameFinished(self):
        self.busyWidget.showBusy(False)
        if self.renameThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed Rename",
                                                self.renameThread.error)
            return
        if self.renameThread.offset is None:
            # filename has been changed
            oldPath = self.editorTabWidget.getEditorData("filePath")
            ext = os.path.splitext(oldPath)[1]
            newPath = os.path.join(os.path.dirname(oldPath),
                                   self.renameThread.new_name + ext)
            self.editorTabWidget.updateEditorData("filePath", newPath)
        else:
            if len(self.renameThread.changedFiles) > 0:
                self.editorTabWidget.reloadModules(
                    self.renameThread.changedFiles)

    def inline(self):
        offset = self.getOffset()
        path = self.editorTabWidget.getEditorData("filePath")
        project = self.getProject()
        resource = project.get_file(path)
        saved = self.editorTabWidget.saveProject()
        if saved:
            self.inlineThread.inline(project, resource, offset)
            self.busyWidget.showBusy(True, "Inlining... please wait!")

    def inlineFinished(self):
        self.busyWidget.showBusy(False)
        if self.inlineThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed Inline",
                                                self.inlineThread.error)
            return
        if len(self.inlineThread.changedFiles) > 0:
            self.editorTabWidget.reloadModules(self.inlineThread.changedFiles)

    def localToField(self):
        offset = self.getOffset()
        path = self.editorTabWidget.getEditorData("filePath")
        project = self.getProject()
        resource = project.get_file(path)
        saved = self.editorTabWidget.saveProject()
        if saved:
            self.localToFieldThread.convert(project, resource, offset)
            self.busyWidget.showBusy(
                True, "Converting Local to Field... please wait!")

    def localToFieldFinished(self):
        self.busyWidget.showBusy(False)
        if self.localToFieldThread.error is not None:
            message = QtGui.QMessageBox.warning(self, "Failed Local-to-Field",
                                                self.localToFieldThread.error)
            return
        if len(self.localToFieldThread.changedFiles) > 0:
            self.editorTabWidget.reloadModules(
                self.localToFieldThread.changedFiles)

    def findDefinition(self):
        saved = self.editorTabWidget.saveProject()
        if saved:
            offset = self.getOffset()
            path = self.editorTabWidget.getEditorData("filePath")
            project = self.getProject()
            resource = project.get_file(path)
            try:
                result = find_definition(project,
                                         self.editorTabWidget.getSource(),
                                         offset, resource)
                if result is None:
                    self.editorTabWidget.showNotification(
                        "No definition found.")
                else:
                    start, end = result.region
                    offset = result.offset
                    line = result.lineno
                    result_path = result.resource.path
                    sourcePath = self.editorTabWidget.pathDict["sourcedir"]
                    if not os.path.isabs(result_path):
                        result_path = os.path.join(sourcePath, result_path)
                    if os.path.samefile(result_path, path):
                        pass
                    else:
                        self.editorTabWidget.loadfile(result_path)
                    editor = self.editorTabWidget.focusedEditor()
                    start = editor.lineIndexFromPosition(start)
                    end = editor.lineIndexFromPosition(end)
                    editor.setSelection(start[0], start[1], end[0], end[1])
                    editor.ensureLineVisible(line - 1)
            except Exception as err:
                self.editorTabWidget.showNotification(str(err))

    def moduleToPackage(self):
        path = self.editorTabWidget.getEditorData("filePath")
        project = self.getProject()
        saved = self.editorTabWidget.saveProject()
        if saved:
            self.moduleToPackageThread.convert(path, project)
            self.busyWidget.showBusy(True, "Converting... please wait!")

    def moduleToPackageFinished(self):
        self.busyWidget.showBusy(False)
        if self.moduleToPackageThread.error is not None:
            message = QtGui.QMessageBox.warning(
                self, "Failed to convert", self.moduleToPackageThread.error)

    def findOccurrences(self):
        offset = self.getOffset()
        project = self.getProject()
        saved = self.editorTabWidget.saveProject()
        if saved:
            path = self.editorTabWidget.getEditorData("filePath")
            self.objectName = self.editorTabWidget.get_current_word()
            self.findThread.find(path, project, offset)
            self.busyWidget.showBusy(True, "Finding usages... please wait!")

    def findOccurrencesFinished(self):
        self.busyWidget.showBusy(False)
        if self.findThread.error is not None:
            self.editorTabWidget.showNotification(self.findThread.error)
            return
        if len(self.findThread.itemsDict) > 0:
            foundList = []
            for parent, lines in self.findThread.itemsDict.items():
                parentItem = QtGui.QTreeWidgetItem()
                parentItem.setForeground(0,
                                         QtGui.QBrush(QtGui.QColor("#003366")))
                parentItem.setText(0, parent)
                for line in lines:
                    childItem = QtGui.QTreeWidgetItem()
                    childItem.setText(0, str(line))
                    childItem.setFirstColumnSpanned(True)
                    parentItem.addChild(childItem)
                    foundList.append(parentItem)
            usageDialog = UsageDialog(self.editorTabWidget,
                                      "Usages: " + self.objectName, foundList,
                                      self)
        else:
            self.editorTabWidget.showNotification("No usages found.")

    def getOffset(self):
        return self.editorTabWidget.getOffset()

    def get_absolute_coordinates(self):
        editor = self.editorTabWidget.focusedEditor()
        point = editor.get_absolute_coordinates()
        return point

    def getCompletions(self):
        offset = self.getOffset()
        project = self.getProject()
        proposals = codeassist.code_assist(project,
                                           self.editorTabWidget.getSource(),
                                           offset)
        proposals = codeassist.sorted_proposals(proposals)
        if len(proposals) > 0:
            cmpl = []
            for i in proposals:
                cmpl.append(str(i))

            return cmpl
        else:
            return []

    def getDoc(self, offset=None):
        if offset is None:
            offset = self.getOffset()
        project = self.getProject()
        try:
            doc = codeassist.get_doc(project, self.editorTabWidget.getSource(),
                                     offset)
            return doc
        except Exception as err:
            return None

    def getCallTip(self, offset=None):
        if offset is None:
            offset = self.getOffset()
        project = self.getProject()
        try:
            calltip = codeassist.get_calltip(project,
                                             self.editorTabWidget.getSource(),
                                             offset)

            return calltip
        except Exception as err:
            return None

    def getProject(self):
        path = self.editorTabWidget.getEditorData("filePath")
        if path is None:
            return self.noProject
        if path.startswith(self.editorTabWidget.pathDict["sourcedir"]):
            return self.ropeProject
        else:
            return self.noProject
예제 #14
0
def find_usages(_, main_project, other_projects, file_path, offset):
    """
    Find usages of symbol under cursor.
    """
    try:
        occurrences = []
        if other_projects:
            for path in [main_project] + other_projects:
                prj = Project(path,
                              ropefolder=api.project.FOLDER,
                              fscommands=FileSystemCommands())
                prj.validate()
                mod = libutils.path_to_resource(prj, file_path)
                occurrences += find_occurrences(prj,
                                                mod,
                                                offset,
                                                unsure=False,
                                                in_hierarchy=True)
                prj.close()
        else:
            prj = Project(main_project,
                          ropefolder=api.project.FOLDER,
                          fscommands=FileSystemCommands())
            prj.validate()
            mod = libutils.path_to_resource(prj, file_path)
            occurrences = find_occurrences(prj,
                                           mod,
                                           offset,
                                           unsure=False,
                                           in_hierarchy=True)
        # transform results to a serialisable list of usages that is ready
        # to use by the find_results widget.
        occurrences_map = {}
        for location in occurrences:
            path = location.resource.real_path
            lineno = location.lineno - 1
            # convert file region to line region
            content = location.resource.read()
            offset = location.offset
            char = content[offset]
            while char != '\n':  # find start of line
                offset -= 1
                char = content[offset]
            # update offsets
            start = location.offset - offset - 1
            end = location.region[1] - offset - 1
            line_text = content.splitlines()[lineno]
            data = (lineno, line_text, [(start, end)])
            if path not in occurrences_map:
                occurrences_map[path] = [data]
            else:
                occurrences_map[path].append(data)
        results = []
        for key, value in occurrences_map.items():
            results.append((key, value))
        results = sorted(results, key=lambda x: x[0])
        return results
    except RopeError as e:
        error = RefactoringError()
        error.exc = str(e)
        error.traceback = traceback.format_exc()
        error.critical = False
        return error
    except Exception as e:
        error = RefactoringError()
        error.exc = str(e)
        error.traceback = traceback.format_exc()
        error.critical = True
        return error
예제 #15
0
def main():
    """Run the program."""

    print_banner("Running camel_snake_pep8.")

    if project_is_package:
        print("The project is detected as a Python package.")
    else:
        print("The project is detected to be non-package Python scripts.")

    print_warning(
        "\nBe sure to make a backup copy of all files before running this"
        "\nprogram. All changes are made to the files in-place.\n")

    print(
        "The default reply for queries (e.g. with enter) when no warning/caution"
        "\nis given is 'y', i.e, do the changes.  If a warning/caution is given then"
        "\nthe default reply is 'n'.")

    print(
        "\nEntering 'c' will query for a changed name string from the user."
        "\nIf the new name is still not the proper form you will then be queried"
        "\nagain about changing it (which you can say no to if it is what you want)."
    )

    print("\nIt is safer to make all changes to a given package/module"
          " in the same run of\nthe program because warnings of"
          " possible collisions will be more accurate.")

    print(
        "\nModifying the docs changes the names in strings, too.  This is convenient,"
        "\nbut things like dict keys will also be changed.  If you choose to modify"
        "\ndocs you can still select 'd' on viewing individual changes to toggle the"
        "\nsetting off temporarily.")

    print_info("Modify docs (default is 'n')? ", end="")
    docs = raw_input("")
    if docs and docs in "yY":
        print("\nModifying the docs by default.")
        docs = True
    else:
        print("\nNot modifying the docs by default.")
        docs = False

    print("\nConverting these files:")
    for f in fname_list:
        print("   ", f)

    print_info("\nHit enter to begin the refactoring... ", end="")
    raw_input("")
    print()

    # Create a project.
    project = Project(project_dir)
    project.prefs.set("soa_followed_calls", SOA_FOLLOWED_CALLS)

    # Analyze the project.
    # Does this help refactoring?  See below for related discussion.
    # https://groups.google.com/forum/#!topic/rope-dev/1P8OADQ0DQ4
    print_info("Analyzing all the modules in the project, may be slow...")
    rope.base.libutils.analyze_modules(project)  # Analyze all the modules.
    print_info("Finished the analysis.", sep="")
    print()

    for filename in fname_list:
        #experiment_with_scoping_classes(project, filename)
        print_banner("Python module name: " +
                     filename_to_module_name(filename),
                     char="%",
                     big=True)

        print_banner("Changing variables assigned in the code.")
        while change_assigned_variables:
            possible_changes = rope_iterate_worder(filename,
                                                   assigned_vars=True)
            if not possible_changes:
                print("No more variable assignment changes.\n")
            if not rope_rename_refactor(
                    project, filename, possible_changes, docs=docs):
                break

        print_banner("Changing function arguments which do not have defaults.")
        while change_function_and_method_arguments:
            possible_changes = rope_iterate_worder(filename,
                                                   fun_arguments=True)
            if not possible_changes:
                print("No more function argument changes.\n")
            if not rope_rename_refactor(
                    project, filename, possible_changes, docs=docs):
                break

        print_banner("Changing function and method names.")
        while change_function_and_method_names:
            possible_changes = rope_iterate_worder(filename,
                                                   fun_name_defs=True)
            if not possible_changes:
                print("No more function and method name changes.\n")
            if not rope_rename_refactor(
                    project, filename, possible_changes, docs=docs):
                break

        if not change_assigned_variables:  # Redundant when that is also selected.
            print_banner("Changing function and method keywords.")
            while change_function_and_method_keywords:
                possible_changes = rope_iterate_worder(filename,
                                                       fun_keywords=True)
                if not possible_changes:
                    print("No more function and method keyword changes.\n")
                if not rope_rename_refactor(
                        project, filename, possible_changes, docs=docs):
                    break

        print_banner("Changing class names.")
        while change_class_names:
            possible_changes = rope_iterate_worder(filename, class_names=True)
            if not possible_changes:
                print("No more class name changes.\n")
            if not rope_rename_refactor(
                    project, filename, possible_changes, docs=docs):
                break

    project.close()
예제 #16
0
option = option_arg;
projectpath = project_arg
if projectpath.startswith("file://"):
	projectpath = projectpath.replace("file://", "")

proj = Project(projectpath)
proj.pycore._init_python_files()

input = open(source_code_arg, 'r')
source_code = input.read()
respath = relpath(projectpath, res_arg)
res = proj.get_resource(respath)

position = int(offset_arg)

try:
	if option == "autocomplete":
		proposals = codeassist.code_assist(proj, source_code, position, resource=res, maxfixes=10)
		proposals = codeassist.sorted_proposals(proposals)

		for proposal in proposals:
			print proposal

	elif option == "calltip":
		proposals = codeassist.get_doc(proj, source_code, position, resource=res, maxfixes=10)
		print proposals
except:
	pass

proj.close()
예제 #17
0
      args
       project: type=rope.base.project.Project

    Example #4::

      pattern ${pow}(${param1}, ${param2})
      goal ${param1} ** ${param2}
      args pow: name=mod.pow, exact

    Example #5::

      pattern ${inst}.longtask(${p1}, ${p2})
      goal
       ${inst}.subtask1(${p1})
       ${inst}.subtask2(${p2})
      args
       inst: type=mod.A,unsure

"""
restructuring = restructure.Restructure(project, pattern, goal, args)

project.do(restructuring.get_changes())
mod2.read()
u'import mod1\nprint(2 ** 3)\n'

# Cleaning up
# mod1.remove()
# mod2.remove()
project.close()
예제 #18
0
class CodimensionProject(QObject):
    " Provides codimension project singleton facility "

    # Constants for the projectChanged signal
    CompleteProject = 0  # It is a completely new project
    Properties = 1  # Project properties were updated

    projectChanged = pyqtSignal(int)
    fsChanged = pyqtSignal(list)

    def __init__(self):
        QObject.__init__(self)

        self.__dirWatcher = None
        self.__formatOK = True

        # Avoid pylint complains
        self.fileName = ""
        self.userProjectDir = ""  # Directory in ~/.codimension/uuidNN/
        self.filesList = set()

        self.scriptName = ""  # Script to run the project
        self.creationDate = ""
        self.author = ""
        self.license = ""
        self.copyright = ""
        self.version = ""
        self.email = ""
        self.description = ""
        self.uuid = ""

        self.__ropeProject = None
        self.__ropeSourceDirs = []

        # Coming from separate files from ~/.codimension/uuidN/
        self.todos = []
        self.bookmarks = []
        self.runParamsCache = RunParametersCache()
        self.topLevelDirs = []
        self.findHistory = []
        self.findNameHistory = []
        self.findFileHistory = []
        self.replaceHistory = []
        self.tabsStatus = []

        self.findFilesWhat = []
        self.findFilesDirs = []
        self.findFilesMasks = []

        self.findClassHistory = []
        self.findFuncHistory = []
        self.findGlobalHistory = []

        self.recentFiles = []
        self.importDirs = []
        self.fileBrowserPaths = []

        self.ignoredExcpt = []
        self.breakpoints = []
        self.watchpoints = []

        # Precompile the exclude filters
        self.__excludeFilter = []
        for flt in Settings().projectFilesFilters:
            self.__excludeFilter.append(re.compile(flt))

        return

    def shouldExclude(self, name):
        " Tests if a file must be excluded "
        for excl in self.__excludeFilter:
            if excl.match(name):
                return True
        return False

    def __resetValues(self):
        """ Initializes or resets all the project members """

        # Empty file name means that the project has not been loaded or
        # created. This must be an absolute path.
        self.fileName = ""
        self.userProjectDir = ""

        # Generated having the project dir Full paths are stored.
        # The set holds all files and directories. The dirs end with os.path.sep
        self.filesList = set()

        self.scriptName = ""
        self.creationDate = ""
        self.author = ""
        self.license = ""
        self.copyright = ""
        self.version = ""
        self.email = ""
        self.description = ""
        self.uuid = ""

        # Coming from separate files from ~/.codimension/uuidN/
        self.todos = []
        self.bookmarks = []
        self.runParamsCache = RunParametersCache()
        self.topLevelDirs = []
        self.findHistory = []
        self.findNameHistory = []
        self.findFileHistory = []
        self.replaceHistory = []
        self.tabsStatus = []

        self.findFilesWhat = []
        self.findFilesDirs = []
        self.findFilesMasks = []

        self.findClassHistory = []
        self.findFuncHistory = []
        self.findGlobalHistory = []

        self.recentFiles = []
        self.importDirs = []
        self.fileBrowserPaths = []

        self.ignoredExcpt = []
        self.breakpoints = []
        self.watchpoints = []

        # Reset the dir watchers if so
        if self.__dirWatcher is not None:
            del self.__dirWatcher
        self.__dirWatcher = None
        return

    def createNew(self, fileName, scriptName, importDirs, author, lic,
                  copyRight, description, creationDate, version, email):
        " Creates a new project "

        # Try to create the user project directory
        projectUuid = str(uuid.uuid1())
        userProjectDir = settingsDir + projectUuid + sep
        if not os.path.exists(userProjectDir):
            try:
                os.mkdir(userProjectDir)
            except:
                logging.error("Cannot create user project directory: " +
                              self.userProjectDir + ". Please check the "
                              "available disk space and re-create the "
                              "project.")
                raise
        else:
            logging.warning("The user project directory existed! "
                            "The content will be overwritten.")
            self.__removeProjectFiles(userProjectDir)

        # Basic pre-requisites are met. We can reset the current project
        self.__resetValues()

        self.fileName = str(fileName)
        self.importDirs = importDirs
        self.scriptName = scriptName
        self.creationDate = creationDate
        self.author = author
        self.license = lic
        self.copyright = copyRight
        self.version = version
        self.email = email
        self.description = description
        self.uuid = projectUuid
        self.userProjectDir = userProjectDir

        self.__createProjectFile()  # ~/.codimension/uuidNN/project

        self.__generateFilesList()

        self.saveProject()

        # Update the watcher
        self.__dirWatcher = Watcher(Settings().projectFilesFilters,
                                    self.getProjectDir())
        self.__dirWatcher.fsChanged.connect(self.onFSChanged)

        self.__createRopeProject()
        self.projectChanged.emit(self.CompleteProject)
        return

    @staticmethod
    def __safeRemove(path):
        " Safe file removal "
        try:
            os.remove(path)
        except:
            return

    def __removeProjectFiles(self, userProjectDir):
        " Removes user project files "

        self.__safeRemove(userProjectDir + "project")
        self.__safeRemove(userProjectDir + "bookmarks")
        self.__safeRemove(userProjectDir + "todos")
        self.__safeRemove(userProjectDir + "searchhistory")
        self.__safeRemove(userProjectDir + "topleveldirs")
        self.__safeRemove(userProjectDir + "tabsstatus")
        self.__safeRemove(userProjectDir + "findinfiles")
        self.__safeRemove(userProjectDir + "recentfiles")
        self.__safeRemove(userProjectDir + "filebrowser")
        self.__safeRemove(userProjectDir + "ignoredexcpt")
        self.__safeRemove(userProjectDir + "breakpoints")
        self.__safeRemove(userProjectDir + "watchpoints")
        return

    def __createProjectFile(self):
        " Helper function to create the user project file "
        try:
            f = open(self.userProjectDir + "project", "w")
            f.write(self.fileName)
            f.close()
        except:
            return

    def saveProject(self):
        " Writes all the settings into the file "
        if not self.isLoaded():
            return

        # Project properties part
        propertiesPart = "[properties]\n" \
                         "scriptname=" + self.scriptName + "\n" \
                         "creationdate=" + self.creationDate + "\n" \
                         "author=" + self.author + "\n" \
                         "license=" + self.license + "\n" \
                         "copyright=" + self.copyright + "\n" \
                         "description=" + \
                            self.description.replace( '\n', '<CR><LF>' ) + \
                            "\n" \
                         "version=" + self.version + "\n" \
                         "email=" + self.email + "\n" \
                         "uuid=" + self.uuid + "\n"

        # It could be another user project file without write permissions
        skipProjectFile = False
        if os.path.exists(self.fileName):
            if not os.access(self.fileName, os.W_OK):
                skipProjectFile = True
        else:
            if not os.access(os.path.dirname(self.fileName), os.W_OK):
                skipProjectFile = True

        if not skipProjectFile:
            f = open(self.fileName, "w")
            self.__writeHeader(f)
            self.__writeList(f, "importdirs", "dir", self.importDirs)
            f.write(propertiesPart + "\n\n\n")
            f.close()

        self.serializeRunParameters()
        self.__saveTopLevelDirs()
        self.__saveSearchHistory()
        self.__saveTabsStatus()
        self.__saveFindFiles()
        self.__saveFindObjects()
        self.__saveRecentFiles()
        self.__saveIgnoredExcpt()
        self.__saveBreakpoints()
        self.__saveWatchpoints()

        self.__formatOK = True
        return

    def serializeRunParameters(self):
        " Saves the run parameters cache "
        self.runParamsCache.serialize(self.userProjectDir + "runparamscache")
        return

    def __saveTabsStatus(self):
        " Helper to save tabs status "
        if self.isLoaded():
            f = open(self.userProjectDir + "tabsstatus", "w")
            self.__writeHeader(f)
            self.__writeList(f, "tabsstatus", "tab", self.tabsStatus)
            f.close()
        return

    def __saveSearchHistory(self):
        " Helper to save the project search history "
        if self.isLoaded():
            f = open(self.userProjectDir + "searchhistory", "w")
            self.__writeHeader(f)
            self.__writeList(f, "findhistory", "find", self.findHistory)
            self.__writeList(f, "replacehistory", "replace",
                             self.replaceHistory)
            self.__writeList(f, "findnamehistory", "find",
                             self.findNameHistory)
            self.__writeList(f, "findfilehistory", "find",
                             self.findFileHistory)
            f.close()
        return

    def __saveTopLevelDirs(self):
        " Helper to save the project top level dirs "
        if self.isLoaded():
            f = open(self.userProjectDir + "topleveldirs", "w")
            self.__writeHeader(f)
            self.__writeList(f, "topleveldirs", "dir", self.topLevelDirs)
            f.close()
        return

    def __saveFindFiles(self):
        " Helper to save the find in files history "
        if self.isLoaded():
            f = open(self.userProjectDir + "findinfiles", "w")
            self.__writeHeader(f)
            self.__writeList(f, "whathistory", "what", self.findFilesWhat)
            self.__writeList(f, "dirhistory", "dir", self.findFilesDirs)
            self.__writeList(f, "maskhistory", "mask", self.findFilesMasks)
            f.close()
        return

    def __saveFindObjects(self):
        " Helper to save find objects history "
        if self.isLoaded():
            f = open(self.userProjectDir + "findobjects", "w")
            self.__writeHeader(f)
            self.__writeList(f, "classhistory", "class", self.findClassHistory)
            self.__writeList(f, "funchistory", "func", self.findFuncHistory)
            self.__writeList(f, "globalhistory", "global",
                             self.findGlobalHistory)
            f.close()
        return

    def __saveSectionToFile(self, fileName, sectionName, itemName, values):
        " Saves the given values into a file "
        if self.isLoaded():
            f = open(self.userProjectDir + fileName, "w")
            self.__writeHeader(f)
            self.__writeList(f, sectionName, itemName, values)
            f.close()
        return

    def __saveRecentFiles(self):
        " Helper to save recent files list "
        self.__saveSectionToFile("recentfiles", "recentfiles", "file",
                                 self.recentFiles)
        return

    def __saveIgnoredExcpt(self):
        " Helper to save ignored exceptions list "
        self.__saveSectionToFile("ignoredexcpt", "ignoredexcpt", "excpttype",
                                 self.ignoredExcpt)
        return

    def __saveBreakpoints(self):
        " Helper to save breakpoints "
        self.__saveSectionToFile("breakpoints", "breakpoints", "bpoint",
                                 self.breakpoints)
        return

    def __saveWatchpoints(self):
        " helper to save watchpoints "
        self.__saveSectionToFile("watchpoints", "watchpoints", "wpoint",
                                 self.watchpoints)
        return

    @staticmethod
    def __writeHeader(fileObj):
        " Helper to write a header with a warning "
        fileObj.write("#\n"
                      "# Generated automatically.\n"
                      "# Don't edit it manually unless you "
                      "know what you are doing.\n"
                      "#\n\n")
        return

    @staticmethod
    def __writeList(fileObj, header, prefix, items):
        " Helper to write a list "
        fileObj.write("[" + header + "]\n")
        index = 0
        for item in items:
            fileObj.write(prefix + str(index) + "=" + item + "\n")
            index += 1
        fileObj.write("\n")
        return

    def __getStr(self, conf, sec, key, default):
        " Helper to read a config value "
        try:
            return conf.get(sec, key).strip()
        except:
            self.__formatOK = False
        return default

    def loadProject(self, projectFile):
        """ Loads a project from the given file """

        absPath = os.path.abspath(projectFile)
        if not os.path.exists(absPath):
            raise Exception("Cannot open project file " + projectFile)
        if not absPath.endswith(".cdm"):
            raise Exception("Unexpected project file extension. "
                            "Expected: .cdm")

        config = ConfigParser.ConfigParser()

        try:
            config.read(absPath)
        except:
            # Bad error - cannot load project file at all
            config = None
            raise Exception("Bad project file")

        self.__resetValues()
        self.fileName = str(absPath)

        # Properties part
        self.scriptName = self.__getStr(config, 'properties', 'scriptname', '')
        self.creationDate = self.__getStr(config, 'properties', 'creationdate',
                                          '')
        self.author = self.__getStr(config, 'properties', 'author', '')
        self.license = self.__getStr(config, 'properties', 'license', '')
        self.copyright = self.__getStr(config, 'properties', 'copyright', '')
        self.description = self.__getStr(config, 'properties', 'description',
                                         '').replace('<CR><LF>', '\n')
        self.version = self.__getStr(config, 'properties', 'version', '')
        self.email = self.__getStr(config, 'properties', 'email', '')
        self.uuid = self.__getStr(config, 'properties', 'uuid', '')
        if self.uuid == "":
            logging.warning("Project file does not have UUID. "
                            "Re-generate it...")
            self.uuid = str(uuid.uuid1())
        self.userProjectDir = settingsDir + self.uuid + sep
        if not os.path.exists(self.userProjectDir):
            os.mkdir(self.userProjectDir)

        # import dirs part
        index = 0
        try:
            while True:
                dirName = config.get('importdirs', 'dir' + str(index)).strip()
                index += 1
                if os.path.isabs(dirName):
                    absPath = dirName
                else:
                    absPath = self.getProjectDir() + dirName
                if not os.path.exists(absPath):
                    logging.error("Codimension project: cannot find "
                                  "import directory: " + dirName)
                elif not isdir(absPath):
                    logging.error("Codimension project: the import path: " +
                                  dirName + " is not a directory")
                self.importDirs.append(dirName)
        except ConfigParser.NoSectionError:
            self.__formatOK = False
        except ConfigParser.NoOptionError:
            # just continue
            pass
        except:
            self.__formatOK = False

        config = None

        # Read the other config files
        self.__loadTopLevelDirs()
        self.__loadSearchHistory()
        self.__loadTabsStatus()
        self.__loadFindFiles()
        self.__loadFindObjects()
        self.__loadRecentFiles()
        self.__loadProjectBrowserExpandedDirs()
        self.__loadIgnoredExceptions()
        self.__loadBreakpoints()
        self.__loadWatchpoints()

        # The project might have been moved...
        self.__createProjectFile()  # ~/.codimension/uuidNN/project
        self.__generateFilesList()

        if os.path.exists(self.userProjectDir + "runparamscache"):
            self.runParamsCache.deserialize(self.userProjectDir +
                                            "runparamscache")

        if not self.__formatOK:
            logging.info("Project files are broken or absent. "
                         "Overwriting the project files.")
            self.saveProject()

        # Update the recent list
        Settings().addRecentProject(self.fileName)

        # Setup the new watcher
        self.__dirWatcher = Watcher(Settings().projectFilesFilters,
                                    self.getProjectDir())
        self.__dirWatcher.fsChanged.connect(self.onFSChanged)

        self.__createRopeProject()
        self.projectChanged.emit(self.CompleteProject)
        self.emit(SIGNAL('restoreProjectExpandedDirs'))
        return

    def getImportDirsAsAbsolutePaths(self):
        " Provides a list of import dirs as absolute paths "
        result = []
        for path in self.importDirs:
            if os.path.isabs(path):
                result.append(path)
            else:
                result.append(self.getProjectDir() + path)
        return result

    def __getImportDirsForRope(self):
        " Provides the list of import dirs for the rope library "
        result = []
        for path in self.importDirs:
            if not os.path.isabs(path):
                # The only relative paths can be accepted by rope
                result.append(path)
        result.sort()
        return result

    def getRopeProject(self):
        " Provides the rope project "
        if self.__getImportDirsForRope() != self.__ropeSourceDirs:
            # The user changed the project import dirs, so let's
            # re-create the project
            self.__createRopeProject()
        return self.__ropeProject

    def validateRopeProject(self, fileName):
        " Validates the rope project "
        # Currently rope can validate a directory only so the argument
        # is ignored
        self.__ropeProject.validate()
        return

    def __createRopeProject(self):
        " Creates a rope library project "
        if self.__ropeProject is not None:
            self.__ropeProject.close()
            self.__ropeProject = None
            self.__ropeSourceDirs = []

        # Deal with import dirs and preferences first
        self.__ropeSourceDirs = self.__getImportDirsForRope()
        prefs = copy.deepcopy(ropePreferences)
        if len(self.__ropeSourceDirs) != 0:
            prefs["source_folders"] = self.__ropeSourceDirs

        # Rope folder is default here, so it will be created
        self.__ropeProject = RopeProject(self.getProjectDir(), **prefs)
        self.__ropeProject.validate(self.__ropeProject.root)
        return

    def onFSChanged(self, items):
        " Triggered when the watcher detects changes "
        ##        report = "REPORT: "
        ##        projectItems = []
        for item in items:
            item = str(item)

            #            if not islink( item ):
            #                realPath = realpath( item[ 1: ] )
            #                isDir = item.endswith( sep )
            #                if isDir:
            #                    if self.isProjectDir( realPath + sep ):
            #                        item = item[ 0 ] + realPath + sep
            #                else:
            #                    if self.isProjectFile( realPath + sep ):
            #                        item = item[ 0 ] + realPath

            #            projectItems.append( item )
            ##            report += " " + item
            try:
                if item.startswith('+'):
                    self.filesList.add(item[1:])
                else:
                    self.filesList.remove(item[1:])
##                projectItems.append( item )
            except:
                #                print "EXCEPTION for '" + item + "'"
                pass


#        print "'" + report + "'"

        self.fsChanged.emit(items)
        #        self.__dirWatcher.debug()
        return

    def __loadTabsStatus(self):
        " Loads the last tabs status "
        configFile = self.userProjectDir + "tabsstatus"
        if not os.path.exists(configFile):
            logging.info("Cannot find tabsstatus project file. "
                         "Expected here: " + configFile)
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(configFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read tabsstatus project file "
                            "from here: " + configFile)
            return

        # tabs part
        self.tabsStatus = self.__loadListSection(config, 'tabsstatus', 'tab')

        config = None
        return

    def __loadProjectBrowserExpandedDirs(self):
        " Loads the project browser expanded dirs "
        configFile = self.userProjectDir + "filebrowser"
        if not os.path.exists(configFile):
            self.fileBrowserPaths = []
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(configFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.fileBrowserPaths = []
            return

        # dirs part
        self.fileBrowserPaths = self.__loadListSection(config, 'filebrowser',
                                                       'path')
        config = None
        return

    def __loadTopLevelDirs(self):
        " Loads the top level dirs "
        configFile = self.userProjectDir + "topleveldirs"
        if not os.path.exists(configFile):
            logging.info("Cannot find topleveldirs project file. "
                         "Expected here: " + configFile)
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(configFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read topleveldirs project file "
                            "from here: " + configFile)
            return

        # dirs part
        self.topLevelDirs = self.__loadListSection(config, 'topleveldirs',
                                                   'dir')

        config = None
        return

    def __loadSearchHistory(self):
        " Loads the search history file content "
        confFile = self.userProjectDir + "searchhistory"
        if not os.path.exists(confFile):
            logging.info("Cannot find searchhistory project file. "
                         "Expected here: " + confFile)
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(confFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read searchhistory project file "
                            "from here: " + confFile)
            return

        # find part
        self.findHistory = self.__loadListSection(config, 'findhistory',
                                                  'find')
        self.findNameHistory = self.__loadListSection(config,
                                                      'findnamehistory',
                                                      'find')
        self.findFileHistory = self.__loadListSection(config,
                                                      'findfilehistory',
                                                      'find')

        # replace part
        self.replaceHistory = self.__loadListSection(config, 'replacehistory',
                                                     'replace')

        config = None
        return

    def __loadFindObjects(self):
        " Loads the find objects history "
        confFile = self.userProjectDir + "findobjects"
        if not os.path.exists(confFile):
            logging.info("Cannot find findobjects project file. "
                         "Expected here: " + confFile)
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(confFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read findobjects project file "
                            "from here: " + confFile)
            return

        self.findClassHistory = self.__loadListSection(config, 'classhistory',
                                                       'class')
        self.findFuncHistory = self.__loadListSection(config, 'funchistory',
                                                      'func')
        self.findGlobalHistory = self.__loadListSection(
            config, 'globalhistory', 'global')
        config = None
        return

    def __loadFindFiles(self):
        " Loads the find in files history "
        confFile = self.userProjectDir + "findinfiles"
        if not os.path.exists(confFile):
            logging.info("Cannot find findinfiles project file. "
                         "Expected here: " + confFile)
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(confFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read findinfiles project file "
                            "from here: " + confFile)
            return

        self.findFilesWhat = self.__loadListSection(config, 'whathistory',
                                                    'what')
        self.findFilesDirs = self.__loadListSection(config, 'dirhistory',
                                                    'dir')
        self.findFilesMasks = self.__loadListSection(config, 'maskhistory',
                                                     'mask')
        config = None
        return

    def __loadRecentFiles(self):
        " Loads the recent files list "
        confFile = self.userProjectDir + "recentfiles"
        if not os.path.exists(confFile):
            logging.info("Cannot find recentfiles project file. "
                         "Expected here: " + confFile)
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read(confFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read recentfiles project file "
                            "from here: " + confFile)
            return

        self.recentFiles = self.__loadListSection(config, 'recentfiles',
                                                  'file')

        # Due to a bug there could be the same files twice in the list.
        # The difference is doubled path separator. Fix it here.
        temp = set()
        for path in self.recentFiles:
            temp.add(os.path.normpath(path))
        self.recentFiles = list(temp)

        config = None
        return

    def __loadSectionFromFile(self, fileName, sectionName, itemName,
                              errMessageEntity):
        " Loads the given section from the given file "
        confFile = self.userProjectDir + fileName
        if not os.path.exists(confFile):
            logging.info("Cannot find " + errMessageEntity + " file. "
                         "Expected here: " + confFile)
            self.__formatOK = False
            return []

        config = ConfigParser.ConfigParser()
        try:
            config.read(confFile)
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning("Cannot read " + errMessageEntity + " file "
                            "from here: " + confFile)
            return []

        values = self.__loadListSection(config, sectionName, itemName)
        config = None
        return values

    def __loadIgnoredExceptions(self):
        " Loads the ignored exceptions list "
        self.ignoredExcpt = self.__loadSectionFromFile("ignoredexcpt",
                                                       "ignoredexcpt",
                                                       "excpttype",
                                                       "ignored exceptions")
        return

    def __loadBreakpoints(self):
        " Loads the project breakpoints "
        self.breakpoints = self.__loadSectionFromFile("breakpoints",
                                                      "breakpoints", "bpoint",
                                                      "breakpoints")
        return

    def __loadWatchpoints(self):
        " Loads the project watchpoints "
        self.watchpoints = self.__loadSectionFromFile("watchpoints",
                                                      "watchpoints", "wpoint",
                                                      "watchpoints")
        return

    def __loadListSection(self, config, section, listPrefix):
        " Loads a list off the given section from the given file "
        items = []
        index = 0
        try:
            while True:
                item = config.get(section, listPrefix + str(index)).strip()
                index += 1
                items.append(item)
        except ConfigParser.NoSectionError:
            self.__formatOK = False
        except ConfigParser.NoOptionError:
            pass  # Just continue
        except:
            self.__formatOK = False
        return items

    def unloadProject(self, emitSignal=True):
        """ Unloads the current project if required """
        self.emit(SIGNAL('projectAboutToUnload'))
        if self.isLoaded():
            self.__saveProjectBrowserExpandedDirs()
        self.__resetValues()
        if emitSignal:
            # No need to send a signal e.g. if IDE is closing
            self.projectChanged.emit(self.CompleteProject)
        if self.__ropeProject is not None:
            try:
                # If the project directory is read only then closing the
                # rope project generates exception
                self.__ropeProject.close()
            except:
                pass
        self.__ropeProject = None
        self.__ropeSourceDirs = []
        return

    def __saveProjectBrowserExpandedDirs(self):
        " Saves the pathes expanded in the project browser "
        if not self.isLoaded():
            return

        try:
            f = open(self.userProjectDir + "filebrowser", "w")
            self.__writeHeader(f)
            self.__writeList(f, "filebrowser", "path", self.fileBrowserPaths)
            f.close()
        except:
            pass
        return

    def setImportDirs(self, paths):
        " Sets a new set of the project import dirs "
        if self.importDirs != paths:
            self.importDirs = paths
            self.saveProject()
            self.projectChanged.emit(self.Properties)
        return

    def __generateFilesList(self):
        """ Generates the files list having the list of dirs """
        self.filesList = set()
        path = self.getProjectDir()
        self.filesList.add(path)
        self.__scanDir(path)
        return

    def __scanDir(self, path):
        """ Recursive function to scan one dir """
        # The path is with '/' at the end
        for item in os.listdir(path):
            if self.shouldExclude(item):
                continue

            # Exclude symlinks if they point to the other project
            # covered pieces
            candidate = path + item
            if islink(candidate):
                realItem = realpath(candidate)
                if isdir(realItem):
                    if self.isProjectDir(realItem):
                        continue
                else:
                    if self.isProjectDir(os.path.dirname(realItem)):
                        continue

            if isdir(candidate):
                self.filesList.add(candidate + sep)
                self.__scanDir(candidate + sep)
                continue
            self.filesList.add(candidate)
        return

    def isProjectDir(self, path):
        " Returns True if the path belongs to the project "
        if not self.isLoaded():
            return False
        path = realpath(str(path))  # it could be a symlink
        if not path.endswith(sep):
            path += sep
        return path.startswith(self.getProjectDir())

    def isProjectFile(self, path):
        " Returns True if the path belongs to the project "
        if not self.isLoaded():
            return False
        return self.isProjectDir(os.path.dirname(path))

    def isTopLevelDir(self, path):
        " Checks if the path is a top level dir "
        if not path.endswith(sep):
            path += sep
        return path in self.topLevelDirs

    def addTopLevelDir(self, path):
        " Adds the path to the top level dirs list "
        if not path.endswith(sep):
            path += sep
        if path in self.topLevelDirs:
            logging.warning("Top level dir " + path +
                            " is already in the list of dirs. "
                            "Ignore adding...")
            return
        self.topLevelDirs.append(path)
        self.__saveTopLevelDirs()
        return

    def removeTopLevelDir(self, path):
        " Removes the path from the top level dirs list "
        if not path.endswith(sep):
            path += sep
        if path not in self.topLevelDirs:
            logging.warning("Top level dir " + path +
                            " is not in the list of dirs. Ignore removing...")
            return
        self.topLevelDirs.remove(path)
        self.__saveTopLevelDirs()
        return

    def setFindNameHistory(self, history):
        " Sets the new find name history and saves it into a file "
        self.findNameHistory = history
        self.__saveSearchHistory()
        return

    def setFindFileHistory(self, history):
        " Sets the new find file history and saves it into a file "
        self.findFileHistory = history
        self.__saveSearchHistory()
        return

    def setFindHistory(self, history):
        " Sets the new find history and save it into a file "
        self.findHistory = history
        self.__saveSearchHistory()
        return

    def setReplaceHistory(self, whatHistory, toHistory):
        " Sets the new replace history and save it into a file "
        self.findHistory = whatHistory
        self.replaceHistory = toHistory
        self.__saveSearchHistory()
        return

    def setTabsStatus(self, status):
        " Sets the new tabs status and save it into a file "
        self.tabsStatus = status
        self.__saveTabsStatus()
        return

    def setFindInFilesHistory(self, what, dirs, masks):
        " Sets the new lists and save them into a file "
        self.findFilesWhat = what
        self.findFilesDirs = dirs
        self.findFilesMasks = masks
        self.__saveFindFiles()
        return

    def setFindClassHistory(self, history):
        " Sets the new history and saves it into a file "
        self.findClassHistory = history
        self.__saveFindObjects()
        return

    def setFindFuncHistory(self, history):
        " Sets the new history and saves it into a file "
        self.findFuncHistory = history
        self.__saveFindObjects()
        return

    def setFindGlobalHistory(self, history):
        " Sets the new history and saves it into a file "
        self.findGlobalHistory = history
        self.__saveFindObjects()
        return

    def updateProperties(self, scriptName, importDirs, creationDate, author,
                         lic, copy_right, version, email, description):
        " Updates the project properties "

        if self.scriptName == scriptName and \
           self.creationDate == creationDate and \
           self.author == author and \
           self.license == lic and \
           self.copyright == copy_right and \
           self.version == version and \
           self.email == email and \
           self.description == description and \
           self.importDirs == importDirs:
            # No real changes
            return

        self.importDirs = importDirs
        self.scriptName = scriptName
        self.creationDate = creationDate
        self.author = author
        self.license = lic
        self.copyright = copy_right
        self.version = version
        self.email = email
        self.description = description
        self.saveProject()
        self.projectChanged.emit(self.Properties)
        return

    def onProjectFileUpdated(self):
        " Called when a project file is updated via direct editing "

        scriptName, importDirs, \
        creationDate, author, \
        lic, copy_right, \
        description, version, \
        email, projectUuid = getProjectProperties( self.fileName )

        self.importDirs = importDirs
        self.scriptName = scriptName
        self.creationDate = creationDate
        self.author = author
        self.license = lic
        self.copyright = copy_right
        self.version = version
        self.email = email
        self.description = description

        # no need to save, but signal just in case
        self.projectChanged.emit(self.Properties)
        return

    def isLoaded(self):
        " returns True if a project is loaded "
        return self.fileName != ""

    def getProjectDir(self):
        " Provides an absolute path to the project dir "
        if not self.isLoaded():
            return ""
        return os.path.dirname(realpath(self.fileName)) + sep

    def getProjectScript(self):
        " Provides the project script file name "
        if not self.isLoaded():
            return ""
        if self.scriptName == "":
            return ""
        if os.path.isabs(self.scriptName):
            return self.scriptName
        return os.path.normpath(self.getProjectDir() + self.scriptName)

    def addRecentFile(self, path):
        " Adds a single recent file. True if a new file was inserted. "
        if path in self.recentFiles:
            self.recentFiles.remove(path)
            self.recentFiles.insert(0, path)
            self.__saveRecentFiles()
            return False
        self.recentFiles.insert(0, path)
        self.__saveRecentFiles()
        if len(self.recentFiles) > 32:
            self.recentFiles = self.recentFiles[0:32]
        self.emit(SIGNAL('recentFilesChanged'))
        return True

    def removeRecentFile(self, path):
        " Removes a single recent file "
        if path in self.recentFiles:
            self.recentFiles.remove(path)
            self.__saveRecentFiles()
        return

    def addExceptionFilter(self, excptType):
        " Adds a new ignored exception type "
        if excptType not in self.ignoredExcpt:
            self.ignoredExcpt.append(excptType)
            self.__saveIgnoredExcpt()
        return

    def deleteExceptionFilter(self, excptType):
        " Remove ignored exception type "
        if excptType in self.ignoredExcpt:
            self.ignoredExcpt.remove(excptType)
            self.__saveIgnoredExcpt()
        return

    def setExceptionFilters(self, newFilters):
        " Sets the new filters "
        self.ignoredExcpt = newFilters
        self.__saveIgnoredExcpt()
        return

    def addBreakpoint(self, bpointStr):
        " Adds serialized breakpoint "
        if bpointStr not in self.breakpoints:
            self.breakpoints.append(bpointStr)
            self.__saveBreakpoints()
        return

    def deleteBreakpoint(self, bpointStr):
        " Deletes serialized breakpoint "
        if bpointStr in self.breakpoints:
            self.breakpoints.remove(bpointStr)
            self.__saveBreakpoints()
        return

    def setBreakpoints(self, bpointStrList):
        " Sets breakpoints list "
        self.breakpoints = bpointStrList
        self.__saveBreakpoints()
        return

    def addWatchpoint(self, wpointStr):
        " Adds serialized watchpoint "
        if wpointStr not in self.watchpoints:
            self.watchpoints.append(wpointStr)
            self.__saveWatchpoints()
        return

    def deleteWatchpoint(self, wpointStr):
        " Deletes serialized watchpoint "
        if wpointStr in self.watchpoints:
            self.watchpoints.remove(wpointStr)
            self.__saveWatchpoints()
        return

    def setWatchpoints(self, wpointStrList):
        " Sets watchpoints list "
        self.watchpoints = wpointStrList
        self.__saveWatchpoints()
        return
예제 #19
0
def find_usages(_, main_project, other_projects, file_path, offset):
    """
    Find usages of symbol under cursor.
    """
    try:
        occurrences = []
        if other_projects:
            for path in [main_project] + other_projects:
                prj = Project(path, ropefolder=api.project.FOLDER,
                              fscommands=FileSystemCommands())
                prj.validate()
                mod = libutils.path_to_resource(prj, file_path)
                occurrences += find_occurrences(
                    prj, mod, offset, unsure=False, in_hierarchy=True)
                prj.close()
        else:
            prj = Project(main_project, ropefolder=api.project.FOLDER,
                          fscommands=FileSystemCommands())
            prj.validate()
            mod = libutils.path_to_resource(prj, file_path)
            occurrences = find_occurrences(prj, mod, offset, unsure=False,
                                           in_hierarchy=True)
        # transform results to a serialisable list of usages that is ready
        # to use by the find_results widget.
        occurrences_map = {}
        for location in occurrences:
            path = location.resource.real_path
            lineno = location.lineno - 1
            # convert file region to line region
            content = location.resource.read()
            offset = location.offset
            char = content[offset]
            while char != '\n':  # find start of line
                offset -= 1
                char = content[offset]
            # update offsets
            start = location.offset - offset - 1
            end = location.region[1] - offset - 1
            line_text = content.splitlines()[lineno]
            data = (lineno, line_text, [(start, end)])
            if path not in occurrences_map:
                occurrences_map[path] = [data]
            else:
                occurrences_map[path].append(data)
        results = []
        for key, value in occurrences_map.items():
            results.append((key, value))
        results = sorted(results, key=lambda x: x[0])
        return results
    except RopeError as e:
        error = RefactoringError()
        error.exc = str(e)
        error.traceback = traceback.format_exc()
        error.critical = False
        return error
    except Exception as e:
        error = RefactoringError()
        error.exc = str(e)
        error.traceback = traceback.format_exc()
        error.critical = True
        return error
예제 #20
0
class PyRefactor(plugins.WorkspacePlugin):
    """
    Adds some refactoring capabilities to the IDE (using the rope library).

    Supported operations:
        - Rename
        - Extract method
        - Extract variable
        - Find occurrences
        - Organize imports

    """
    def activate(self):
        self._preview_dock = None
        self._occurrences_dock = None
        self._occurrences_results = None
        self._review_widget = None
        api.signals.connect_slot(api.signals.CURRENT_PROJECT_CHANGED,
                                 self._on_current_project_changed)
        api.signals.connect_slot(api.signals.EDITOR_CREATED,
                                 self._on_editor_created)
        api.signals.connect_slot(api.signals.CURRENT_EDITOR_CHANGED,
                                 self._update_edit_actions_state)
        path = api.project.get_current_project()
        self._main_project = Project(path,
                                     ropefolder=api.project.FOLDER,
                                     fscommands=FileSystemCommands())
        self._main_project.validate()
        api.signals.connect_slot(api.signals.DOCUMENT_SAVED,
                                 self._on_document_saved)

    def close(self):
        self._main_project.close()

    def rename(self):
        """
        Renames word under cursor.
        """
        editor = api.editor.get_current_editor()
        if editor is None:
            return
        editor.file.save()
        assert isinstance(editor, PyCodeEdit)
        module = libutils.path_to_resource(self._main_project,
                                           editor.file.path)
        self._main_project.validate()
        cursor_position = self._get_real_position(
            editor.textCursor().position())
        try:
            renamer = Rename(self._main_project, module, cursor_position)
        except RopeError:
            return
        if not renamer.get_old_name():
            return
        preview, replacement = DlgRope.rename(self.main_window,
                                              renamer.get_old_name())
        if preview is None and replacement is None:
            return
        multiproj = self._has_multiple_projects()
        other_projects = self._get_other_projects()
        main_project = self._main_project
        self._preview = preview
        api.tasks.start(_('Refactoring: rename'),
                        rename_symbol,
                        self._on_changes_available,
                        args=(main_project, multiproj, other_projects,
                              editor.file.path, cursor_position, replacement),
                        cancellable=True,
                        use_thread=True)

    def organise_imports(self):
        editor = api.editor.get_current_editor()
        api.editor.save_all_editors()
        if editor is None:
            return
        self._preview = True
        file_path = editor.file.path
        project = self.get_project_for_file(file_path)
        if project:
            api.tasks.start(_('Refactoring: organize imports'),
                            organize_imports,
                            self._on_changes_available,
                            args=(project, file_path),
                            cancellable=True,
                            use_thread=True)

    def extract_method(self):
        """
        Extracts a method from the selected text (if possible, otherwise a
        warning message will appear).
        """
        api.editor.save_all_editors()
        self._main_project.validate()
        editor = api.editor.get_current_editor()
        if editor is None or not editor.textCursor().hasSelection():
            return
        editor.file.save()
        if not editor.textCursor().hasSelection():
            TextHelper(editor).select_whole_line()
        start = self._get_real_position(editor.textCursor().selectionStart())
        end = self._get_real_position(editor.textCursor().selectionEnd())
        preview, replacement = DlgRope.extract_method(self.main_window)
        if preview is None and replacement is None:
            return
        multiproj = self._has_multiple_projects()
        other_projects = self._get_other_projects()
        main_project = self._main_project
        cursor_position_start = start
        cursor_position_end = end
        replacement = replacement
        self._preview = preview
        api.tasks.start(_('Refactoring: extract method'),
                        extract_method,
                        self._on_changes_available,
                        args=(multiproj, main_project, other_projects,
                              editor.file.path, cursor_position_start,
                              cursor_position_end, replacement),
                        cancellable=True,
                        use_thread=True)

    def extract_variable(self):
        """
        Extracts a variable from the selected statement (if possible).
        """
        api.editor.save_all_editors()
        self._main_project.validate()
        editor = api.editor.get_current_editor()
        if editor is None or not editor.textCursor().hasSelection():
            return
        editor.file.save()
        if not editor.textCursor().hasSelection():
            TextHelper(editor).select_whole_line()
        start = self._get_real_position(editor.textCursor().selectionStart())
        end = self._get_real_position(editor.textCursor().selectionEnd())
        preview, replacement = DlgRope.extract_variable(self.main_window)
        if preview is None and replacement is None:
            return
        multiproj = self._has_multiple_projects()
        other_projects = self._get_other_projects()
        main_project = self._main_project
        cursor_position_start = start
        cursor_position_end = end
        replacement = replacement
        self._preview = preview
        api.tasks.start(_('Refactoring: extract variable'),
                        extract_variable,
                        self._on_changes_available,
                        args=(multiproj, main_project, other_projects,
                              editor.file.path, cursor_position_start,
                              cursor_position_end, replacement),
                        cancellable=True,
                        use_thread=True)

    def find_usages(self):
        """
        Find all usages of the word under cursor.
        """
        api.editor.save_all_editors()
        if api.editor.get_current_editor() is None:
            return
        file_path = api.editor.get_current_path()
        api.editor.save_current_editor()
        offset = self._get_real_position(
            api.editor.get_current_editor().textCursor().position())
        self._occurrence_to_search = worder.get_name_at(
            libutils.path_to_resource(self._main_project, file_path), offset)
        main_project = api.project.get_current_project()
        other_projects = self._get_other_projects(path_only=True)
        file_path = file_path
        api.tasks.start(_('Refactoring: find usages'),
                        find_usages,
                        self._on_find_usages_finished,
                        args=(main_project, other_projects, file_path, offset),
                        cancellable=True,
                        use_thread=True)

    def get_project_for_file(self, path):
        for prj in [self._main_project] + self._get_other_projects():
            p = prj.address + os.path.sep
            if p in path:
                return prj
        return None

    @staticmethod
    def clean_changes(pending_changes):
        if hasattr(pending_changes, '__iter__'):
            cleaned = []
            for prj, changeset in pending_changes:
                for change in changeset.changes:
                    if isinstance(change.resource.project, NoProject):
                        break
                else:
                    cleaned.append((prj, changeset))
            return cleaned
        return pending_changes

    def _on_changes_available(self, changes):
        if isinstance(changes, RefactoringError):
            api.events.post(RefactoringErrorEvent(changes), False)
            return
        _, self._pending_changes = changes

        self._pending_changes = self.clean_changes(self._pending_changes)

        if self._pending_changes is None:
            return
        if self._preview and self._pending_changes:
            self._create_review_dock()
            self._update_preview()
        else:
            self._refactor()

    def _on_find_usages_finished(self, results):
        if results is None:
            # todo: show no results notification
            return
        if isinstance(results, RefactoringError):
            api.events.post(RefactoringErrorEvent(results), False)
            return
        if self._occurrences_results is None:
            self._create_occurrences_dock()
        else:
            self._occurrences_dock.show()
            self._occurrences_dock.button.show()
            self._occurrences_dock.button.action.setVisible(True)
        self._occurrences_results.show_results(results,
                                               self._occurrence_to_search)

    @staticmethod
    def apply_preferences():
        for editor in api.editor.get_all_editors(True):
            if isinstance(editor, PyCodeEdit):
                actions = editor.refactoring_actions
                items = [('usages', 'Find usages', _('Find usages'), 'Alt+F7'),
                         ('rename', 'Refactor: rename', _('Refactor: rename'),
                          'Shift+F6'),
                         ('extract_method', 'Refactor: extract method',
                          _('Refactor: extract method'), 'Ctrl+Alt+M'),
                         ('extract_var', 'Refactor: extract variable',
                          _('Refactor: extract variable'), 'Ctrl+Alt+V'),
                         ('imports', 'Refactor: organize imports',
                          _('Refactor: organize imports'), 'Alt+F8')]
                for id, name, text, default in items:
                    actions[id].setShortcut(
                        api.shortcuts.get(name, text, default))

                actions['extract_var'].setIcon(special_icons.variable_icon())

    def create_refactor_menu(self, editor):
        """
        Creates the refactor menu; this menu will appear in the menu bar of the
        main window and in the context menu of any python editor.
        """
        mnu_refactor = QtWidgets.QMenu(editor)
        mnu_refactor.setTitle(_('Refactor'))
        mnu_refactor.setIcon(QtGui.QIcon.fromTheme('edit-rename'))

        action_find_usages = mnu_refactor.addAction(_('Find usages'))
        action_find_usages.setToolTip(
            _('Find occurrences of symbol under cursor'))
        action_find_usages.setIcon(QtGui.QIcon.fromTheme('edit-find'))
        action_find_usages.setShortcut(
            api.shortcuts.get('Find usages', _('Find usages'), 'Alt+F7'))
        action_find_usages.triggered.connect(self.find_usages)

        # Rename
        action_rename = mnu_refactor.addAction(_('Rename'))
        action_rename.setToolTip(_('Rename symnbol under cursor'))
        action_rename.setIcon(QtGui.QIcon.fromTheme('edit-find-replace'))
        action_rename.setShortcut(
            api.shortcuts.get('Refactor: rename', _('Refactor: rename'),
                              'Shift+F6'))
        action_rename.triggered.connect(self.rename)

        # Extract variable
        action_extract_var = mnu_refactor.addAction(_('Extract variable'))
        action_extract_var.setToolTip(
            _('Extract variable (a statement must be selected)'))
        action_extract_var.setIcon(special_icons.variable_icon())
        action_extract_var.setShortcut(
            api.shortcuts.get('Refactor: extract variable',
                              _('Refactor: extract variable'), 'Ctrl+Alt+V'))
        action_extract_var.triggered.connect(self.extract_variable)

        # Extract method
        action_extract_method = mnu_refactor.addAction(_('Extract method'))
        action_extract_method.setToolTip(
            _('Extract method (some statements must be selected)'))
        action_extract_method.setIcon(special_icons.function_icon())
        action_extract_method.setShortcut(
            api.shortcuts.get('Refactor: extract method',
                              _('Refactor: extract method'), 'Ctrl+Alt+M'))
        action_extract_method.triggered.connect(self.extract_method)

        mnu_refactor.addSeparator()

        action_organize_imports = mnu_refactor.addAction(_('Organize imports'))
        action_organize_imports.setToolTip(
            _('Organize top level imports (sort, remove unused,...)'))
        action_organize_imports.setIcon(special_icons.namespace_icon())
        action_organize_imports.setShortcut(
            api.shortcuts.get('Refactor: organize imports',
                              _('Refactor: organize imports'), 'Alt+F8'))
        action_organize_imports.triggered.connect(self.organise_imports)

        actions = {
            'usages': action_find_usages,
            'rename': action_rename,
            'extract_method': action_extract_method,
            'extract_var': action_extract_var,
            'imports': action_organize_imports
        }

        return mnu_refactor, actions

    def _on_current_project_changed(self, path):
        """
        Changes the active rope project when the current project changed in
        the IDE.
        :param path: Path of the new project.
        """
        self._main_project.close()
        self._main_project = Project(path,
                                     ropefolder=api.project.FOLDER,
                                     fscommands=FileSystemCommands())
        self._main_project.validate()

    def _on_editor_created(self, editor):
        """
        Adds the refactor menu to the editor that have just been created.
        :param editor: editor to modify.
        """
        if isinstance(editor, PyCodeEdit):
            sep = QtWidgets.QAction(editor)
            sep.setSeparator(True)
            menu, actions = self.create_refactor_menu(editor)
            editor.insert_action(menu.menuAction(), 0)
            editor.refactoring_actions = actions
            editor.insert_action(sep, 1)
            editor.cursorPositionChanged.connect(
                self._update_edit_actions_state)

    @staticmethod
    def _update_edit_actions_state(editor=None):
        if editor is None:
            editor = api.editor.get_current_editor()
        if isinstance(editor, PyCodeEdit):
            flg = bool(
                TextHelper(editor).word_under_cursor(
                    select_whole_word=True).selectedText())
            try:
                editor.refactoring_actions
            except AttributeError:
                return
            else:
                editor.refactoring_actions['usages'].setEnabled(flg)
                editor.refactoring_actions['rename'].setEnabled(flg)
                flg = editor.textCursor().hasSelection()
                editor.refactoring_actions['extract_method'].setEnabled(flg)
                editor.refactoring_actions['extract_var'].setEnabled(flg)

    def _create_occurrences_dock(self):
        """
        Creates the dock widget that shows all the occurrences of a python
        name.
        """
        self._occurrences_widget = QtWidgets.QWidget()
        vlayout = QtWidgets.QVBoxLayout()
        # buttons
        self._setup_occurrences_buttons(vlayout)
        self._occurrences_results = FindResultsWidget(term='usages')
        self._occurrences_results.itemActivated.connect(
            self._on_occurrence_activated)
        vlayout.addWidget(self._occurrences_results)
        self._occurrences_widget.setLayout(vlayout)
        # Dock widget
        self._occurrences_dock = api.window.add_dock_widget(
            self._occurrences_widget, _('Find usages'),
            QtGui.QIcon.fromTheme('edit-find'), QtCore.Qt.BottomDockWidgetArea)

    @staticmethod
    def _on_occurrence_activated(item):
        assert isinstance(item, QtWidgets.QTreeWidgetItem)
        data = item.data(0, QtCore.Qt.UserRole)
        try:
            l = data['line']
        except TypeError:
            return  # file item or root item
        l = data['line']
        start = data['start']
        lenght = data['end'] - start
        if data is not None:
            # open editor and go to line/column
            editor = api.editor.open_file(data['path'], data['line'],
                                          data['start'])
            if editor is None:
                return
            # select text
            helper = TextHelper(editor)
            cursor = helper.select_lines(start=l, end=l)
            cursor.movePosition(cursor.StartOfBlock)
            cursor.movePosition(cursor.Right, cursor.MoveAnchor, start)
            cursor.movePosition(cursor.Right, cursor.KeepAnchor, lenght)
            editor.setTextCursor(cursor)

    def _setup_occurrences_buttons(self, vlayout):
        """
        Creates the occurrences dock widget buttons
        :param vlayout: main layout
        """
        buttons = QtWidgets.QWidget()
        buttons_layout = QtWidgets.QHBoxLayout()
        buttons_layout.setContentsMargins(0, 0, 0, 0)
        # Close
        bt = QtWidgets.QPushButton()
        bt.setText(_('Close'))
        bt.clicked.connect(self._remove_occurrences_dock)
        buttons_layout.addWidget(bt)
        # Spacer
        buttons_layout.addSpacerItem(
            QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding))
        buttons.setLayout(buttons_layout)
        vlayout.addWidget(buttons)

    def _create_review_dock(self):
        """
        Creates the dock widget that shows the refactor diff.
        """
        if self._review_widget:
            self._preview_dock.show()
            self._preview_dock.button.show()
            self._preview_dock.button.action.setVisible(True)
            return
        self._review_widget = QtWidgets.QWidget()
        vlayout = QtWidgets.QVBoxLayout()
        # buttons
        bt_refactor = self._setup_review_buttons(vlayout)
        # Diff viewer
        self._viewer = DiffViewer()
        vlayout.addWidget(self._viewer)
        self._review_widget.setLayout(vlayout)

        # Dock widget
        self._preview_dock = api.window.add_dock_widget(
            self._review_widget, _('Review'),
            QtGui.QIcon.fromTheme('edit-find'), QtCore.Qt.BottomDockWidgetArea)
        bt_refactor.setFocus()

    def _update_preview(self):
        try:
            texts = []
            for prj, changes in self._pending_changes:
                lines = ''
                for subchanges in changes.changes:
                    resource = subchanges.get_changed_resources()[0]
                    resource_path = resource.real_path
                    if prj.address + os.sep in resource_path:
                        desc = subchanges.get_description()
                        lines += desc
                if lines:
                    texts.append('%s\n\n*** Project: %s\n\n%s\n' %
                                 (str(changes), prj.address, lines))
            self._viewer.setPlainText('\n'.join(texts))
        except TypeError:
            # not multiproj, one single change set
            self._viewer.setPlainText(self._pending_changes.get_description())

    def _setup_review_buttons(self, vlayout):
        """
        Creates the buttons of the preview dock widget
        :param vlayout: main layout
        """
        buttons = QtWidgets.QWidget()
        buttons_layout = QtWidgets.QHBoxLayout()
        buttons_layout.setContentsMargins(0, 0, 0, 0)
        bt_refactor = bt = QtWidgets.QPushButton()
        bt.setText(_('Refactor'))
        bt.clicked.connect(self._refactor)
        buttons_layout.addWidget(bt)
        # Close
        bt = QtWidgets.QPushButton()
        bt.setText(_('Cancel'))
        bt.clicked.connect(self._remove_preview_dock)
        buttons_layout.addWidget(bt)
        # Spacer
        buttons_layout.addSpacerItem(
            QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding))
        buttons.setLayout(buttons_layout)
        vlayout.addWidget(buttons)

        return bt_refactor

    def _remove_preview_dock(self):
        """
        Removes the preview dock widget
        """
        if self._preview_dock is not None:
            self._preview_dock.hide()
            self._preview_dock.button.hide()
            self._preview_dock.button.action.setVisible(False)

    def _remove_occurrences_dock(self):
        """
        Removes the occurrences dock widget.
        """
        if self._occurrences_dock is not None:
            self._occurrences_dock.hide()
            self._occurrences_dock.button.hide()
            self._occurrences_dock.button.action.setVisible(False)

    def _refactor(self):
        """
        Performs the refactoring.
        """
        main_project = self._main_project
        multiproj = self._has_multiple_projects()
        pending_changes = self._pending_changes
        api.tasks.start(_('Refactoring: apply pending changes'),
                        apply_pending_changes,
                        self._on_refactoring_finished,
                        args=(main_project, multiproj, pending_changes),
                        use_thread=True)

    def _on_refactoring_finished(self, ret_val):
        self._remove_preview_dock()
        if ret_val is not True:
            api.events.post(RefactoringErrorEvent(ret_val), False)

    @staticmethod
    def _get_other_projects(path_only=False):
        """
        Gets the list of secondary projects (all except current).
        """
        projects = []
        current = api.project.get_current_project()
        for path in api.project.get_projects():
            if path == current:
                continue
            if not path_only:
                prj = Project(path,
                              ropefolder=api.project.FOLDER,
                              fscommands=FileSystemCommands())
                prj.validate()
            else:
                prj = path
            projects.append(prj)
        return projects

    @staticmethod
    def _has_multiple_projects():
        """
        Checks whether multiple project have been opened in the main window.
        :return: True if window has multiple project, False if window only has
                 one project.
        """
        return len(api.project.get_projects()) > 1

    @staticmethod
    def _on_document_saved(path, old_content):
        if not path:
            return
        project = None
        for project in api.project.get_projects():
            prj_path = project + os.sep
            if prj_path in path:
                project = Project(prj_path,
                                  ropefolder=api.project.FOLDER,
                                  fscommands=FileSystemCommands())
                break
        if project:
            if path.endswith('_rc.py'):
                return
            api.tasks.start(_('Refactoring: reporting changes'),
                            report_changes,
                            None,
                            args=(project, path, old_content),
                            use_thread=False)

    @staticmethod
    def _get_real_position(position):
        """
        Gets the real cursor position (there might be a difference between
        editor content and file system content because of clean_trailing
        whitespaces on save). This function will converts the absolute position
        into a line/column pair and use this info to get the position in the
        file.
        """
        tc = api.editor.get_current_editor().textCursor()
        tc.setPosition(position)
        l = tc.blockNumber()
        c = tc.positionInBlock()
        e = api.editor.get_current_editor().file.encoding
        path = api.editor.get_current_editor().file.path
        try:
            with open(path, 'r', encoding=e) as f:
                lines = f.readlines()
        except OSError:
            _logger().exception('failed to read file %s', path)
            lines = []
        real_pos = 0
        for i, line in enumerate(lines):
            if i == l:
                real_pos += c
                break
            else:
                real_pos += len(line)
        return real_pos
예제 #21
0
class CodimensionProject( QObject ):
    " Provides codimension project singleton facility "

    # Constants for the projectChanged signal
    CompleteProject = 0     # It is a completely new project
    Properties      = 1     # Project properties were updated

    projectChanged = pyqtSignal( int )
    fsChanged = pyqtSignal( list )

    def __init__( self ):
        QObject.__init__( self )

        self.__dirWatcher = None
        self.__formatOK = True

        # Avoid pylint complains
        self.fileName = ""
        self.userProjectDir = ""    # Directory in ~/.codimension/uuidNN/
        self.filesList = set()

        self.scriptName = ""        # Script to run the project
        self.creationDate = ""
        self.author = ""
        self.license = ""
        self.copyright = ""
        self.version = ""
        self.email = ""
        self.description = ""
        self.uuid = ""

        self.__ropeProject = None
        self.__ropeSourceDirs = []

        # Coming from separate files from ~/.codimension/uuidN/
        self.todos = []
        self.bookmarks = []
        self.runParamsCache = RunParametersCache()
        self.topLevelDirs = []
        self.findHistory = []
        self.findNameHistory = []
        self.findFileHistory = []
        self.replaceHistory = []
        self.tabsStatus = []

        self.findFilesWhat = []
        self.findFilesDirs = []
        self.findFilesMasks = []

        self.findClassHistory = []
        self.findFuncHistory = []
        self.findGlobalHistory = []

        self.recentFiles = []
        self.importDirs = []
        self.fileBrowserPaths = []

        self.ignoredExcpt = []
        self.breakpoints = []
        self.watchpoints = []

        # Precompile the exclude filters
        self.__excludeFilter = []
        for flt in Settings().projectFilesFilters:
            self.__excludeFilter.append( re.compile( flt ) )

        return

    def shouldExclude( self, name ):
        " Tests if a file must be excluded "
        for excl in self.__excludeFilter:
            if excl.match( name ):
                return True
        return False

    def __resetValues( self ):
        """ Initializes or resets all the project members """

        # Empty file name means that the project has not been loaded or
        # created. This must be an absolute path.
        self.fileName = ""
        self.userProjectDir = ""

        # Generated having the project dir Full paths are stored.
        # The set holds all files and directories. The dirs end with os.path.sep
        self.filesList = set()

        self.scriptName = ""
        self.creationDate = ""
        self.author = ""
        self.license = ""
        self.copyright = ""
        self.version = ""
        self.email = ""
        self.description = ""
        self.uuid = ""

        # Coming from separate files from ~/.codimension/uuidN/
        self.todos = []
        self.bookmarks = []
        self.runParamsCache = RunParametersCache()
        self.topLevelDirs = []
        self.findHistory = []
        self.findNameHistory = []
        self.findFileHistory = []
        self.replaceHistory = []
        self.tabsStatus = []

        self.findFilesWhat = []
        self.findFilesDirs = []
        self.findFilesMasks = []

        self.findClassHistory = []
        self.findFuncHistory = []
        self.findGlobalHistory = []

        self.recentFiles = []
        self.importDirs = []
        self.fileBrowserPaths = []

        self.ignoredExcpt = []
        self.breakpoints = []
        self.watchpoints = []

        # Reset the dir watchers if so
        if self.__dirWatcher is not None:
            del self.__dirWatcher
        self.__dirWatcher = None
        return

    def createNew( self, fileName, scriptName, importDirs, author, lic,
                   copyRight, description, creationDate, version, email ):
        " Creates a new project "

        # Try to create the user project directory
        projectUuid = str( uuid.uuid1() )
        userProjectDir = settingsDir + projectUuid + sep
        if not os.path.exists( userProjectDir ):
            try:
                os.mkdir( userProjectDir )
            except:
                logging.error( "Cannot create user project directory: " +
                               self.userProjectDir + ". Please check the "
                               "available disk space and re-create the "
                               "project." )
                raise
        else:
            logging.warning( "The user project directory existed! "
                             "The content will be overwritten." )
            self.__removeProjectFiles( userProjectDir )

        # Basic pre-requisites are met. We can reset the current project
        self.__resetValues()

        self.fileName = str( fileName )
        self.importDirs = importDirs
        self.scriptName = scriptName
        self.creationDate = creationDate
        self.author = author
        self.license = lic
        self.copyright = copyRight
        self.version = version
        self.email = email
        self.description = description
        self.uuid = projectUuid
        self.userProjectDir = userProjectDir

        self.__createProjectFile()  # ~/.codimension/uuidNN/project

        self.__generateFilesList()

        self.saveProject()

        # Update the watcher
        self.__dirWatcher = Watcher( Settings().projectFilesFilters,
                                     self.getProjectDir() )
        self.__dirWatcher.fsChanged.connect( self.onFSChanged )

        self.__createRopeProject()
        self.projectChanged.emit( self.CompleteProject )
        return

    @staticmethod
    def __safeRemove( path ):
        " Safe file removal "
        try:
            os.remove( path )
        except:
            return

    def __removeProjectFiles( self, userProjectDir ):
        " Removes user project files "

        self.__safeRemove( userProjectDir + "project" )
        self.__safeRemove( userProjectDir + "bookmarks" )
        self.__safeRemove( userProjectDir + "todos" )
        self.__safeRemove( userProjectDir + "searchhistory" )
        self.__safeRemove( userProjectDir + "topleveldirs" )
        self.__safeRemove( userProjectDir + "tabsstatus" )
        self.__safeRemove( userProjectDir + "findinfiles" )
        self.__safeRemove( userProjectDir + "recentfiles" )
        self.__safeRemove( userProjectDir + "filebrowser" )
        self.__safeRemove( userProjectDir + "ignoredexcpt" )
        self.__safeRemove( userProjectDir + "breakpoints" )
        self.__safeRemove( userProjectDir + "watchpoints" )
        return

    def __createProjectFile( self ):
        " Helper function to create the user project file "
        try:
            f = open( self.userProjectDir + "project", "w" )
            f.write( self.fileName )
            f.close()
        except:
            return

    def saveProject( self ):
        " Writes all the settings into the file "
        if not self.isLoaded():
            return

        # Project properties part
        propertiesPart = "[properties]\n" \
                         "scriptname=" + self.scriptName + "\n" \
                         "creationdate=" + self.creationDate + "\n" \
                         "author=" + self.author + "\n" \
                         "license=" + self.license + "\n" \
                         "copyright=" + self.copyright + "\n" \
                         "description=" + \
                            self.description.replace( '\n', '<CR><LF>' ) + \
                            "\n" \
                         "version=" + self.version + "\n" \
                         "email=" + self.email + "\n" \
                         "uuid=" + self.uuid + "\n"

        # It could be another user project file without write permissions
        skipProjectFile = False
        if os.path.exists( self.fileName ):
            if not os.access( self.fileName, os.W_OK ):
                skipProjectFile = True
        else:
            if not os.access( os.path.dirname( self.fileName ), os.W_OK ):
                skipProjectFile = True

        if not skipProjectFile:
            f = open( self.fileName, "w" )
            self.__writeHeader( f )
            self.__writeList( f, "importdirs", "dir", self.importDirs )
            f.write( propertiesPart + "\n\n\n" )
            f.close()

        self.serializeRunParameters()
        self.__saveTopLevelDirs()
        self.__saveSearchHistory()
        self.__saveTabsStatus()
        self.__saveFindFiles()
        self.__saveFindObjects()
        self.__saveRecentFiles()
        self.__saveIgnoredExcpt()
        self.__saveBreakpoints()
        self.__saveWatchpoints()

        self.__formatOK = True
        return

    def serializeRunParameters( self ):
        " Saves the run parameters cache "
        self.runParamsCache.serialize( self.userProjectDir + "runparamscache" )
        return

    def __saveTabsStatus( self ):
        " Helper to save tabs status "
        if self.isLoaded():
            f = open( self.userProjectDir + "tabsstatus", "w" )
            self.__writeHeader( f )
            self.__writeList( f, "tabsstatus", "tab", self.tabsStatus )
            f.close()
        return

    def __saveSearchHistory( self ):
        " Helper to save the project search history "
        if self.isLoaded():
            f = open( self.userProjectDir + "searchhistory", "w" )
            self.__writeHeader( f )
            self.__writeList( f, "findhistory", "find",
                              self.findHistory )
            self.__writeList( f, "replacehistory", "replace",
                              self.replaceHistory )
            self.__writeList( f, "findnamehistory", "find",
                              self.findNameHistory )
            self.__writeList( f, "findfilehistory", "find",
                              self.findFileHistory )
            f.close()
        return

    def __saveTopLevelDirs( self ):
        " Helper to save the project top level dirs "
        if self.isLoaded():
            f = open( self.userProjectDir + "topleveldirs", "w" )
            self.__writeHeader( f )
            self.__writeList( f, "topleveldirs", "dir", self.topLevelDirs )
            f.close()
        return

    def __saveFindFiles( self ):
        " Helper to save the find in files history "
        if self.isLoaded():
            f = open( self.userProjectDir + "findinfiles", "w" )
            self.__writeHeader( f )
            self.__writeList( f, "whathistory", "what", self.findFilesWhat )
            self.__writeList( f, "dirhistory", "dir", self.findFilesDirs )
            self.__writeList( f, "maskhistory", "mask", self.findFilesMasks )
            f.close()
        return

    def __saveFindObjects( self ):
        " Helper to save find objects history "
        if self.isLoaded():
            f = open( self.userProjectDir + "findobjects", "w" )
            self.__writeHeader( f )
            self.__writeList( f, "classhistory", "class",
                              self.findClassHistory )
            self.__writeList( f, "funchistory", "func",
                              self.findFuncHistory )
            self.__writeList( f, "globalhistory", "global",
                              self.findGlobalHistory )
            f.close()
        return

    def __saveSectionToFile( self, fileName, sectionName, itemName, values ):
        " Saves the given values into a file "
        if self.isLoaded():
            f = open( self.userProjectDir + fileName, "w" )
            self.__writeHeader( f )
            self.__writeList( f, sectionName, itemName, values )
            f.close()
        return

    def __saveRecentFiles( self ):
        " Helper to save recent files list "
        self.__saveSectionToFile( "recentfiles", "recentfiles", "file",
                                  self.recentFiles )
        return

    def __saveIgnoredExcpt( self ):
        " Helper to save ignored exceptions list "
        self.__saveSectionToFile( "ignoredexcpt", "ignoredexcpt",
                                  "excpttype", self.ignoredExcpt )
        return

    def __saveBreakpoints( self ):
        " Helper to save breakpoints "
        self.__saveSectionToFile( "breakpoints", "breakpoints",
                                  "bpoint", self.breakpoints )
        return

    def __saveWatchpoints( self ):
        " helper to save watchpoints "
        self.__saveSectionToFile( "watchpoints", "watchpoints",
                                  "wpoint", self.watchpoints )
        return

    @staticmethod
    def __writeHeader( fileObj ):
        " Helper to write a header with a warning "
        fileObj.write( "#\n"
                       "# Generated automatically.\n"
                       "# Don't edit it manually unless you "
                       "know what you are doing.\n"
                       "#\n\n" )
        return

    @staticmethod
    def __writeList( fileObj, header, prefix, items ):
        " Helper to write a list "
        fileObj.write( "[" + header + "]\n" )
        index = 0
        for item in items:
            fileObj.write( prefix + str( index ) + "=" + item + "\n" )
            index += 1
        fileObj.write( "\n" )
        return

    def __getStr( self, conf, sec, key, default ):
        " Helper to read a config value "
        try:
            return conf.get( sec, key ).strip()
        except:
            self.__formatOK = False
        return default

    def loadProject( self, projectFile ):
        """ Loads a project from the given file """

        absPath = os.path.abspath( projectFile )
        if not os.path.exists( absPath ):
            raise Exception( "Cannot open project file " + projectFile )
        if not absPath.endswith( ".cdm" ):
            raise Exception( "Unexpected project file extension. "
                             "Expected: .cdm" )

        config = ConfigParser.ConfigParser()

        try:
            config.read( absPath )
        except:
            # Bad error - cannot load project file at all
            config = None
            raise Exception( "Bad project file" )


        self.__resetValues()
        self.fileName = str( absPath )

        # Properties part
        self.scriptName = self.__getStr( config, 'properties',
                                                 'scriptname', '' )
        self.creationDate = self.__getStr( config, 'properties',
                                                   'creationdate', '' )
        self.author = self.__getStr( config, 'properties', 'author', '' )
        self.license = self.__getStr( config, 'properties', 'license', '' )
        self.copyright = self.__getStr( config, 'properties', 'copyright', '' )
        self.description = self.__getStr( config, 'properties', 'description',
                                          '' ).replace( '<CR><LF>', '\n' )
        self.version = self.__getStr( config, 'properties', 'version', '' )
        self.email = self.__getStr( config, 'properties', 'email', '' )
        self.uuid = self.__getStr( config, 'properties', 'uuid', '' )
        if self.uuid == "":
            logging.warning( "Project file does not have UUID. "
                             "Re-generate it..." )
            self.uuid = str( uuid.uuid1() )
        self.userProjectDir = settingsDir + self.uuid + sep
        if not os.path.exists( self.userProjectDir ):
            os.mkdir( self.userProjectDir )

        # import dirs part
        index = 0
        try:
            while True:
                dirName = config.get( 'importdirs',
                                      'dir' + str( index ) ).strip()
                index += 1
                if os.path.isabs( dirName ):
                    absPath = dirName
                else:
                    absPath = self.getProjectDir() + dirName
                if not os.path.exists( absPath ):
                    logging.error( "Codimension project: cannot find "
                                   "import directory: " + dirName )
                elif not isdir( absPath ):
                    logging.error( "Codimension project: the import path: " +
                                   dirName + " is not a directory" )
                self.importDirs.append( dirName )
        except ConfigParser.NoSectionError:
            self.__formatOK = False
        except ConfigParser.NoOptionError:
            # just continue
            pass
        except:
            self.__formatOK = False

        config = None

        # Read the other config files
        self.__loadTopLevelDirs()
        self.__loadSearchHistory()
        self.__loadTabsStatus()
        self.__loadFindFiles()
        self.__loadFindObjects()
        self.__loadRecentFiles()
        self.__loadProjectBrowserExpandedDirs()
        self.__loadIgnoredExceptions()
        self.__loadBreakpoints()
        self.__loadWatchpoints()

        # The project might have been moved...
        self.__createProjectFile()  # ~/.codimension/uuidNN/project
        self.__generateFilesList()

        if os.path.exists( self.userProjectDir + "runparamscache" ):
            self.runParamsCache.deserialize( self.userProjectDir +
                                             "runparamscache" )

        if not self.__formatOK:
            logging.info( "Project files are broken or absent. "
                          "Overwriting the project files." )
            self.saveProject()

        # Update the recent list
        Settings().addRecentProject( self.fileName )

        # Setup the new watcher
        self.__dirWatcher = Watcher( Settings().projectFilesFilters,
                                     self.getProjectDir() )
        self.__dirWatcher.fsChanged.connect( self.onFSChanged )

        self.__createRopeProject()
        self.projectChanged.emit( self.CompleteProject )
        self.emit( SIGNAL( 'restoreProjectExpandedDirs' ) )
        return

    def getImportDirsAsAbsolutePaths( self ):
        " Provides a list of import dirs as absolute paths "
        result = []
        for path in self.importDirs:
            if os.path.isabs( path ):
                result.append( path )
            else:
                result.append( self.getProjectDir() + path )
        return result

    def __getImportDirsForRope( self ):
        " Provides the list of import dirs for the rope library "
        result = []
        for path in self.importDirs:
            if not os.path.isabs( path ):
                # The only relative paths can be accepted by rope
                result.append( path )
        result.sort()
        return result

    def getRopeProject( self ):
        " Provides the rope project "
        if self.__getImportDirsForRope() != self.__ropeSourceDirs:
            # The user changed the project import dirs, so let's
            # re-create the project
            self.__createRopeProject()
        return self.__ropeProject

    def validateRopeProject( self, fileName ):
        " Validates the rope project "
        # Currently rope can validate a directory only so the argument
        # is ignored
        self.__ropeProject.validate()
        return

    def __createRopeProject( self ):
        " Creates a rope library project "
        if self.__ropeProject is not None:
            self.__ropeProject.close()
            self.__ropeProject = None
            self.__ropeSourceDirs = []

        # Deal with import dirs and preferences first
        self.__ropeSourceDirs = self.__getImportDirsForRope()
        prefs = copy.deepcopy( ropePreferences )
        if len( self.__ropeSourceDirs ) != 0:
            prefs[ "source_folders" ] = self.__ropeSourceDirs

        # Rope folder is default here, so it will be created
        self.__ropeProject = RopeProject( self.getProjectDir(),
                                          **prefs )
        self.__ropeProject.validate( self.__ropeProject.root )
        return

    def onFSChanged( self, items ):
        " Triggered when the watcher detects changes "
##        report = "REPORT: "
##        projectItems = []
        for item in items:
            item = str( item )

#            if not islink( item ):
#                realPath = realpath( item[ 1: ] )
#                isDir = item.endswith( sep )
#                if isDir:
#                    if self.isProjectDir( realPath + sep ):
#                        item = item[ 0 ] + realPath + sep
#                else:
#                    if self.isProjectFile( realPath + sep ):
#                        item = item[ 0 ] + realPath

#            projectItems.append( item )
##            report += " " + item
            try:
                if item.startswith( '+' ):
                    self.filesList.add( item[ 1: ] )
                else:
                    self.filesList.remove( item[ 1: ] )
##                projectItems.append( item )
            except:
#                print "EXCEPTION for '" + item + "'"
                pass
#        print "'" + report + "'"

        self.fsChanged.emit( items )
#        self.__dirWatcher.debug()
        return

    def __loadTabsStatus( self ):
        " Loads the last tabs status "
        configFile = self.userProjectDir + "tabsstatus"
        if not os.path.exists( configFile ):
            logging.info( "Cannot find tabsstatus project file. "
                          "Expected here: " + configFile )
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( configFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read tabsstatus project file "
                             "from here: " + configFile )
            return

        # tabs part
        self.tabsStatus = self.__loadListSection( config, 'tabsstatus', 'tab' )

        config = None
        return

    def __loadProjectBrowserExpandedDirs( self ):
        " Loads the project browser expanded dirs "
        configFile = self.userProjectDir + "filebrowser"
        if not os.path.exists( configFile ):
            self.fileBrowserPaths = []
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( configFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.fileBrowserPaths = []
            return

        # dirs part
        self.fileBrowserPaths = self.__loadListSection( config, 'filebrowser',
                                                        'path' )
        config = None
        return

    def __loadTopLevelDirs( self ):
        " Loads the top level dirs "
        configFile = self.userProjectDir + "topleveldirs"
        if not os.path.exists( configFile ):
            logging.info( "Cannot find topleveldirs project file. "
                          "Expected here: " + configFile )
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( configFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read topleveldirs project file "
                             "from here: " + configFile )
            return

        # dirs part
        self.topLevelDirs = self.__loadListSection(
                config, 'topleveldirs', 'dir' )

        config = None
        return

    def __loadSearchHistory( self ):
        " Loads the search history file content "
        confFile = self.userProjectDir + "searchhistory"
        if not os.path.exists( confFile ):
            logging.info( "Cannot find searchhistory project file. "
                          "Expected here: " + confFile )
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( confFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read searchhistory project file "
                             "from here: " + confFile )
            return

        # find part
        self.findHistory = self.__loadListSection(
                config, 'findhistory', 'find' )
        self.findNameHistory = self.__loadListSection(
                config, 'findnamehistory', 'find' )
        self.findFileHistory = self.__loadListSection(
                config, 'findfilehistory', 'find' )

        # replace part
        self.replaceHistory = self.__loadListSection(
                config, 'replacehistory', 'replace' )

        config = None
        return

    def __loadFindObjects( self ):
        " Loads the find objects history "
        confFile = self.userProjectDir + "findobjects"
        if not os.path.exists( confFile ):
            logging.info( "Cannot find findobjects project file. "
                          "Expected here: " + confFile )
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( confFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read findobjects project file "
                             "from here: " + confFile )
            return

        self.findClassHistory = self.__loadListSection(
                config, 'classhistory', 'class' )
        self.findFuncHistory = self.__loadListSection(
                config, 'funchistory', 'func' )
        self.findGlobalHistory = self.__loadListSection(
                config, 'globalhistory', 'global' )
        config = None
        return

    def __loadFindFiles( self ):
        " Loads the find in files history "
        confFile = self.userProjectDir + "findinfiles"
        if not os.path.exists( confFile ):
            logging.info( "Cannot find findinfiles project file. "
                          "Expected here: " + confFile )
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( confFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read findinfiles project file "
                             "from here: " + confFile )
            return

        self.findFilesWhat = self.__loadListSection(
                config, 'whathistory', 'what' )
        self.findFilesDirs = self.__loadListSection(
                config, 'dirhistory', 'dir' )
        self.findFilesMasks = self.__loadListSection(
                config, 'maskhistory', 'mask' )
        config = None
        return

    def __loadRecentFiles( self ):
        " Loads the recent files list "
        confFile = self.userProjectDir + "recentfiles"
        if not os.path.exists( confFile ):
            logging.info( "Cannot find recentfiles project file. "
                          "Expected here: " + confFile )
            self.__formatOK = False
            return

        config = ConfigParser.ConfigParser()
        try:
            config.read( confFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read recentfiles project file "
                             "from here: " + confFile )
            return

        self.recentFiles = self.__loadListSection(
                config, 'recentfiles', 'file' )

        # Due to a bug there could be the same files twice in the list.
        # The difference is doubled path separator. Fix it here.
        temp = set()
        for path in self.recentFiles:
            temp.add( os.path.normpath( path ) )
        self.recentFiles = list( temp )

        config = None
        return

    def __loadSectionFromFile( self, fileName, sectionName, itemName,
                               errMessageEntity ):
        " Loads the given section from the given file "
        confFile = self.userProjectDir + fileName
        if not os.path.exists( confFile ):
            logging.info( "Cannot find " + errMessageEntity + " file. "
                          "Expected here: " + confFile )
            self.__formatOK = False
            return []

        config = ConfigParser.ConfigParser()
        try:
            config.read( confFile )
        except:
            # Bad error - cannot load project file at all
            config = None
            self.__formatOK = False
            logging.warning( "Cannot read " + errMessageEntity + " file "
                             "from here: " + confFile )
            return []

        values = self.__loadListSection( config, sectionName, itemName )
        config = None
        return values

    def __loadIgnoredExceptions( self ):
        " Loads the ignored exceptions list "
        self.ignoredExcpt = self.__loadSectionFromFile(
                                    "ignoredexcpt", "ignoredexcpt",
                                    "excpttype", "ignored exceptions" )
        return

    def __loadBreakpoints( self ):
        " Loads the project breakpoints "
        self.breakpoints = self.__loadSectionFromFile(
                                    "breakpoints", "breakpoints",
                                    "bpoint", "breakpoints" )
        return

    def __loadWatchpoints( self ):
        " Loads the project watchpoints "
        self.watchpoints = self.__loadSectionFromFile(
                                    "watchpoints", "watchpoints",
                                    "wpoint", "watchpoints" )
        return

    def __loadListSection( self, config, section, listPrefix ):
        " Loads a list off the given section from the given file "
        items = []
        index = 0
        try:
            while True:
                item = config.get( section, listPrefix + str(index) ).strip()
                index += 1
                items.append( item )
        except ConfigParser.NoSectionError:
            self.__formatOK = False
        except ConfigParser.NoOptionError:
            pass    # Just continue
        except:
            self.__formatOK = False
        return items

    def unloadProject( self, emitSignal = True ):
        """ Unloads the current project if required """
        self.emit( SIGNAL( 'projectAboutToUnload' ) )
        if self.isLoaded():
            self.__saveProjectBrowserExpandedDirs()
        self.__resetValues()
        if emitSignal:
            # No need to send a signal e.g. if IDE is closing
            self.projectChanged.emit( self.CompleteProject )
        if self.__ropeProject is not None:
            try:
                # If the project directory is read only then closing the
                # rope project generates exception
                self.__ropeProject.close()
            except:
                pass
        self.__ropeProject = None
        self.__ropeSourceDirs = []
        return

    def __saveProjectBrowserExpandedDirs( self ):
        " Saves the pathes expanded in the project browser "
        if not self.isLoaded():
            return

        try:
            f = open( self.userProjectDir + "filebrowser", "w" )
            self.__writeHeader( f )
            self.__writeList( f, "filebrowser", "path", self.fileBrowserPaths )
            f.close()
        except:
            pass
        return

    def setImportDirs( self, paths ):
        " Sets a new set of the project import dirs "
        if self.importDirs != paths:
            self.importDirs = paths
            self.saveProject()
            self.projectChanged.emit( self.Properties )
        return

    def __generateFilesList( self ):
        """ Generates the files list having the list of dirs """
        self.filesList = set()
        path = self.getProjectDir()
        self.filesList.add( path )
        self.__scanDir( path )
        return

    def __scanDir( self, path ):
        """ Recursive function to scan one dir """
        # The path is with '/' at the end
        for item in os.listdir( path ):
            if self.shouldExclude( item ):
                continue

            # Exclude symlinks if they point to the other project
            # covered pieces
            candidate = path + item
            if islink( candidate ):
                realItem = realpath( candidate )
                if isdir( realItem ):
                    if self.isProjectDir( realItem ):
                        continue
                else:
                    if self.isProjectDir( os.path.dirname( realItem ) ):
                        continue

            if isdir( candidate ):
                self.filesList.add( candidate + sep )
                self.__scanDir( candidate + sep )
                continue
            self.filesList.add( candidate )
        return

    def isProjectDir( self, path ):
        " Returns True if the path belongs to the project "
        if not self.isLoaded():
            return False
        path = realpath( str( path ) )     # it could be a symlink
        if not path.endswith( sep ):
            path += sep
        return path.startswith( self.getProjectDir() )

    def isProjectFile( self, path ):
        " Returns True if the path belongs to the project "
        if not self.isLoaded():
            return False
        return self.isProjectDir( os.path.dirname( path ) )

    def isTopLevelDir( self, path ):
        " Checks if the path is a top level dir "
        if not path.endswith( sep ):
            path += sep
        return path in self.topLevelDirs

    def addTopLevelDir( self, path ):
        " Adds the path to the top level dirs list "
        if not path.endswith( sep ):
            path += sep
        if path in self.topLevelDirs:
            logging.warning( "Top level dir " + path +
                             " is already in the list of dirs. "
                             "Ignore adding..." )
            return
        self.topLevelDirs.append( path )
        self.__saveTopLevelDirs()
        return

    def removeTopLevelDir( self, path ):
        " Removes the path from the top level dirs list "
        if not path.endswith( sep ):
            path += sep
        if path not in self.topLevelDirs:
            logging.warning( "Top level dir " + path +
                             " is not in the list of dirs. Ignore removing..." )
            return
        self.topLevelDirs.remove( path )
        self.__saveTopLevelDirs()
        return

    def setFindNameHistory( self, history ):
        " Sets the new find name history and saves it into a file "
        self.findNameHistory = history
        self.__saveSearchHistory()
        return

    def setFindFileHistory( self, history ):
        " Sets the new find file history and saves it into a file "
        self.findFileHistory = history
        self.__saveSearchHistory()
        return

    def setFindHistory( self, history ):
        " Sets the new find history and save it into a file "
        self.findHistory = history
        self.__saveSearchHistory()
        return

    def setReplaceHistory( self, whatHistory, toHistory ):
        " Sets the new replace history and save it into a file "
        self.findHistory = whatHistory
        self.replaceHistory = toHistory
        self.__saveSearchHistory()
        return

    def setTabsStatus( self, status ):
        " Sets the new tabs status and save it into a file "
        self.tabsStatus = status
        self.__saveTabsStatus()
        return

    def setFindInFilesHistory( self, what, dirs, masks ):
        " Sets the new lists and save them into a file "
        self.findFilesWhat = what
        self.findFilesDirs = dirs
        self.findFilesMasks = masks
        self.__saveFindFiles()
        return

    def setFindClassHistory( self, history ):
        " Sets the new history and saves it into a file "
        self.findClassHistory = history
        self.__saveFindObjects()
        return

    def setFindFuncHistory( self, history ):
        " Sets the new history and saves it into a file "
        self.findFuncHistory = history
        self.__saveFindObjects()
        return

    def setFindGlobalHistory( self, history ):
        " Sets the new history and saves it into a file "
        self.findGlobalHistory = history
        self.__saveFindObjects()
        return

    def updateProperties( self, scriptName, importDirs, creationDate, author,
                          lic, copy_right, version,
                          email, description ):
        " Updates the project properties "

        if self.scriptName == scriptName and \
           self.creationDate == creationDate and \
           self.author == author and \
           self.license == lic and \
           self.copyright == copy_right and \
           self.version == version and \
           self.email == email and \
           self.description == description and \
           self.importDirs == importDirs:
            # No real changes
            return

        self.importDirs = importDirs
        self.scriptName = scriptName
        self.creationDate = creationDate
        self.author = author
        self.license = lic
        self.copyright = copy_right
        self.version = version
        self.email = email
        self.description = description
        self.saveProject()
        self.projectChanged.emit( self.Properties )
        return

    def onProjectFileUpdated( self ):
        " Called when a project file is updated via direct editing "

        scriptName, importDirs, \
        creationDate, author, \
        lic, copy_right, \
        description, version, \
        email, projectUuid = getProjectProperties( self.fileName )

        self.importDirs = importDirs
        self.scriptName = scriptName
        self.creationDate = creationDate
        self.author = author
        self.license = lic
        self.copyright = copy_right
        self.version = version
        self.email = email
        self.description = description

        # no need to save, but signal just in case
        self.projectChanged.emit( self.Properties )
        return

    def isLoaded( self ):
        " returns True if a project is loaded "
        return self.fileName != ""

    def getProjectDir( self ):
        " Provides an absolute path to the project dir "
        if not self.isLoaded():
            return ""
        return os.path.dirname( realpath( self.fileName ) ) + sep

    def getProjectScript( self ):
        " Provides the project script file name "
        if not self.isLoaded():
            return ""
        if self.scriptName == "":
            return ""
        if os.path.isabs( self.scriptName ):
            return self.scriptName
        return os.path.normpath( self.getProjectDir() + self.scriptName )

    def addRecentFile( self, path ):
        " Adds a single recent file. True if a new file was inserted. "
        if path in self.recentFiles:
            self.recentFiles.remove( path )
            self.recentFiles.insert( 0, path )
            self.__saveRecentFiles()
            return False
        self.recentFiles.insert( 0, path )
        self.__saveRecentFiles()
        if len( self.recentFiles ) > 32:
            self.recentFiles = self.recentFiles[ 0 : 32 ]
        self.emit( SIGNAL( 'recentFilesChanged' ) )
        return True

    def removeRecentFile( self, path ):
        " Removes a single recent file "
        if path in self.recentFiles:
            self.recentFiles.remove( path )
            self.__saveRecentFiles()
        return

    def addExceptionFilter( self, excptType ):
        " Adds a new ignored exception type "
        if excptType not in self.ignoredExcpt:
            self.ignoredExcpt.append( excptType )
            self.__saveIgnoredExcpt()
        return

    def deleteExceptionFilter( self, excptType ):
        " Remove ignored exception type "
        if excptType in self.ignoredExcpt:
            self.ignoredExcpt.remove( excptType )
            self.__saveIgnoredExcpt()
        return

    def setExceptionFilters( self, newFilters ):
        " Sets the new filters "
        self.ignoredExcpt = newFilters
        self.__saveIgnoredExcpt()
        return

    def addBreakpoint( self, bpointStr ):
        " Adds serialized breakpoint "
        if bpointStr not in self.breakpoints:
            self.breakpoints.append( bpointStr )
            self.__saveBreakpoints()
        return

    def deleteBreakpoint( self, bpointStr ):
        " Deletes serialized breakpoint "
        if bpointStr in self.breakpoints:
            self.breakpoints.remove( bpointStr )
            self.__saveBreakpoints()
        return

    def setBreakpoints( self, bpointStrList ):
        " Sets breakpoints list "
        self.breakpoints = bpointStrList
        self.__saveBreakpoints()
        return

    def addWatchpoint( self, wpointStr ):
        " Adds serialized watchpoint "
        if wpointStr not in self.watchpoints:
            self.watchpoints.append( wpointStr )
            self.__saveWatchpoints()
        return

    def deleteWatchpoint( self, wpointStr ):
        " Deletes serialized watchpoint "
        if wpointStr in self.watchpoints:
            self.watchpoints.remove( wpointStr )
            self.__saveWatchpoints()
        return

    def setWatchpoints( self, wpointStrList ):
        " Sets watchpoints list "
        self.watchpoints = wpointStrList
        self.__saveWatchpoints()
        return
예제 #22
0
class PyRefactor(plugins.WorkspacePlugin):
    """
    Adds some refactoring capabilities to the IDE (using the rope library).

    Supported operations:
        - Rename
        - Extract method
        - Extract variable
        - Find occurrences
        - Organize imports

    """
    def activate(self):
        self._preview_dock = None
        self._occurrences_dock = None
        self._occurrences_results = None
        self._review_widget = None
        api.signals.connect_slot(api.signals.CURRENT_PROJECT_CHANGED,
                                 self._on_current_project_changed)
        api.signals.connect_slot(api.signals.EDITOR_CREATED,
                                 self._on_editor_created)
        api.signals.connect_slot(api.signals.CURRENT_EDITOR_CHANGED,
                                 self._update_edit_actions_state)
        path = api.project.get_current_project()
        self._main_project = Project(path, ropefolder=api.project.FOLDER,
                                     fscommands=FileSystemCommands())
        self._main_project.validate()
        api.signals.connect_slot(api.signals.DOCUMENT_SAVED,
                                 self._on_document_saved)

    def close(self):
        self._main_project.close()

    def rename(self):
        """
        Renames word under cursor.
        """
        editor = api.editor.get_current_editor()
        if editor is None:
            return
        editor.file.save()
        assert isinstance(editor, PyCodeEdit)
        module = libutils.path_to_resource(
            self._main_project, editor.file.path)
        self._main_project.validate()
        cursor_position = self._get_real_position(
            editor.textCursor().position())
        try:
            renamer = Rename(self._main_project, module, cursor_position)
        except RopeError:
            return
        if not renamer.get_old_name():
            return
        preview, replacement = DlgRope.rename(
            self.main_window, renamer.get_old_name())
        if preview is None and replacement is None:
            return
        multiproj = self._has_multiple_projects()
        other_projects = self._get_other_projects()
        main_project = self._main_project
        self._preview = preview
        api.tasks.start(_('Refactoring: rename'), rename_symbol,
                        self._on_changes_available,
                        args=(
                            main_project, multiproj, other_projects,
                            editor.file.path, cursor_position, replacement),
                        cancellable=True, use_thread=True)

    def organise_imports(self):
        editor = api.editor.get_current_editor()
        api.editor.save_all_editors()
        if editor is None:
            return
        self._preview = True
        file_path = editor.file.path
        project = self.get_project_for_file(file_path)
        if project:
            api.tasks.start(
                _('Refactoring: organize imports'), organize_imports,
                self._on_changes_available,
                args=(project, file_path),
                cancellable=True, use_thread=True)

    def extract_method(self):
        """
        Extracts a method from the selected text (if possible, otherwise a
        warning message will appear).
        """
        api.editor.save_all_editors()
        self._main_project.validate()
        editor = api.editor.get_current_editor()
        if editor is None or not editor.textCursor().hasSelection():
            return
        editor.file.save()
        if not editor.textCursor().hasSelection():
            TextHelper(editor).select_whole_line()
        start = self._get_real_position(editor.textCursor().selectionStart())
        end = self._get_real_position(editor.textCursor().selectionEnd())
        preview, replacement = DlgRope.extract_method(self.main_window)
        if preview is None and replacement is None:
            return
        multiproj = self._has_multiple_projects()
        other_projects = self._get_other_projects()
        main_project = self._main_project
        cursor_position_start = start
        cursor_position_end = end
        replacement = replacement
        self._preview = preview
        api.tasks.start(
            _('Refactoring: extract method'), extract_method,
            self._on_changes_available,
            args=(multiproj, main_project, other_projects, editor.file.path,
                  cursor_position_start, cursor_position_end, replacement),
            cancellable=True, use_thread=True)

    def extract_variable(self):
        """
        Extracts a variable from the selected statement (if possible).
        """
        api.editor.save_all_editors()
        self._main_project.validate()
        editor = api.editor.get_current_editor()
        if editor is None or not editor.textCursor().hasSelection():
            return
        editor.file.save()
        if not editor.textCursor().hasSelection():
            TextHelper(editor).select_whole_line()
        start = self._get_real_position(editor.textCursor().selectionStart())
        end = self._get_real_position(editor.textCursor().selectionEnd())
        preview, replacement = DlgRope.extract_variable(self.main_window)
        if preview is None and replacement is None:
            return
        multiproj = self._has_multiple_projects()
        other_projects = self._get_other_projects()
        main_project = self._main_project
        cursor_position_start = start
        cursor_position_end = end
        replacement = replacement
        self._preview = preview
        api.tasks.start(
            _('Refactoring: extract variable'), extract_variable,
            self._on_changes_available,
            args=(multiproj, main_project, other_projects, editor.file.path,
                  cursor_position_start, cursor_position_end, replacement),
            cancellable=True, use_thread=True)

    def find_usages(self):
        """
        Find all usages of the word under cursor.
        """
        api.editor.save_all_editors()
        if api.editor.get_current_editor() is None:
            return
        file_path = api.editor.get_current_path()
        api.editor.save_current_editor()
        offset = self._get_real_position(
            api.editor.get_current_editor().textCursor().position())
        self._occurrence_to_search = worder.get_name_at(
            libutils.path_to_resource(self._main_project, file_path), offset)
        main_project = api.project.get_current_project()
        other_projects = self._get_other_projects(path_only=True)
        file_path = file_path
        api.tasks.start(
            _('Refactoring: find usages'), find_usages,
            self._on_find_usages_finished,
            args=(main_project, other_projects, file_path, offset),
            cancellable=True, use_thread=True)

    def get_project_for_file(self, path):
        for prj in [self._main_project] + self._get_other_projects():
            p = prj.address + os.path.sep
            if p in path:
                return prj
        return None

    @staticmethod
    def clean_changes(pending_changes):
        if hasattr(pending_changes, '__iter__'):
            cleaned = []
            for prj, changeset in pending_changes:
                for change in changeset.changes:
                    if isinstance(change.resource.project, NoProject):
                        break
                else:
                    cleaned.append((prj, changeset))
            return cleaned
        return pending_changes

    def _on_changes_available(self, changes):
        if isinstance(changes, RefactoringError):
            api.events.post(RefactoringErrorEvent(changes), False)
            return
        _, self._pending_changes = changes

        self._pending_changes = self.clean_changes(self._pending_changes)

        if self._pending_changes is None:
            return
        if self._preview and self._pending_changes:
            self._create_review_dock()
            self._update_preview()
        else:
            self._refactor()

    def _on_find_usages_finished(self, results):
        if results is None:
            # todo: show no results notification
            return
        if isinstance(results, RefactoringError):
            api.events.post(RefactoringErrorEvent(results), False)
            return
        if self._occurrences_results is None:
            self._create_occurrences_dock()
        else:
            self._occurrences_dock.show()
            self._occurrences_dock.button.show()
            self._occurrences_dock.button.action.setVisible(True)
        self._occurrences_results.show_results(
            results, self._occurrence_to_search)

    @staticmethod
    def apply_preferences():
        for editor in api.editor.get_all_editors(True):
            if isinstance(editor, PyCodeEdit):
                actions = editor.refactoring_actions
                items = [
                    ('usages', 'Find usages', _('Find usages'), 'Alt+F7'),
                    ('rename', 'Refactor: rename', _('Refactor: rename'),
                     'Shift+F6'),
                    ('extract_method', 'Refactor: extract method',
                     _('Refactor: extract method'), 'Ctrl+Alt+M'),
                    ('extract_var', 'Refactor: extract variable',
                     _('Refactor: extract variable'), 'Ctrl+Alt+V'),
                    ('imports', 'Refactor: organize imports',
                     _('Refactor: organize imports'), 'Alt+F8')
                ]
                for id, name, text, default in items:
                    actions[id].setShortcut(api.shortcuts.get(
                        name, text, default))

                actions['extract_var'].setIcon(special_icons.variable_icon())

    def create_refactor_menu(self, editor):
        """
        Creates the refactor menu; this menu will appear in the menu bar of the
        main window and in the context menu of any python editor.
        """
        mnu_refactor = QtWidgets.QMenu(editor)
        mnu_refactor.setTitle(_('Refactor'))
        mnu_refactor.setIcon(QtGui.QIcon.fromTheme('edit-rename'))

        action_find_usages = mnu_refactor.addAction(_('Find usages'))
        action_find_usages.setToolTip(
            _('Find occurrences of symbol under cursor'))
        action_find_usages.setIcon(QtGui.QIcon.fromTheme('edit-find'))
        action_find_usages.setShortcut(
            api.shortcuts.get('Find usages', _('Find usages'), 'Alt+F7'))
        action_find_usages.triggered.connect(self.find_usages)

        # Rename
        action_rename = mnu_refactor.addAction(_('Rename'))
        action_rename.setToolTip(_('Rename symnbol under cursor'))
        action_rename.setIcon(QtGui.QIcon.fromTheme('edit-find-replace'))
        action_rename.setShortcut(
            api.shortcuts.get('Refactor: rename', _('Refactor: rename'),
                              'Shift+F6'))
        action_rename.triggered.connect(self.rename)

        # Extract variable
        action_extract_var = mnu_refactor.addAction(_('Extract variable'))
        action_extract_var.setToolTip(
            _('Extract variable (a statement must be selected)'))
        action_extract_var.setIcon(special_icons.variable_icon())
        action_extract_var.setShortcut(
            api.shortcuts.get('Refactor: extract variable',
                              _('Refactor: extract variable'), 'Ctrl+Alt+V'))
        action_extract_var.triggered.connect(self.extract_variable)

        # Extract method
        action_extract_method = mnu_refactor.addAction(_('Extract method'))
        action_extract_method.setToolTip(
            _('Extract method (some statements must be selected)'))
        action_extract_method.setIcon(special_icons.function_icon())
        action_extract_method.setShortcut(
            api.shortcuts.get('Refactor: extract method',
                              _('Refactor: extract method'), 'Ctrl+Alt+M'))
        action_extract_method.triggered.connect(self.extract_method)

        mnu_refactor.addSeparator()

        action_organize_imports = mnu_refactor.addAction(_('Organize imports'))
        action_organize_imports.setToolTip(
            _('Organize top level imports (sort, remove unused,...)'))
        action_organize_imports.setIcon(special_icons.namespace_icon())
        action_organize_imports.setShortcut(
            api.shortcuts.get('Refactor: organize imports',
                              _('Refactor: organize imports'), 'Alt+F8'))
        action_organize_imports.triggered.connect(self.organise_imports)

        actions = {
            'usages': action_find_usages,
            'rename': action_rename,
            'extract_method': action_extract_method,
            'extract_var': action_extract_var,
            'imports': action_organize_imports
        }

        return mnu_refactor, actions

    def _on_current_project_changed(self, path):
        """
        Changes the active rope project when the current project changed in
        the IDE.
        :param path: Path of the new project.
        """
        self._main_project.close()
        self._main_project = Project(path, ropefolder=api.project.FOLDER,
                                     fscommands=FileSystemCommands())
        self._main_project.validate()

    def _on_editor_created(self, editor):
        """
        Adds the refactor menu to the editor that have just been created.
        :param editor: editor to modify.
        """
        if isinstance(editor, PyCodeEdit):
            sep = QtWidgets.QAction(editor)
            sep.setSeparator(True)
            menu, actions = self.create_refactor_menu(editor)
            editor.insert_action(menu.menuAction(), 0)
            editor.refactoring_actions = actions
            editor.insert_action(sep, 1)
            editor.cursorPositionChanged.connect(
                self._update_edit_actions_state)

    @staticmethod
    def _update_edit_actions_state(editor=None):
        if editor is None:
            editor = api.editor.get_current_editor()
        if isinstance(editor, PyCodeEdit):
            flg = bool(TextHelper(editor).word_under_cursor(
                select_whole_word=True).selectedText())
            try:
                editor.refactoring_actions
            except AttributeError:
                return
            else:
                editor.refactoring_actions['usages'].setEnabled(flg)
                editor.refactoring_actions['rename'].setEnabled(flg)
                flg = editor.textCursor().hasSelection()
                editor.refactoring_actions['extract_method'].setEnabled(flg)
                editor.refactoring_actions['extract_var'].setEnabled(flg)

    def _create_occurrences_dock(self):
        """
        Creates the dock widget that shows all the occurrences of a python
        name.
        """
        self._occurrences_widget = QtWidgets.QWidget()
        vlayout = QtWidgets.QVBoxLayout()
        # buttons
        self._setup_occurrences_buttons(vlayout)
        self._occurrences_results = FindResultsWidget(term='usages')
        self._occurrences_results.itemActivated.connect(
            self._on_occurrence_activated)
        vlayout.addWidget(self._occurrences_results)
        self._occurrences_widget.setLayout(vlayout)
        # Dock widget
        self._occurrences_dock = api.window.add_dock_widget(
            self._occurrences_widget, _('Find usages'),
            QtGui.QIcon.fromTheme('edit-find'),
            QtCore.Qt.BottomDockWidgetArea)

    @staticmethod
    def _on_occurrence_activated(item):
        assert isinstance(item, QtWidgets.QTreeWidgetItem)
        data = item.data(0, QtCore.Qt.UserRole)
        try:
            l = data['line']
        except TypeError:
            return  # file item or root item
        l = data['line']
        start = data['start']
        lenght = data['end'] - start
        if data is not None:
            # open editor and go to line/column
            editor = api.editor.open_file(
                data['path'], data['line'], data['start'])
            if editor is None:
                return
            # select text
            helper = TextHelper(editor)
            cursor = helper.select_lines(start=l, end=l)
            cursor.movePosition(cursor.StartOfBlock)
            cursor.movePosition(cursor.Right, cursor.MoveAnchor, start)
            cursor.movePosition(cursor.Right, cursor.KeepAnchor, lenght)
            editor.setTextCursor(cursor)

    def _setup_occurrences_buttons(self, vlayout):
        """
        Creates the occurrences dock widget buttons
        :param vlayout: main layout
        """
        buttons = QtWidgets.QWidget()
        buttons_layout = QtWidgets.QHBoxLayout()
        buttons_layout.setContentsMargins(0, 0, 0, 0)
        # Close
        bt = QtWidgets.QPushButton()
        bt.setText(_('Close'))
        bt.clicked.connect(self._remove_occurrences_dock)
        buttons_layout.addWidget(bt)
        # Spacer
        buttons_layout.addSpacerItem(QtWidgets.QSpacerItem(
            20, 20, QtWidgets.QSizePolicy.Expanding))
        buttons.setLayout(buttons_layout)
        vlayout.addWidget(buttons)

    def _create_review_dock(self):
        """
        Creates the dock widget that shows the refactor diff.
        """
        if self._review_widget:
            self._preview_dock.show()
            self._preview_dock.button.show()
            self._preview_dock.button.action.setVisible(True)
            return
        self._review_widget = QtWidgets.QWidget()
        vlayout = QtWidgets.QVBoxLayout()
        # buttons
        bt_refactor = self._setup_review_buttons(vlayout)
        # Diff viewer
        self._viewer = DiffViewer()
        vlayout.addWidget(self._viewer)
        self._review_widget.setLayout(vlayout)

        # Dock widget
        self._preview_dock = api.window.add_dock_widget(
            self._review_widget, _('Review'),
            QtGui.QIcon.fromTheme('edit-find'),
            QtCore.Qt.BottomDockWidgetArea)
        bt_refactor.setFocus()

    def _update_preview(self):
        try:
            texts = []
            for prj, changes in self._pending_changes:
                lines = ''
                for subchanges in changes.changes:
                    resource = subchanges.get_changed_resources()[0]
                    resource_path = resource.real_path
                    if prj.address + os.sep in resource_path:
                        desc = subchanges.get_description()
                        lines += desc
                if lines:
                    texts.append('%s\n\n*** Project: %s\n\n%s\n' %
                                 (str(changes), prj.address, lines))
            self._viewer.setPlainText('\n'.join(texts))
        except TypeError:
            # not multiproj, one single change set
            self._viewer.setPlainText(self._pending_changes.get_description())

    def _setup_review_buttons(self, vlayout):
        """
        Creates the buttons of the preview dock widget
        :param vlayout: main layout
        """
        buttons = QtWidgets.QWidget()
        buttons_layout = QtWidgets.QHBoxLayout()
        buttons_layout.setContentsMargins(0, 0, 0, 0)
        bt_refactor = bt = QtWidgets.QPushButton()
        bt.setText(_('Refactor'))
        bt.clicked.connect(self._refactor)
        buttons_layout.addWidget(bt)
        # Close
        bt = QtWidgets.QPushButton()
        bt.setText(_('Cancel'))
        bt.clicked.connect(self._remove_preview_dock)
        buttons_layout.addWidget(bt)
        # Spacer
        buttons_layout.addSpacerItem(QtWidgets.QSpacerItem(
            20, 20, QtWidgets.QSizePolicy.Expanding))
        buttons.setLayout(buttons_layout)
        vlayout.addWidget(buttons)

        return bt_refactor

    def _remove_preview_dock(self):
        """
        Removes the preview dock widget
        """
        if self._preview_dock is not None:
            self._preview_dock.hide()
            self._preview_dock.button.hide()
            self._preview_dock.button.action.setVisible(False)

    def _remove_occurrences_dock(self):
        """
        Removes the occurrences dock widget.
        """
        if self._occurrences_dock is not None:
            self._occurrences_dock.hide()
            self._occurrences_dock.button.hide()
            self._occurrences_dock.button.action.setVisible(False)

    def _refactor(self):
        """
        Performs the refactoring.
        """
        main_project = self._main_project
        multiproj = self._has_multiple_projects()
        pending_changes = self._pending_changes
        api.tasks.start(_('Refactoring: apply pending changes'),
                        apply_pending_changes, self._on_refactoring_finished,
                        args=(main_project, multiproj, pending_changes),
                        use_thread=True)

    def _on_refactoring_finished(self, ret_val):
        self._remove_preview_dock()
        if ret_val is not True:
            api.events.post(RefactoringErrorEvent(ret_val), False)

    @staticmethod
    def _get_other_projects(path_only=False):
        """
        Gets the list of secondary projects (all except current).
        """
        projects = []
        current = api.project.get_current_project()
        for path in api.project.get_projects():
            if path == current:
                continue
            if not path_only:
                prj = Project(path, ropefolder=api.project.FOLDER,
                              fscommands=FileSystemCommands())
                prj.validate()
            else:
                prj = path
            projects.append(prj)
        return projects

    @staticmethod
    def _has_multiple_projects():
        """
        Checks whether multiple project have been opened in the main window.
        :return: True if window has multiple project, False if window only has
                 one project.
        """
        return len(api.project.get_projects()) > 1

    @staticmethod
    def _on_document_saved(path, old_content):
        if not path:
            return
        project = None
        for project in api.project.get_projects():
            prj_path = project + os.sep
            if prj_path in path:
                project = Project(prj_path, ropefolder=api.project.FOLDER,
                                  fscommands=FileSystemCommands())
                break
        if project:
            if path.endswith('_rc.py'):
                return
            api.tasks.start(_('Refactoring: reporting changes'),
                            report_changes, None,
                            args=(project, path, old_content),
                            use_thread=False)

    @staticmethod
    def _get_real_position(position):
        """
        Gets the real cursor position (there might be a difference between
        editor content and file system content because of clean_trailing
        whitespaces on save). This function will converts the absolute position
        into a line/column pair and use this info to get the position in the
        file.
        """
        tc = api.editor.get_current_editor().textCursor()
        tc.setPosition(position)
        l = tc.blockNumber()
        c = tc.positionInBlock()
        e = api.editor.get_current_editor().file.encoding
        path = api.editor.get_current_editor().file.path
        try:
            with open(path, 'r', encoding=e) as f:
                lines = f.readlines()
        except OSError:
            _logger().exception('failed to read file %s', path)
            lines = []
        real_pos = 0
        for i, line in enumerate(lines):
            if i == l:
                real_pos += c
                break
            else:
                real_pos += len(line)
        return real_pos