Commits

Anonymous committed f284fac

Separating information getting phases

  • Participants
  • Parent commits 869da7f

Comments (0)

Files changed (12)

File docs/done.txt

 ===========
 
 
+- Encapsulate field : October 19, 2006
+
+
 - Convert local variable to field refactoring : October 18, 2006
 
 

File docs/issues.txt

 To Be Discussed
 ===============
 
-* Showing available refactorings for a place
 * Designing refactorings to be used on IDEs
 * Rope's preference saving system: format, location
 
 * Using a modification of `compiler` AST for simplifying refactorings
 
 
+Designing Refactorings to Be Used for IDEs
+==========================================
+
+* Multi-step refactorings
+* Previewing refactorings
+* Finding available refactorings
+* A better facade
+* Better refactoring categoriztion
+
+
+Multi-step Refactorings
+-----------------------
+
+A refactoring should be done in three steps.
+
+1. Getting immediate information
+   
+   After this step we can provide some useful information.  Like the
+   initial name for rename refactoring.
+   
+2. Getting user provided information
+
+   The problem with this step is that based on the first step we
+   might need different data.  For example for move refactoring we
+   need different information for moving a field versus moving a
+   class.
+   
+   This separation helps us to report some of the errors earlier.
+
+3. Finding the changes to be made
+
+   We perform the actual refactoring here.  After this step we can
+   preview the changes.
+
+4. Performing the refactoring
+
+
+What will happen to `PythonRefactoring` facade?
+
+
+Finding Available Refactorings
+------------------------------
+
+The question we need to answer before proceeding is to decide
+wether this belongs to UI or core.
+
+
 Undo Unification
 ================
 

File docs/stories.txt

 * Pull up method
 
 
+* Type hierarchy
+
+
 * Push down method
 
 
 * Running unit tests view
 
 
-* Encapsulate field
-
-
 Remaining Stories
 -----------------
 

File docs/workingon.txt

-Encapsulate Field
+Designing Refactorings to Be Used for IDEs
+==========================================
+
+
+Before 0.3 Release
+==================
+
+* Better refactoring categoriztion
+* ``overview.txt``
+
+
+Remaining Stories
 =================
 
-- Tuple assignments in encapsulate field
-
 * Handling `AssList` for inline variable and encapsulate field
 * Changing refactor modules to use `sourcetools.add_methods`
 * Adding ``add_statements(method, statements)``
 * Changing `Rename` to take an `OccuranceFinder`
 * Caching `Occurrence` fields
+* ``break`` and ``continue`` in extract method
 
-
-Before 0.3 Release
-==================
-
-* Better refactoring categoriztion
-
-
-Remaining Stories
-=================
-
+* Eliminating `Editor.refactoring`; Use editing contexts
 * Considering logical lines in `rope.codeanalyze`
 * Reporting unhandled exceptions as error dialogs in the GUI
 * Better move dialog; complete modules names; use `editor._CompletionListHandle`
 * Moving/renaming current module
-* Showing initial name in rename dialogs
 * Adding ``overview.txt``, ``README.txt``, ``tutorial.txt`` and
   ``COPYING`` to help menu; Adding readonly mode?
 * Extract method and return statements
 * Preferring `DefinedName`\s in attribute code assists
 * `rope.exceptions` should only contain user exceptions that are
   thrown from public classes like `Project`, `PyCore`, `Refactoring`
-* Eliminating `Editor.refactoring`; Use editing contexts
 * Calling `sys.settrace` for all threads in `rope.runmod` module
 * Goto definition for ``"# comment.\na_var"``

File rope/refactor/__init__.py

         self._undo = Undo()
 
     def local_rename(self, resource, offset, new_name):
-        changes = RenameRefactoring(self.pycore).\
-                  local_rename(resource, offset, new_name)
+        changes = RenameRefactoring(self.pycore, resource, offset).\
+                  local_rename(new_name)
         self._add_and_commit_changes(changes)
     
     def rename(self, resource, offset, new_name):
-        changes = RenameRefactoring(self.pycore).rename(resource, offset, new_name)
+        changes = RenameRefactoring(self.pycore, resource, offset).\
+                  rename(new_name)
         self._add_and_commit_changes(changes)
     
     def extract_method(self, resource, start_offset, end_offset,
                        extracted_name):
-        changes = ExtractMethodRefactoring(self.pycore).\
-                  extract_method(resource, start_offset, end_offset,
-                                 extracted_name)
+        changes = ExtractMethodRefactoring(self.pycore, resource,
+                                           start_offset, end_offset).\
+                                           extract_method(extracted_name)
         self._add_and_commit_changes(changes)
     
     def extract_variable(self, resource, start_offset, end_offset,
                          extracted_name):
