ropemode-0.2/0000755000175000001440000000000011755011141013755 5ustar generalusers00000000000000ropemode-0.2/ropemode.egg-info/0000755000175000001440000000000011755011141017261 5ustar generalusers00000000000000ropemode-0.2/ropemode.egg-info/PKG-INFO0000644000175000001440000000137011755011141020357 0ustar generalusers00000000000000Metadata-Version: 1.1 Name: ropemode Version: 0.2 Summary: ropemode, a helper for using rope refactoring library in IDEs Home-page: http://rope.sf.net/ Author: Ali Gholami Rudi Author-email: aligrudi@users.sourceforge.net License: GNU GPL Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Environment :: X11 Applications Classifier: Environment :: Win32 (MS Windows) Classifier: Environment :: MacOS X Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Topic :: Software Development Requires: rope (>= 0.9.4) ropemode-0.2/ropemode.egg-info/requires.txt0000644000175000001440000000001511755011141021655 0ustar generalusers00000000000000rope >= 0.9.4ropemode-0.2/ropemode.egg-info/dependency_links.txt0000644000175000001440000000000111755011141023327 0ustar generalusers00000000000000 ropemode-0.2/ropemode.egg-info/SOURCES.txt0000644000175000001440000000047411755011141021152 0ustar generalusers00000000000000setup.py ropemode/__init__.py ropemode/decorators.py ropemode/dialog.py ropemode/environment.py ropemode/filter.py ropemode/interface.py ropemode/refactor.py ropemode.egg-info/PKG-INFO ropemode.egg-info/SOURCES.txt ropemode.egg-info/dependency_links.txt ropemode.egg-info/requires.txt ropemode.egg-info/top_level.txtropemode-0.2/ropemode.egg-info/top_level.txt0000644000175000001440000000001111755011141022003 0ustar generalusers00000000000000ropemode ropemode-0.2/ropemode/0000755000175000001440000000000011755011141015567 5ustar generalusers00000000000000ropemode-0.2/ropemode/environment.py0000644000175000001440000000360511533667560020531 0ustar generalusers00000000000000class Environment(object): def ask(self, prompt, default=None, starting=None): pass def ask_values(self, prompt, values, default=None, starting=None): pass def ask_directory(self, prompt, default=None, starting=None): pass def ask_completion(self, prompt, values, starting=None): pass def message(self, message): pass def yes_or_no(self, prompt): pass def y_or_n(self, prompt): pass def get(self, name, default=None): pass def get_offset(self): pass def get_text(self): pass def get_region(self): pass def filename(self): pass def is_modified(self): pass def goto_line(self, lineno): pass def insert_line(self, line, lineno): pass def insert(self, text): pass def delete(self, start, end): pass def filenames(self): pass def save_files(self, filenames): pass def reload_files(self, filenames, moves={}): pass def find_file(self, filename, readonly=False, other=False): pass def create_progress(self, name): pass def current_word(self): pass def push_mark(self): pass def pop_mark(self): pass def prefix_value(self, prefix): pass def show_occurrences(self, locations): pass def show_doc(self, docs, altview=False): pass def preview_changes(self, diffs): pass def local_command(self, name, callback, key=None, prefix=False): pass def global_command(self, name, callback, key=None, prefix=False): pass def add_hook(self, name, callback, hook): pass def _completion_text(self, proposal): return proposal.name def _completion_data(self, proposal): return self._completion_text(proposal) ropemode-0.2/ropemode/filter.py0000644000175000001440000000225511533667560017452 0ustar generalusers00000000000000from rope.base import exceptions def resources(project, rules): """Find python files in the `project` matching `rules` `rules` is a multi-line `str`; each line starts with either a '+' or '-'. Each '+' means include the file (or its children if it's a folder) that comes after it. '-' has the same meaning for exclusion. """ all = set(project.pycore.get_python_files()) files = None for line in rules.splitlines(): if not line.strip(): continue first, path = (line[0], line[1:]) if first not in '+-': continue try: resource = project.get_resource(path.strip()) except exceptions.ResourceNotFoundError: continue if resource.is_folder(): matches = set(filter(lambda item: resource.contains(item), all)) else: matches = set([resource]) if first == '+': if files is None: files = set() files.update(matches) if first == '-': if files is None: files = set(all) files -= matches if files is None: return all return files ropemode-0.2/ropemode/__init__.py0000644000175000001440000000117211755011042017701 0ustar generalusers00000000000000"""ropemode, a helper for using rope refactoring library in IDEs""" INFO = __doc__ VERSION = '0.2' COPYRIGHT = """\ Copyright (C) 2007-2012 Ali Gholami Rudi This program is free software; you can redistribute it and/or modify it under the terms of GNU General Public License as published by the Free Software Foundation; either version 2 of the license, or (at your opinion) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.""" ropemode-0.2/ropemode/decorators.py0000644000175000001440000000465711533667560020342 0ustar generalusers00000000000000import traceback from rope.base import exceptions class Logger(object): message = None only_short = False def __call__(self, message, short=None): if short is None or not self.only_short: self._show(message) if short is not None: self._show(short) def _show(self, message): if message is None: print message else: self.message(message) logger = Logger() def lisphook(func): def newfunc(*args, **kwds): try: func(*args, **kwds) except Exception, e: trace = str(traceback.format_exc()) short = 'Ignored an exception in ropemode hook: %s' % \ _exception_message(e) logger(trace, short) newfunc.lisp = None newfunc.__name__ = func.__name__ newfunc.__doc__ = func.__doc__ return newfunc def lispfunction(func): func.lisp = None return func input_exceptions = (exceptions.RefactoringError, exceptions.ModuleSyntaxError, exceptions.BadIdentifierError) def _exception_handler(func): def newfunc(*args, **kwds): try: return func(*args, **kwds) except exceptions.RopeError, e: short = None if isinstance(e, input_exceptions): short = _exception_message(e) logger(str(traceback.format_exc()), short) newfunc.__name__ = func.__name__ newfunc.__doc__ = func.__doc__ return newfunc def _exception_message(e): return '%s: %s' % (e.__class__.__name__, str(e)) def rope_hook(hook): def decorator(func): func = lisphook(func) func.name = func.__name__ func.kind = 'hook' func.hook = hook return func return decorator def local_command(key=None, prefix=False, shortcut=None, name=None): def decorator(func, name=name): func = _exception_handler(func) func.kind = 'local' func.prefix = prefix func.local_key = key func.shortcut_key = shortcut if name is None: name = func.__name__ func.name = name return func return decorator def global_command(key=None, prefix=False): def decorator(func): func = _exception_handler(func) func.kind = 'global' func.prefix = prefix func.global_key = key func.name = func.__name__ return func return decorator ropemode-0.2/ropemode/interface.py0000644000175000001440000006170111755010744020116 0ustar generalusers00000000000000import os import rope.base.change from rope.base import libutils, utils, exceptions from rope.contrib import codeassist, generate, autoimport, findit from ropemode import refactor, decorators, dialog class RopeMode(object): def __init__(self, env): self.project = None self.old_content = None self.env = env self._prepare_refactorings() self.autoimport = None self._init_mode() def init(self): """Initialize rope mode""" def _init_mode(self): for attrname in dir(self): attr = getattr(self, attrname) if not callable(attr): continue kind = getattr(attr, 'kind', None) if kind == 'local': key = getattr(attr, 'local_key', None) prefix = getattr(attr, 'prefix', None) self.env.local_command(attrname, attr, key, prefix) if kind == 'global': key = getattr(attr, 'global_key', None) prefix = getattr(attr, 'prefix', None) self.env.global_command(attrname, attr, key, prefix) if kind == 'hook': hook = getattr(attr, 'hook', None) self.env.add_hook(attrname, attr, hook) def _prepare_refactorings(self): for name in dir(refactor): if not name.startswith('_') and name != 'Refactoring': attr = getattr(refactor, name) if isinstance(attr, type) and \ issubclass(attr, refactor.Refactoring): refname = self._refactoring_name(attr) @decorators.local_command(attr.key, 'P', None, refname) def do_refactor(prefix, self=self, refactoring=attr): initial_asking = prefix is None refactoring(self, self.env).show(initial_asking=initial_asking) setattr(self, refname, do_refactor) def _refactoring_name(self, refactoring): return refactor.refactoring_name(refactoring) @decorators.rope_hook('before_save') def before_save_actions(self): if self.project is not None: if not self._is_python_file(self.env.filename()): return resource = self._get_resource() if resource.exists(): self.old_content = resource.read() else: self.old_content = '' @decorators.rope_hook('after_save') def after_save_actions(self): if self.project is not None and self.old_content is not None: libutils.report_change(self.project, self.env.filename(), self.old_content) self.old_content = None @decorators.rope_hook('exit') def exiting_actions(self): if self.project is not None: self.close_project() @decorators.global_command('o') def open_project(self, root=None): if not root: root = self.env.ask_directory('Rope project root folder: ') if self.project is not None: self.close_project() address = rope.base.project._realpath(os.path.join(root, '.ropeproject')) if not os.path.exists(address): if not self.env.y_or_n('Project not exists in %s, ' \ 'create one?' % root): self.env.message("Project creation aborted") return progress = self.env.create_progress('Opening [%s] project' % root) self.project = rope.base.project.Project(root) if self.env.get('enable_autoimport'): underlined = self.env.get('autoimport_underlineds') self.autoimport = autoimport.AutoImport(self.project, underlined=underlined) progress.done() @decorators.global_command('k') def close_project(self): if self.project is not None: progress = self.env.create_progress('Closing [%s] project' % self.project.address) self.project.close() self.project = None progress.done() @decorators.global_command() def write_project(self): if self.project is not None: progress = self.env.create_progress( 'Writing [%s] project data to disk' % self.project.address) self.project.sync() progress.done() @decorators.global_command('u') def undo(self): self._check_project() change = self.project.history.tobe_undone if change is None: self.env.message('Nothing to undo!') return if self.env.y_or_n('Undo [%s]? ' % str(change)): def undo(handle): for changes in self.project.history.undo(task_handle=handle): self._reload_buffers(changes, undo=True) refactor.runtask(self.env, undo, 'Undo refactoring', interrupts=False) @decorators.global_command('r') def redo(self): self._check_project() change = self.project.history.tobe_redone if change is None: self.env.message('Nothing to redo!') return if self.env.y_or_n('Redo [%s]? ' % str(change)): def redo(handle): for changes in self.project.history.redo(task_handle=handle): self._reload_buffers(changes) refactor.runtask(self.env, redo, 'Redo refactoring', interrupts=False) @decorators.local_command('a g', shortcut='C-c g') def goto_definition(self): definition = self._base_definition_location() if definition: self.env.push_mark() self._goto_location(definition[0], definition[1]) else: self.env.message('Cannot find the definition!') @decorators.local_command() def pop_mark(self): self.env.pop_mark() @decorators.local_command() def definition_location(self): definition = self._base_definition_location() if definition: return str(definition[0].real_path), definition[1] return None def _base_definition_location(self): self._check_project() resource, offset = self._get_location() maxfixes = self.env.get('codeassist_maxfixes') try: definition = codeassist.get_definition_location( self.project, self._get_text(), offset, resource, maxfixes) except exceptions.BadIdentifierError: return None if tuple(definition) != (None, None): return definition return None @decorators.local_command('a d', 'P', 'C-c d') def show_doc(self, prefix): self._check_project() self._base_show_doc(prefix, self._base_get_doc(codeassist.get_doc)) @decorators.local_command() def get_calltip(self): self._check_project() def _get_doc(project, text, offset, *args, **kwds): try: offset = text.rindex('(', 0, offset) - 1 except ValueError: return None return codeassist.get_calltip(project, text, offset, *args, **kwds) return self._base_get_doc(_get_doc) @decorators.local_command('a c', 'P') def show_calltip(self, prefix): self._base_show_doc(prefix, self.get_calltip()) def _base_show_doc(self, prefix, docs): if docs: self.env.show_doc(docs, prefix) else: self.env.message('No docs available!') @decorators.local_command() def get_doc(self): self._check_project() return self._base_get_doc(codeassist.get_doc) def _base_get_doc(self, get_doc): maxfixes = self.env.get('codeassist_maxfixes') text = self._get_text() offset = self.env.get_offset() try: return get_doc(self.project, text, offset, self.resource, maxfixes) except exceptions.BadIdentifierError: return None def _get_text(self): resource = self.resource if not self.env.is_modified() and resource is not None: return resource.read() return self.env.get_text() def _base_findit(self, do_find, optionals, get_kwds): self._check_project() self._save_buffers() resource, offset = self._get_location() action, values = dialog.show_dialog( self._askdata, ['search', 'cancel'], optionals=optionals) if action == 'search': kwds = get_kwds(values) def calculate(handle): resources = refactor._resources(self.project, values.get('resources')) return do_find(self.project, resource, offset, resources=resources, task_handle=handle, **kwds) result = refactor.runtask(self.env, calculate, 'Find Occurrences') locations = [Location(location) for location in result] self.env.show_occurrences(locations) @decorators.local_command('a f', shortcut='C-c f') def find_occurrences(self): optionals = { 'unsure': dialog.Data('Find uncertain occurrences: ', default='no', values=['yes', 'no']), 'resources': dialog.Data('Files to search: '), 'in_hierarchy': dialog.Data( 'Rename methods in class hierarchy: ', default='no', values=['yes', 'no'])} def get_kwds(values): return {'unsure': values.get('unsure') == 'yes', 'in_hierarchy': values.get('in_hierarchy') == 'yes'} self._base_findit(findit.find_occurrences, optionals, get_kwds) @decorators.local_command('a i') def find_implementations(self): optionals = {'resources': dialog.Data('Files to search: ')} def get_kwds(values): return {} self._base_findit(findit.find_implementations, optionals, get_kwds) @decorators.local_command('a /', 'P', 'M-/') def code_assist(self, prefix): _CodeAssist(self, self.env).code_assist(prefix) @decorators.local_command('a ?', 'P', 'M-?') def lucky_assist(self, prefix): _CodeAssist(self, self.env).lucky_assist(prefix) @decorators.local_command() def auto_import(self): _CodeAssist(self, self.env).auto_import() @decorators.local_command() def completions(self): return _CodeAssist(self, self.env).completions() @decorators.local_command() def extended_completions(self): return _CodeAssist(self, self.env).extended_completions() def _check_autoimport(self): self._check_project() if self.autoimport is None: self.env.message('autoimport is disabled; ' 'see `enable_autoimport\' variable') return False return True @decorators.global_command() def generate_autoimport_cache(self): if not self._check_autoimport(): return modules = self.env.get('autoimport_modules') modnames = [] if modules: for i in range(len(modules)): modname = modules[i] if not isinstance(modname, basestring): modname = modname.value() modnames.append(modname) else: modules = [] def generate(handle): self.autoimport.generate_cache(task_handle=handle) self.autoimport.generate_modules_cache(modules, task_handle=handle) refactor.runtask(self.env, generate, 'Generate autoimport cache') @decorators.global_command('f', 'P') def find_file(self, prefix): file = self._base_find_file(prefix) if file is not None: self.env.find_file(file.real_path) @decorators.global_command('4 f', 'P') def find_file_other_window(self, prefix): file = self._base_find_file(prefix) if file is not None: self.env.find_file(file.real_path, other=True) def _base_find_file(self, prefix): self._check_project() if prefix: files = self.project.pycore.get_python_files() else: files = self.project.get_files() return self._ask_file(files) def _ask_file(self, files): names = [] for file in files: names.append('<'.join(reversed(file.path.split('/')))) result = self.env.ask_values('Rope Find File: ', names) if result is not None: path = '/'.join(reversed(result.split('<'))) file = self.project.get_file(path) return file self.env.message('No file selected') @decorators.local_command('a j') def jump_to_global(self): if not self._check_autoimport(): return all_names = list(self.autoimport.get_all_names()) name = self.env.ask_values('Global name: ', all_names) result = dict(self.autoimport.get_name_locations(name)) if len(result) == 1: resource = list(result.keys())[0] else: resource = self._ask_file(result.keys()) if resource: self._goto_location(resource, result[resource]) @decorators.global_command('c') def project_config(self): self._check_project() if self.project.ropefolder is not None: config = self.project.ropefolder.get_child('config.py') self.env.find_file(config.real_path) else: self.env.message('No rope project folder found') @decorators.global_command('n m') def create_module(self): def callback(sourcefolder, name): return generate.create_module(self.project, name, sourcefolder) self._create('module', callback) @decorators.global_command('n p') def create_package(self): def callback(sourcefolder, name): folder = generate.create_package(self.project, name, sourcefolder) return folder.get_child('__init__.py') self._create('package', callback) @decorators.global_command('n f') def create_file(self): def callback(parent, name): return parent.create_file(name) self._create('file', callback, 'parent') @decorators.global_command('n d') def create_directory(self): def callback(parent, name): parent.create_folder(name) self._create('directory', callback, 'parent') @decorators.local_command() def analyze_module(self): """Perform static object analysis on this module""" self._check_project() self.project.pycore.analyze_module(self.resource) @decorators.global_command() def analyze_modules(self): """Perform static object analysis on all project modules""" self._check_project() def _analyze_modules(handle): libutils.analyze_modules(self.project, task_handle=handle) refactor.runtask(self.env, _analyze_modules, 'Analyze project modules') @decorators.local_command() def run_module(self): """Run and perform dynamic object analysis on this module""" self._check_project() process = self.project.pycore.run_module(self.resource) try: process.wait_process() finally: process.kill_process() def _create(self, name, callback, parentname='source'): self._check_project() confs = {'name': dialog.Data(name.title() + ' name: ')} parentname = parentname + 'folder' optionals = {parentname: dialog.Data( parentname.title() + ' Folder: ', default=self.project.address, kind='directory')} action, values = dialog.show_dialog( self._askdata, ['perform', 'cancel'], confs, optionals) if action == 'perform': parent = libutils.path_to_resource( self.project, values.get(parentname, self.project.address)) resource = callback(parent, values['name']) if resource: self.env.find_file(resource.real_path) def _goto_location(self, resource, lineno): if resource: self.env.find_file(str(resource.real_path), other=self.env.get('goto_def_newwin')) if lineno: self.env.goto_line(lineno) def _get_location(self): offset = self.env.get_offset() return self.resource, offset def _get_resource(self, filename=None): if filename is None: filename = self.env.filename() if filename is None or self.project is None: return resource = libutils.path_to_resource(self.project, filename, 'file') return resource @property def resource(self): """the current resource Returns `None` when file does not exist. """ resource = self._get_resource() if resource and resource.exists(): return resource @decorators.global_command() def get_project_root(self): if self.project is not None: return self.project.root.real_path else: return None def _check_project(self): if self.project is None: if self.env.get('guess_project'): self.open_project(self._guess_project()) else: self.open_project() else: self.project.validate(self.project.root) def _guess_project(self): cwd = self.env.filename() if cwd is not None: while True: ropefolder = os.path.join(cwd, '.ropeproject') if os.path.exists(ropefolder) and os.path.isdir(ropefolder): return cwd newcwd = os.path.dirname(cwd) if newcwd == cwd: break cwd = newcwd def _reload_buffers(self, changes, undo=False): self._reload_buffers_for_changes( changes.get_changed_resources(), self._get_moved_resources(changes, undo)) def _reload_buffers_for_changes(self, changed, moved={}): filenames = [resource.real_path for resource in changed] moved = dict([(resource.real_path, moved[resource].real_path) for resource in moved]) self.env.reload_files(filenames, moved) def _get_moved_resources(self, changes, undo=False): result = {} if isinstance(changes, rope.base.change.ChangeSet): for change in changes.changes: result.update(self._get_moved_resources(change)) if isinstance(changes, rope.base.change.MoveResource): result[changes.resource] = changes.new_resource if undo: return dict([(value, key) for key, value in result.items()]) return result def _save_buffers(self, only_current=False): if only_current: filenames = [self.env.filename()] else: filenames = self.env.filenames() pythons = [] for filename in filenames: if self._is_python_file(filename): pythons.append(filename) self.env.save_files(pythons) def _is_python_file(self, path): resource = self._get_resource(path) return (resource is not None and resource.project == self.project and self.project.pycore.is_python_file(resource)) def _askdata(self, data, starting=None): ask_func = self.env.ask ask_args = {'prompt': data.prompt, 'starting': starting, 'default': data.default} if data.values: ask_func = self.env.ask_values ask_args['values'] = data.values elif data.kind == 'directory': ask_func = self.env.ask_directory return ask_func(**ask_args) class Location(object): def __init__(self, location): self.location = location self.filename = location.resource.real_path self.offset = location.offset self.note = '' if location.unsure: self.note = '?' @property def lineno(self): if hasattr(self.location, 'lineno'): return self.location.lineno return self.location.resource.read().count('\n', 0, self.offset) + 1 class _CodeAssist(object): def __init__(self, interface, env): self.interface = interface self.env = env def code_assist(self, prefix): proposals = self._calculate_proposals() if prefix is not None: arg = self.env.prefix_value(prefix) if arg == 0: arg = len(proposals) common_start = self._calculate_prefix(proposals[:arg]) self.env.insert(common_start[self.offset - self.starting_offset:]) self._starting = common_start self._offset = self.starting_offset + len(common_start) prompt = 'Completion for %s: ' % self.expression proposals = map(self.env._completion_data, proposals) result = self.env.ask_completion(prompt, proposals, self.starting) if result is not None: self._apply_assist(result) def lucky_assist(self, prefix): proposals = self._calculate_proposals() selected = 0 if prefix is not None: selected = self.env.prefix_value(prefix) if 0 <= selected < len(proposals): result = self.env._completion_text(proposals[selected]) else: self.env.message('Not enough proposals!') return self._apply_assist(result) def auto_import(self): if not self.interface._check_autoimport(): return name = self.env.current_word() modules = self.autoimport.get_modules(name) if modules: if len(modules) == 1: module = modules[0] else: module = self.env.ask_values( 'Which module to import: ', modules) self._insert_import(name, module) else: self.env.message('Global name %s not found!' % name) def completions(self): proposals = self._calculate_proposals() prefix = self.offset - self.starting_offset return [self.env._completion_text(proposal)[prefix:] for proposal in proposals] def extended_completions(self): proposals = self._calculate_proposals() prefix = self.offset - self.starting_offset return [[proposal.name[prefix:], proposal.get_doc(), proposal.type] for proposal in proposals] def _apply_assist(self, assist): if ' : ' in assist: name, module = assist.rsplit(' : ', 1) self.env.delete(self.starting_offset + 1, self.offset + 1) self.env.insert(name) self._insert_import(name, module) else: self.env.delete(self.starting_offset + 1, self.offset + 1) self.env.insert(assist) def _calculate_proposals(self): self.interface._check_project() resource = self.interface.resource maxfixes = self.env.get('codeassist_maxfixes') proposals = codeassist.code_assist( self.interface.project, self.source, self.offset, resource, maxfixes=maxfixes) if self.env.get('sorted_completions', True): proposals = codeassist.sorted_proposals(proposals) if self.autoimport is not None: if self.starting.strip() and '.' not in self.expression: import_assists = self.autoimport.import_assist(self.starting) for assist in import_assists: p = codeassist.CompletionProposal(' : '.join(assist), 'autoimport') proposals.append(p) return proposals def _insert_import(self, name, module): lineno = self.autoimport.find_insertion_line(self.source) line = 'from %s import %s' % (module, name) self.env.insert_line(line, lineno) def _calculate_prefix(self, proposals): if not proposals: return '' prefix = self.env._completion_text(proposals[0]) for proposal in proposals: common = 0 name = self.env._completion_text(proposal) for c1, c2 in zip(prefix, name): if c1 != c2 or ' ' in (c1, c2): break common += 1 prefix = prefix[:common] return prefix @property @utils.cacheit def offset(self): return self.env.get_offset() @property @utils.cacheit def source(self): return self.interface._get_text() @property @utils.cacheit def starting_offset(self): return codeassist.starting_offset(self.source, self.offset) @property @utils.cacheit def starting(self): return self.source[self.starting_offset:self.offset] @property @utils.cacheit def expression(self): return codeassist.starting_expression(self.source, self.offset) @property def autoimport(self): return self.interface.autoimport ropemode-0.2/ropemode/refactor.py0000644000175000001440000003655711533667560020006 0ustar generalusers00000000000000import re import rope.base.change import rope.contrib.generate import rope.refactor.change_signature import rope.refactor.extract import rope.refactor.inline import rope.refactor.introduce_factory import rope.refactor.method_object import rope.refactor.move import rope.refactor.rename import rope.refactor.restructure import rope.refactor.usefunction from rope.base import taskhandle from ropemode import dialog, filter class Refactoring(object): key = None confs = {} optionals = {} saveall = True def __init__(self, interface, env): self.interface = interface self.env = env def show(self, initial_asking=True): self.interface._check_project() self.interface._save_buffers(only_current=not self.saveall) self._create_refactoring() action, result = dialog.show_dialog( self.interface._askdata, ['perform', 'preview', 'cancel'], self._get_confs(), self._get_optionals(), initial_asking=initial_asking) if action == 'cancel': self.env.message('Cancelled!') return def calculate(handle): return self._calculate_changes(result, handle) name = 'Calculating %s changes' % self.name changes = runtask(self.env, calculate, name=name) if action == 'perform': self._perform(changes) if action == 'preview': if changes is not None: diffs = changes.get_description() if self.env.preview_changes(diffs): self._perform(changes) else: self.env.message('Thrown away!') else: self.env.message('No changes!') @property def project(self): return self.interface.project @property def resource(self): return self.interface._get_resource() @property def offset(self): return self.env.get_offset() @property def region(self): return self.env.get_region() @property def name(self): return refactoring_name(self.__class__) def _calculate_changes(self, option_values, task_handle): pass def _create_refactoring(self): pass def _done(self): pass def _perform(self, changes): if changes is None: self.env.message('No changes!') return def perform(handle, self=self, changes=changes): self.project.do(changes, task_handle=handle) self.interface._reload_buffers(changes) self._done() runtask(self.env, perform, 'Making %s changes' % self.name, interrupts=False) self.env.message(str(changes.description) + ' finished') def _get_confs(self): return self.confs def _get_optionals(self): return self.optionals @property def resources_option(self): return dialog.Data('Files to apply this refactoring on: ', decode=self._decode_resources) def _decode_resources(self, value): return _resources(self.project, value) class Rename(Refactoring): key = 'r' saveall = True def _create_refactoring(self): self.renamer = rope.refactor.rename.Rename( self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): return self.renamer.get_changes(task_handle=task_handle, **values) def _get_optionals(self): opts = {} opts['docs'] = dialog.Boolean('Search comments and docs: ', True) if self.renamer.is_method(): opts['in_hierarchy'] = dialog.Boolean('Rename methods in ' 'class hierarchy: ') opts['resources'] = self.resources_option opts['unsure'] = dialog.Data('Unsure occurrences: ', decode=self._decode_unsure, values=['ignore', 'match'], default='ignore') return opts def _get_confs(self): oldname = str(self.renamer.get_old_name()) return {'new_name': dialog.Data('New name: ', default=oldname)} def _decode_unsure(self, value): unsure = value == 'match' return lambda occurrence: unsure class RenameCurrentModule(Rename): key = '1 r' offset = None class Restructure(Refactoring): key = 'x' confs = {'pattern': dialog.Data('Restructuring pattern: '), 'goal': dialog.Data('Restructuring goal: ')} def _calculate_changes(self, values, task_handle): restructuring = rope.refactor.restructure.Restructure( self.project, values['pattern'], values['goal'], args=values['args'], imports=values['imports']) return restructuring.get_changes(resources=values['resources'], task_handle=task_handle) def _get_optionals(self): return { 'args': dialog.Data('Arguments: ', decode=self._decode_args), 'imports': dialog.Data('Imports: ', decode=self._decode_imports), 'resources': self.resources_option} def _decode_args(self, value): if value: args = {} for raw_check in value.split('\n'): if raw_check: key, value = raw_check.split(':', 1) args[key.strip()] = value.strip() return args def _decode_imports(self, value): if value: return [line.strip() for line in value.split('\n')] class UseFunction(Refactoring): key = 'u' def _create_refactoring(self): self.user = rope.refactor.usefunction.UseFunction( self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): return self.user.get_changes(task_handle=task_handle, **values) def _get_optionals(self): return {'resources': self.resources_option} class Move(Refactoring): key = 'v' def _create_refactoring(self): self.mover = rope.refactor.move.create_move(self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): destination = values['destination'] resources = values.get('resources', None) if isinstance(self.mover, rope.refactor.move.MoveGlobal): return self._move_global(destination, resources, task_handle) if isinstance(self.mover, rope.refactor.move.MoveModule): return self._move_module(destination, resources, task_handle) if isinstance(self.mover, rope.refactor.move.MoveMethod): return self._move_method(destination, resources, task_handle) def _move_global(self, dest, resources, handle): destination = self.project.pycore.find_module(dest) return self.mover.get_changes( destination, resources=resources, task_handle=handle) def _move_method(self, dest, resources, handle): return self.mover.get_changes( dest, self.mover.get_method_name(), resources=resources, task_handle=handle) def _move_module(self, dest, resources, handle): destination = self.project.pycore.find_module(dest) return self.mover.get_changes( destination, resources=resources, task_handle=handle) def _get_confs(self): if isinstance(self.mover, rope.refactor.move.MoveGlobal): prompt = 'Destination module: ' if isinstance(self.mover, rope.refactor.move.MoveModule): prompt = 'Destination package: ' if isinstance(self.mover, rope.refactor.move.MoveMethod): prompt = 'Destination attribute: ' return {'destination': dialog.Data(prompt)} def _get_optionals(self): return {'resources': self.resources_option} class MoveCurrentModule(Move): key = '1 v' offset = None class ModuleToPackage(Refactoring): key = '1 p' saveall = False def _create_refactoring(self): self.packager = rope.refactor.ModuleToPackage( self.project, self.resource) def _calculate_changes(self, values, task_handle): return self.packager.get_changes() class Inline(Refactoring): key = 'i' def _create_refactoring(self): self.inliner = rope.refactor.inline.create_inline( self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): return self.inliner.get_changes(task_handle=task_handle, **values) def _get_optionals(self): opts = {'resources': self.resources_option} if self.inliner.get_kind() == 'parameter': opts['in_hierarchy'] = dialog.Boolean( 'Apply on all matching methods in class hierarchy: ', False) else: opts['remove'] = dialog.Boolean('Remove the definition: ', True) opts['only_current'] = dialog.Boolean('Inline this ' 'occurrence only: ') return opts class _Extract(Refactoring): saveall = False optionals = {'similar': dialog.Boolean('Extract similar pieces: ', True), 'global_': dialog.Boolean('Make global: ')} kind = None constructor = None def _create_refactoring(self): start, end = self.region self.extractor = self.constructor(self.project, self.resource, start, end) def _calculate_changes(self, values, task_handle): similar = values.get('similar') global_ = values.get('global_') return self.extractor.get_changes(values['name'], similar=similar, global_=global_) def _get_confs(self): return {'name': dialog.Data('Extracted %s name: ' % self.kind)} class ExtractVariable(_Extract): key = 'l' kind = 'variable' constructor = rope.refactor.extract.ExtractVariable class ExtractMethod(_Extract): key = 'm' kind = 'method' constructor = rope.refactor.extract.ExtractMethod class OrganizeImports(Refactoring): key = 'o' saveall = False def _create_refactoring(self): self.organizer = rope.refactor.ImportOrganizer(self.project) def _calculate_changes(self, values, task_handle): return self.organizer.organize_imports(self.resource) class MethodObject(Refactoring): saveall = False confs = {'classname': dialog.Data('New class name: ', default='_ExtractedClass')} def _create_refactoring(self): self.objecter = rope.refactor.method_object.MethodObject( self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): classname = values.get('classname') return self.objecter.get_changes(classname) class IntroduceFactory(Refactoring): saveall = True key = 'f' def _create_refactoring(self): self.factory = rope.refactor.introduce_factory.IntroduceFactory( self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): return self.factory.get_changes(task_handle=task_handle, **values) def _get_confs(self): default = 'create_%s' % self.factory.old_name.lower() return {'factory_name': dialog.Data('Factory name: ', default)} def _get_optionals(self): return {'global_factory': dialog.Boolean('Make global: ', True), 'resources': self.resources_option} class ChangeSignature(Refactoring): saveall = True key = 's' def _create_refactoring(self): self.changer = rope.refactor.change_signature.ChangeSignature( self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): signature = values.get('signature') args = re.sub(r'[\s\(\)]+', '', signature).split(',') olds = [arg[0] for arg in self._get_args()] changers = [] for arg in list(olds): if arg in args: continue changers.append(rope.refactor.change_signature. ArgumentRemover(olds.index(arg))) olds.remove(arg) order = [] for index, arg in enumerate(args): if arg not in olds: changers.append(rope.refactor.change_signature. ArgumentAdder(index, arg)) olds.insert(index, arg) order.append(olds.index(arg)) changers.append(rope.refactor.change_signature. ArgumentReorderer(order, autodef='None')) del values['signature'] return self.changer.get_changes(changers, task_handle=task_handle, **values) def _get_args(self): if hasattr(self.changer, 'get_args'): return self.changer.get_args() return self.changer.get_definition_info().args_with_defaults def _get_confs(self): args = [] for arg, default in self._get_args(): args.append(arg) signature = '(' + ', '.join(args) + ')' return {'signature': dialog.Data('Change the signature: ', default=signature)} def _get_optionals(self): opts = {'resources': self.resources_option} if self.changer.is_method(): opts['in_hierarchy'] = dialog.Boolean('Rename methods in ' 'class hierarchy: ') return opts class _GenerateElement(Refactoring): def _create_refactoring(self): kind = self.name.split('_')[-1] self.generator = rope.contrib.generate.create_generate( kind, self.project, self.resource, self.offset) def _calculate_changes(self, values, task_handle): return self.generator.get_changes() def _done(self): resource, lineno = self.generator.get_location() self.interface._goto_location(resource, lineno) class GenerateVariable(_GenerateElement): key = 'n v' class GenerateFunction(_GenerateElement): key = 'n f' class GenerateClass(_GenerateElement): key = 'n c' class GenerateModule(_GenerateElement): key = 'n m' class GeneratePackage(_GenerateElement): key = 'n p' def refactoring_name(refactoring): classname = refactoring.__name__ result = [] for c in classname: if result and c.isupper(): result.append('_') result.append(c.lower()) name = ''.join(result) return name def _resources(project, text): if text is None or text.strip() == '': return None return filter.resources(project, text) def runtask(env, command, name, interrupts=True): return RunTask(env, command, name, interrupts)() class RunTask(object): def __init__(self, env, task, name, interrupts=True): self.env = env self.task = task self.name = name self.interrupts = interrupts def __call__(self): handle = taskhandle.TaskHandle(name=self.name) progress = self.env.create_progress(self.name) def update_progress(): jobset = handle.current_jobset() if jobset: percent = jobset.get_percent_done() if percent is not None: progress.update(percent) handle.add_observer(update_progress) result = self.task(handle) progress.done() return result ropemode-0.2/ropemode/dialog.py0000644000175000001440000000564511533667560017432 0ustar generalusers00000000000000class Data(object): def __init__(self, prompt=None, default=None, values=None, kind=None, decode=None): self.prompt = prompt self.default = default self.values = values self.kind = kind self._decode = decode def decode(self, value): if self._decode: return self._decode(value) return value class Boolean(Data): def __init__(self, prompt=None, default=False): Data.__init__(self, prompt, self._encode(default), [self._encode(True), self._encode(False)]) def _encode(self, value): if value: return 'yes' return 'no' def decode(self, value): if value.lower() in ('yes', '1', 'true'): return True return False def show_dialog(askdata, actions, confs={}, optionals={}, initial_asking=True): result = {} if initial_asking: for name, conf in confs.items(): result[name] = askdata(conf) actions.append('batchset') names = list(actions) names.extend(optionals.keys()) names.extend(confs.keys()) base_question = Data('Choose what to do: ', default=actions[0], values=names) batchset_question = Data('Batch sets: ') while True: response = askdata(base_question) if response == '': response = base_question.default elif response == 'batchset': sets = askdata(batchset_question) for key, value in _parse_batchset(sets).items(): if key.endswith(':'): key = key[:-1] if key in names: conf = confs.get(key, optionals.get(key)) result[key] = value elif response in actions: break else: if response in confs: conf = confs[response] else: conf = optionals[response] oldvalue = result.get(response, None) result[response] = askdata(conf, starting=oldvalue) decoded = {} all_confs = dict(confs) all_confs.update(optionals) for key in all_confs: conf = all_confs.get(key) if key in result: decoded[key] = conf.decode(result[key]) else: decoded[key] = conf.decode(conf.default) return response, decoded def _parse_batchset(sets): result = [] multiline = False for line in sets.splitlines(True): if line[0].isspace(): if multiline: result[-1][1] += line[1:] else: if not line.strip(): continue multiline= False tokens = line.split(None, 1) value = '' if len(tokens) > 1: result.append([tokens[0], tokens[1].rstrip('\r\n')]) else: multiline = True result.append([tokens[0], '']) return dict(result) ropemode-0.2/PKG-INFO0000644000175000001440000000137011755011141015053 0ustar generalusers00000000000000Metadata-Version: 1.1 Name: ropemode Version: 0.2 Summary: ropemode, a helper for using rope refactoring library in IDEs Home-page: http://rope.sf.net/ Author: Ali Gholami Rudi Author-email: aligrudi@users.sourceforge.net License: GNU GPL Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Environment :: X11 Applications Classifier: Environment :: Win32 (MS Windows) Classifier: Environment :: MacOS X Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Topic :: Software Development Requires: rope (>= 0.9.4) ropemode-0.2/setup.py0000644000175000001440000000170211755011003015464 0ustar generalusers00000000000000extra_kwargs = {} try: from setuptools import setup extra_kwargs['install_requires'] = ['rope >= 0.9.4'] except ImportError: from distutils.core import setup import ropemode classifiers=[ 'Development Status :: 4 - Beta', 'Operating System :: OS Independent', 'Environment :: X11 Applications', 'Environment :: Win32 (MS Windows)', 'Environment :: MacOS X', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Natural Language :: English', 'Programming Language :: Python', 'Topic :: Software Development'] setup(name='ropemode', version=ropemode.VERSION, description=ropemode.INFO, author='Ali Gholami Rudi', author_email='aligrudi@users.sourceforge.net', url='http://rope.sf.net/', packages=['ropemode'], license='GNU GPL', classifiers=classifiers, requires=['rope (>= 0.9.4)'], **extra_kwargs ) ropemode-0.2/setup.cfg0000644000175000001440000000007311755011141015576 0ustar generalusers00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0