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()
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()
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
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, )
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()
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()
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()
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
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)
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!')
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()
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
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
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
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()
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()
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()
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
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
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
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
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