-        changes = ExtractMethodRefactoring(self.pycore).\
-                  extract_variable(resource, start_offset, end_offset,
-                                   extracted_name)
+        changes = ExtractMethodRefactoring(self.pycore, resource,
+                                           start_offset, end_offset).\
+                                           extract_variable(extracted_name)
         self._add_and_commit_changes(changes)
     
     def transform_module_to_package(self, resource):
         return import_tools.transform_relative_imports_to_absolute(pymodule)
     
     def introduce_factory(self, resource, offset, factory_name, global_factory=False):
-        factory_introducer = IntroduceFactoryRefactoring(self.pycore, resource,
-                                                         offset, factory_name, global_factory)
-        changes = factory_introducer.introduce_factory()
+        factory_introducer = IntroduceFactoryRefactoring(self.pycore,
+                                                         resource, offset)
+        changes = factory_introducer.introduce_factory(factory_name,
+                                                       global_factory)
         self._add_and_commit_changes(changes)
     
     def move(self, resource, offset, dest_resource):
-        changes = MoveRefactoring(self.pycore, resource,
-                                  offset, dest_resource).move()
+        changes = MoveRefactoring(self.pycore, resource, offset).\
+                  move(dest_resource)
         self._add_and_commit_changes(changes)
     
     def inline_local_variable(self, resource, offset):
         self._add_and_commit_changes(changes)
     
     def convert_local_variable_to_field(self, resource, offset):
-        changes = ConvertLocalToFieldRefactoring(self.pycore).\
-                  convert_local_variable_to_field(resource, offset)
+        changes = ConvertLocalToFieldRefactoring(self.pycore, resource, offset).\
+                  convert_local_variable_to_field()
         self._add_and_commit_changes(changes)
         
     def _add_and_commit_changes(self, changes):

File rope/refactor/encapsulate_field.py

             equals_offset = line.index('=')
         except ValueError:
             return False
-        return relative_offset < equals_offset and (prev_char == ',' or next_char in ',)')
+        if prev_char != ',' and next_char not in ',)':
+            return False
+        parens_start = word_finder.find_parens_start_from_inside(relative_offset)
+        return relative_offset < equals_offset and (parens_start <= 0 or
+                                                    line[:parens_start].strip() == '')
 
     def _manage_writes(self, offset, result):
         if self.last_set is not None and self.last_set <= offset:

File rope/refactor/extract.py

 
 class ExtractMethodRefactoring(object):
     
-    def __init__(self, pycore):
+    def __init__(self, pycore, resource, start_offset, end_offset):
         self.pycore = pycore
+        self.resource = resource
+        self.start_offset = start_offset
+        self.end_offset = end_offset
     
-    def extract_method(self, resource, start_offset, end_offset,
-                       extracted_name):
-        info = _ExtractInformation(self.pycore, resource, start_offset, end_offset)
+    def extract_method(self, extracted_name):
+        info = _ExtractInformation(self.pycore, self.resource,
+                                   self.start_offset, self.end_offset)
         if info.is_one_line_extract():
