Path: blob/master/elisp/emacs-for-python/rope-dist/ropemode/refactor.py
990 views
import re12import rope.base.change3import rope.contrib.generate4import rope.refactor.change_signature5import rope.refactor.extract6import rope.refactor.inline7import rope.refactor.introduce_factory8import rope.refactor.method_object9import rope.refactor.move10import rope.refactor.rename11import rope.refactor.restructure12import rope.refactor.usefunction13from rope.base import taskhandle1415from ropemode import dialog, filter161718class Refactoring(object):19key = None20confs = {}21optionals = {}22saveall = True2324def __init__(self, interface, env):25self.interface = interface26self.env = env2728def show(self, initial_asking=True):29self.interface._check_project()30self.interface._save_buffers(only_current=not self.saveall)31self._create_refactoring()32action, result = dialog.show_dialog(33self.interface._askdata, ['perform', 'preview', 'cancel'],34self._get_confs(), self._get_optionals(),35initial_asking=initial_asking)36if action == 'cancel':37self.env.message('Cancelled!')38return39def calculate(handle):40return self._calculate_changes(result, handle)41name = 'Calculating %s changes' % self.name42changes = runtask(self.env, calculate, name=name)43if action == 'perform':44self._perform(changes)45if action == 'preview':46if changes is not None:47diffs = changes.get_description()48if self.env.preview_changes(diffs):49self._perform(changes)50else:51self.env.message('Thrown away!')52else:53self.env.message('No changes!')5455@property56def project(self):57return self.interface.project5859@property60def resource(self):61return self.interface._get_resource()6263@property64def offset(self):65return self.env.get_offset()6667@property68def region(self):69return self.env.get_region()7071@property72def name(self):73return refactoring_name(self.__class__)7475def _calculate_changes(self, option_values, task_handle):76pass7778def _create_refactoring(self):79pass8081def _done(self):82pass8384def _perform(self, changes):85if changes is None:86self.env.message('No changes!')87return88def perform(handle, self=self, changes=changes):89self.project.do(changes, task_handle=handle)90self.interface._reload_buffers(changes)91self._done()92runtask(self.env, perform, 'Making %s changes' % self.name,93interrupts=False)94self.env.message(str(changes.description) + ' finished')9596def _get_confs(self):97return self.confs9899def _get_optionals(self):100return self.optionals101102@property103def resources_option(self):104return dialog.Data('Files to apply this refactoring on: ',105decode=self._decode_resources)106107def _decode_resources(self, value):108return _resources(self.project, value)109110111class Rename(Refactoring):112key = 'r'113114saveall = True115116def _create_refactoring(self):117self.renamer = rope.refactor.rename.Rename(118self.project, self.resource, self.offset)119120def _calculate_changes(self, values, task_handle):121return self.renamer.get_changes(task_handle=task_handle, **values)122123def _get_optionals(self):124opts = {}125opts['docs'] = dialog.Boolean('Search comments and docs: ', True)126if self.renamer.is_method():127opts['in_hierarchy'] = dialog.Boolean('Rename methods in '128'class hierarchy: ')129opts['resources'] = self.resources_option130opts['unsure'] = dialog.Data('Unsure occurrences: ',131decode=self._decode_unsure,132values=['ignore', 'match'],133default='ignore')134return opts135136def _get_confs(self):137oldname = str(self.renamer.get_old_name())138return {'new_name': dialog.Data('New name: ', default=oldname)}139140def _decode_unsure(self, value):141unsure = value == 'match'142return lambda occurrence: unsure143144145class RenameCurrentModule(Rename):146key = '1 r'147offset = None148149150class Restructure(Refactoring):151key = 'x'152confs = {'pattern': dialog.Data('Restructuring pattern: '),153'goal': dialog.Data('Restructuring goal: ')}154155def _calculate_changes(self, values, task_handle):156restructuring = rope.refactor.restructure.Restructure(157self.project, values['pattern'], values['goal'],158args=values['args'], imports=values['imports'])159return restructuring.get_changes(resources=values['resources'],160task_handle=task_handle)161162def _get_optionals(self):163return {164'args': dialog.Data('Arguments: ', decode=self._decode_args),165'imports': dialog.Data('Imports: ', decode=self._decode_imports),166'resources': self.resources_option}167168def _decode_args(self, value):169if value:170args = {}171for raw_check in value.split('\n'):172if raw_check:173key, value = raw_check.split(':', 1)174args[key.strip()] = value.strip()175return args176177def _decode_imports(self, value):178if value:179return [line.strip() for line in value.split('\n')]180181182class UseFunction(Refactoring):183key = 'u'184185def _create_refactoring(self):186self.user = rope.refactor.usefunction.UseFunction(187self.project, self.resource, self.offset)188189def _calculate_changes(self, values, task_handle):190return self.user.get_changes(task_handle=task_handle, **values)191192def _get_optionals(self):193return {'resources': self.resources_option}194195196class Move(Refactoring):197key = 'v'198199def _create_refactoring(self):200self.mover = rope.refactor.move.create_move(self.project,201self.resource,202self.offset)203204def _calculate_changes(self, values, task_handle):205destination = values['destination']206resources = values.get('resources', None)207if isinstance(self.mover, rope.refactor.move.MoveGlobal):208return self._move_global(destination, resources, task_handle)209if isinstance(self.mover, rope.refactor.move.MoveModule):210return self._move_module(destination, resources, task_handle)211if isinstance(self.mover, rope.refactor.move.MoveMethod):212return self._move_method(destination, resources, task_handle)213214def _move_global(self, dest, resources, handle):215destination = self.project.pycore.find_module(dest)216return self.mover.get_changes(217destination, resources=resources, task_handle=handle)218219def _move_method(self, dest, resources, handle):220return self.mover.get_changes(221dest, self.mover.get_method_name(),222resources=resources, task_handle=handle)223224def _move_module(self, dest, resources, handle):225destination = self.project.pycore.find_module(dest)226return self.mover.get_changes(227destination, resources=resources, task_handle=handle)228229def _get_confs(self):230if isinstance(self.mover, rope.refactor.move.MoveGlobal):231prompt = 'Destination module: '232if isinstance(self.mover, rope.refactor.move.MoveModule):233prompt = 'Destination package: '234if isinstance(self.mover, rope.refactor.move.MoveMethod):235prompt = 'Destination attribute: '236return {'destination': dialog.Data(prompt)}237238def _get_optionals(self):239return {'resources': self.resources_option}240241242class MoveCurrentModule(Move):243key = '1 v'244offset = None245246247class ModuleToPackage(Refactoring):248key = '1 p'249saveall = False250251def _create_refactoring(self):252self.packager = rope.refactor.ModuleToPackage(253self.project, self.resource)254255def _calculate_changes(self, values, task_handle):256return self.packager.get_changes()257258259class Inline(Refactoring):260key = 'i'261262def _create_refactoring(self):263self.inliner = rope.refactor.inline.create_inline(264self.project, self.resource, self.offset)265266def _calculate_changes(self, values, task_handle):267return self.inliner.get_changes(task_handle=task_handle, **values)268269def _get_optionals(self):270opts = {'resources': self.resources_option}271if self.inliner.get_kind() == 'parameter':272opts['in_hierarchy'] = dialog.Boolean(273'Apply on all matching methods in class hierarchy: ', False)274else:275opts['remove'] = dialog.Boolean('Remove the definition: ', True)276opts['only_current'] = dialog.Boolean('Inline this '277'occurrence only: ')278return opts279280281class _Extract(Refactoring):282saveall = False283optionals = {'similar': dialog.Boolean('Extract similar pieces: ', True),284'global_': dialog.Boolean('Make global: ')}285kind = None286constructor = None287288def _create_refactoring(self):289start, end = self.region290self.extractor = self.constructor(self.project,291self.resource, start, end)292293def _calculate_changes(self, values, task_handle):294similar = values.get('similar')295global_ = values.get('global_')296return self.extractor.get_changes(values['name'], similar=similar,297global_=global_)298299def _get_confs(self):300return {'name': dialog.Data('Extracted %s name: ' % self.kind)}301302303class ExtractVariable(_Extract):304key = 'l'305kind = 'variable'306constructor = rope.refactor.extract.ExtractVariable307308309class ExtractMethod(_Extract):310key = 'm'311kind = 'method'312constructor = rope.refactor.extract.ExtractMethod313314315class OrganizeImports(Refactoring):316key = 'o'317saveall = False318319def _create_refactoring(self):320self.organizer = rope.refactor.ImportOrganizer(self.project)321322def _calculate_changes(self, values, task_handle):323return self.organizer.organize_imports(self.resource)324325326class MethodObject(Refactoring):327saveall = False328confs = {'classname': dialog.Data('New class name: ',329default='_ExtractedClass')}330331def _create_refactoring(self):332self.objecter = rope.refactor.method_object.MethodObject(333self.project, self.resource, self.offset)334335def _calculate_changes(self, values, task_handle):336classname = values.get('classname')337return self.objecter.get_changes(classname)338339340class IntroduceFactory(Refactoring):341saveall = True342key = 'f'343344def _create_refactoring(self):345self.factory = rope.refactor.introduce_factory.IntroduceFactory(346self.project, self.resource, self.offset)347348def _calculate_changes(self, values, task_handle):349return self.factory.get_changes(task_handle=task_handle, **values)350351def _get_confs(self):352default = 'create_%s' % self.factory.old_name.lower()353return {'factory_name': dialog.Data('Factory name: ', default)}354355def _get_optionals(self):356return {'global_factory': dialog.Boolean('Make global: ', True),357'resources': self.resources_option}358359360class ChangeSignature(Refactoring):361saveall = True362key = 's'363364def _create_refactoring(self):365self.changer = rope.refactor.change_signature.ChangeSignature(366self.project, self.resource, self.offset)367368def _calculate_changes(self, values, task_handle):369signature = values.get('signature')370args = re.sub(r'[\s\(\)]+', '', signature).split(',')371olds = [arg[0] for arg in self._get_args()]372373changers = []374for arg in list(olds):375if arg in args:376continue377changers.append(rope.refactor.change_signature.378ArgumentRemover(olds.index(arg)))379olds.remove(arg)380381order = []382for index, arg in enumerate(args):383if arg not in olds:384changers.append(rope.refactor.change_signature.385ArgumentAdder(index, arg))386olds.insert(index, arg)387order.append(olds.index(arg))388changers.append(rope.refactor.change_signature.389ArgumentReorderer(order, autodef='None'))390391del values['signature']392return self.changer.get_changes(changers, task_handle=task_handle,393**values)394395def _get_args(self):396if hasattr(self.changer, 'get_args'):397return self.changer.get_args()398return self.changer.get_definition_info().args_with_defaults399400def _get_confs(self):401args = []402for arg, default in self._get_args():403args.append(arg)404signature = '(' + ', '.join(args) + ')'405return {'signature': dialog.Data('Change the signature: ',406default=signature)}407408def _get_optionals(self):409opts = {'resources': self.resources_option}410if self.changer.is_method():411opts['in_hierarchy'] = dialog.Boolean('Rename methods in '412'class hierarchy: ')413return opts414415416class _GenerateElement(Refactoring):417418def _create_refactoring(self):419kind = self.name.split('_')[-1]420self.generator = rope.contrib.generate.create_generate(421kind, self.project, self.resource, self.offset)422423def _calculate_changes(self, values, task_handle):424return self.generator.get_changes()425426def _done(self):427resource, lineno = self.generator.get_location()428self.interface._goto_location(resource, lineno)429430431class GenerateVariable(_GenerateElement):432key = 'n v'433434435class GenerateFunction(_GenerateElement):436key = 'n f'437438439class GenerateClass(_GenerateElement):440key = 'n c'441442443class GenerateModule(_GenerateElement):444key = 'n m'445446447class GeneratePackage(_GenerateElement):448key = 'n p'449450451def refactoring_name(refactoring):452classname = refactoring.__name__453result = []454for c in classname:455if result and c.isupper():456result.append('_')457result.append(c.lower())458name = ''.join(result)459return name460461def _resources(project, text):462if text is None or text.strip() == '':463return None464return filter.resources(project, text)465466467def runtask(env, command, name, interrupts=True):468return RunTask(env, command, name, interrupts)()469470class RunTask(object):471472def __init__(self, env, task, name, interrupts=True):473self.env = env474self.task = task475self.name = name476self.interrupts = interrupts477478def __call__(self):479handle = taskhandle.TaskHandle(name=self.name)480progress = self.env.create_progress(self.name)481def update_progress():482jobset = handle.current_jobset()483if jobset:484percent = jobset.get_percent_done()485if percent is not None:486progress.update(percent)487handle.add_observer(update_progress)488result = self.task(handle)489progress.done()490return result491492493