-            new_contents = _OneLineExtractPerformer(self.pycore, resource, info,
+            new_contents = _OneLineExtractPerformer(self.pycore, self.resource, info,
                                                     extracted_name).extract()
         else:
-            new_contents = _MultiLineExtractPerformer(self.pycore, resource, info,
+            new_contents = _MultiLineExtractPerformer(self.pycore, self.resource, info,
                                                       extracted_name).extract()
         changes = ChangeSet()
-        changes.add_change(ChangeFileContents(resource, new_contents))
+        changes.add_change(ChangeFileContents(self.resource, new_contents))
         return changes
         
-    def extract_variable(self, resource, start_offset, end_offset,
-                         extracted_name):
-        info = _ExtractInformation(self.pycore, resource, start_offset, end_offset)
-        new_contents = _OneLineExtractPerformer(self.pycore, resource, info, 
+    def extract_variable(self, extracted_name):
+        info = _ExtractInformation(self.pycore, self.resource,
+                                   self.start_offset, self.end_offset)
+        new_contents = _OneLineExtractPerformer(self.pycore, self.resource, info, 
                                                 extracted_name, True).extract()
         changes = ChangeSet()
-        changes.add_change(ChangeFileContents(resource, new_contents))
+        changes.add_change(ChangeFileContents(self.resource, new_contents))
         return changes
 
 

File rope/refactor/introduce_factory.py

 from rope.refactor.change import (ChangeSet, ChangeFileContents)
 from rope.refactor import sourceutils
 
+
 class IntroduceFactoryRefactoring(object):
     
-    def __init__(self, pycore, resource, offset, factory_name, global_factory=False):
+    def __init__(self, pycore, resource, offset):
         self.pycore = pycore
         self.offset = offset
-        self.factory_name = factory_name
-        self.global_factory = global_factory
         
         self.old_pyname = \
             rope.codeanalyze.get_pyname_at(self.pycore, resource, offset)
         self.pymodule = self.old_pyname.get_object().get_module()
         self.resource = self.pymodule.get_resource()
 
-    def introduce_factory(self):
+    def introduce_factory(self, factory_name, global_factory=False):
         changes = ChangeSet()
-        self._change_occurrences_in_other_modules(changes)
-    
-        self._change_resource(changes)
+        self._change_occurrences_in_other_modules(changes, factory_name,
+                                                  global_factory)
+        self._change_resource(changes, factory_name, global_factory)
         return changes
 
-    def _change_resource(self, changes):
+    def _change_resource(self, changes, factory_name, global_factory):
         class_scope = self.old_pyname.get_object().get_scope()
         rename_in_module = rope.refactor.rename.RenameInModule(
-            self.pycore, [self.old_pyname], self.old_name, self._get_new_function_name(), True)
+            self.pycore, [self.old_pyname], self.old_name,
+            self._get_new_function_name(factory_name, global_factory), True)
         source_code = rename_in_module.get_changed_module(pymodule=self.pymodule)
         if source_code is None:
             source_code = self.pymodule.source_code
         lines = self.pymodule.lines
         start = self._get_insertion_offset(class_scope, lines)
         result = source_code[:start]
-        result += self._get_factory_method(lines, class_scope)
+        result += self._get_factory_method(lines, class_scope,
+                                           factory_name, global_factory)
         result += source_code[start:]
         changes.add_change(ChangeFileContents(self.resource, result))
 
         start = lines.get_line_end(start_line) + 1
         return start
 
-    def _get_factory_method(self, lines, class_scope):
-        if self.global_factory:
+    def _get_factory_method(self, lines, class_scope,
+                            factory_name, global_factory):
+        if global_factory:
             if self._get_scope_indents(lines, class_scope) > 0:
                 raise rope.exceptions.RefactoringException(
                     'Cannot make global factory method for nested classes.')
             return ('\ndef %s(*args, **kwds):\n    return %s(*args, **kwds)\n' %
-                    (self.factory_name, self.old_name))
+                    (factory_name, self.old_name))
         unindented_factory = ('@staticmethod\n' +
-                              'def %s(*args, **kwds):\n' % self.factory_name +
+                              'def %s(*args, **kwds):\n' % factory_name +
                               '    return %s(*args, **kwds)\n' % self.old_name)
         return '\n' + sourceutils.indent_lines(
             unindented_factory, self._get_scope_indents(lines, class_scope) + 4)
     def _get_scope_indents(self, lines, scope):
         return sourceutils.get_indents(lines, scope.get_start())
 
-    def _get_new_function_name(self):
-        if self.global_factory:
-            return self.factory_name
+    def _get_new_function_name(self, factory_name, global_factory):
+        if global_factory:
+            return factory_name
         else:
-            return self.old_name + '.' + self.factory_name
+            return self.old_name + '.' + factory_name
     
-    def _change_occurrences_in_other_modules(self, changes):
-        changed_name = self._get_new_function_name()
+    def _change_occurrences_in_other_modules(self, changes,
+                                             factory_name, global_factory):
+        changed_name = self._get_new_function_name(factory_name, global_factory)
         import_tools = rope.importutils.ImportTools(self.pycore)
         new_import = import_tools.get_import_for_module(self.pymodule)
-        if self.global_factory:
-            changed_name = new_import.names_and_aliases[0][0] + '.' + self.factory_name
+        if global_factory:
+            changed_name = new_import.names_and_aliases[0][0] + '.' + factory_name
         
         for file_ in self.pycore.get_python_files():
             if file_ == self.resource:
                 continue
             rename_in_module = rope.refactor.rename.RenameInModule(
                 self.pycore, [self.old_pyname], self.old_name, changed_name,
-                only_calls=True, replace_primary=self.global_factory)
+                only_calls=True, replace_primary=global_factory)
             changed_code = rename_in_module.get_changed_module(resource=file_)
             if changed_code is not None:
-                if self.global_factory:
+                if global_factory:
                     new_pymodule = self.pycore.get_string_module(changed_code, self.resource)
                     module_with_imports = import_tools.get_module_with_imports(new_pymodule)
                     module_with_imports.add_import(new_import)

File rope/refactor/localtofield.py

 
 class ConvertLocalToFieldRefactoring(object):
     
-    def __init__(self, pycore):
+    def __init__(self, pycore, resource, offset):
         self.pycore = pycore
-    
-    def convert_local_variable_to_field(self, resource, offset):
-        name = rope.codeanalyze.get_name_at(resource, offset)
-        pyname = rope.codeanalyze.get_pyname_at(self.pycore, resource, offset)
+        self.resource = resource
+        self.offset = offset
+
+    def convert_local_variable_to_field(self):
+        name = rope.codeanalyze.get_name_at(self.resource, self.offset)
+        pyname = rope.codeanalyze.get_pyname_at(self.pycore, self.resource, self.offset)
         if not self._is_a_method_local(pyname):
             raise rope.exceptions.RefactoringException(
                 'Convert local variable to field should be performed on \n'
                 'The field %s already exists' % name)
         
         new_name = self._get_field_name(function_scope.pyobject, name)
-        changes = RenameRefactoring(self.pycore).local_rename(resource, offset,
-                                                              new_name)
+        changes = RenameRefactoring(self.pycore, self.resource, self.offset).\
+                  local_rename(new_name)
         return changes
 
     def _get_field_name(self, pyfunction, name):

File rope/refactor/move.py

 
 class MoveRefactoring(object):
     
-    def __init__(self, pycore, resource, offset, dest_resource):
+    def __init__(self, pycore, resource, offset):
         self.pycore = pycore
-        self.dest_resource = dest_resource
-        pyname = rope.codeanalyze.get_pyname_at(self.pycore, resource, offset)
-        if pyname is None:
+        self.pyname = rope.codeanalyze.get_pyname_at(self.pycore, resource, offset)
+        if self.pyname is None:
             raise rope.exceptions.RefactoringException(
                 'Move works on classes,functions or modules.')
-        moving_object = pyname.get_object()
+    
+    def move(self, dest_resource):
+        moving_object = self.pyname.get_object()
         if moving_object.get_type() == rope.pyobjects.PyObject.get_base_type('Module'):
-            self.mover = _ModuleMover(pycore, pyname, dest_resource)
+            mover = _ModuleMover(self.pycore, self.pyname, dest_resource)
         else:
-            self.mover = _GlobalMover(pycore, pyname, dest_resource)
-    
-    def move(self):
-        return self.mover.move()
+            mover = _GlobalMover(self.pycore, self.pyname, dest_resource)
+        return mover.move()
 
 
 class _Mover(object):

File rope/refactor/rename.py

 
 class RenameRefactoring(object):
     
-    def __init__(self, pycore):
+    def __init__(self, pycore, resource, offset):
         self.pycore = pycore
+        self.resource = resource
+        self.offset = offset
     
-    def local_rename(self, resource, offset, new_name):
-        return self._rename(resource, offset, new_name, True)
+    def local_rename(self, new_name):
+        return self._rename(new_name, True)
     
-    def rename(self, resource, offset, new_name):
-        return self._rename(resource, offset, new_name)
+    def rename(self, new_name):
+        return self._rename(new_name)
     
-    def _rename(self, resource, offset, new_name, in_file=False):
-        old_name = rope.codeanalyze.get_name_at(resource, offset)
-        old_pynames = self._get_old_pynames(offset, resource, in_file, old_name)
+    def _rename(self, new_name, in_file=False):
+        old_name = rope.codeanalyze.get_name_at(self.resource, self.offset)
+        old_pynames = self._get_old_pynames(self.offset, self.resource, in_file, old_name)
         if not old_pynames:
             return None
         # HACK: Do a local rename for names defined in function scopes.
         if not in_file and len(old_pynames) == 1 and \
            self._is_renaming_a_function_local_name(old_pynames[0]):
             in_file = True
-        files = self._get_interesting_files(resource, in_file)
+        files = self._get_interesting_files(self.resource, in_file)
         changes = ChangeSet()
         for file_ in files:
             new_content = RenameInModule(self.pycore, old_pynames, old_name, new_name).\

File ropetest/refactor/__init__.py

         self.mod1.write('import mod\na_var = mod.A()\na_var.attr = 1\nb, a_var.attr = 1, 2\n')
         self.refactoring.encapsulate_field(self.mod1, self.mod1.read().index('attr') + 1)
 
+    def test_tuple_assignments_and_function_calls(self):
+        self.mod1.write('import mod\ndef func(a1=0, a2=0):\n    pass\n'
+                        'a_var = mod.A()\nfunc(a_var.attr, a2=2)\n')
+        self.mod.write(self.a_class)
+        self.refactoring.encapsulate_field(self.mod, self.mod.read().index('attr') + 1)
+        self.assertEquals('import mod\ndef func(a1=0, a2=0):\n    pass\n'
+                          'a_var = mod.A()\nfunc(a_var.get_attr(), a2=2)\n',
+                          self.mod1.read())
+
     def test_tuple_assignments(self):
         self.mod1.write('import mod\na_var = mod.A()\na, b = a_var.attr, 1\n')
         self.mod.write(self.a_class)