Commits

Anonymous committed f25af9e

0.4rc1

Comments (0)

Files changed (15)

 
 Features added in this release:
 
-* 
+* Project History; Undoing refactorings in any order
+* Handling ``global`` keywords
+* Undoing everything
+* Removing `PythonRefactoring` facade
+* Basic ``lambda`` handling
+* Handling builtin `property`
+
+The "Undo/Redo Refactoring" menu item has been removed from refactor
+menu.  Instead a new "Undo/Redo Project Change" has been added to the
+edit menu.  The new actions undo every change to a project; like saving
+files, creating files and folders and refactorings.  Also a "Project
+History" action has been added to edit menu.  In its dialog you can
+see and select changes to be undone in any order.  Note that undoing
+changes in project history undoes the changes it depends on, too.
+
+`rope.refactor.PythonRefactoring` facade has been removed.  You can
+use `rope.refactor` sub-modules for performing refactorings.  Also
+you can commit the changes using `Project.do()`.  Also the
+`Project.history` has been added for undoing and redoing changes.
 
 
 Getting Started

docs/dev/done.txt

 ===========
 
 
+> Public Release 0.4rc1 : January 28, 2007
+
+
 - Project History; Undoing refactorings in any order : January 25, 2007
 
 

docs/dev/issues.txt

 Hot Topics
 ==========
 
-* `Local History`_
-
 
 To Be Discussed
 ===============

docs/dev/stories.txt

 
 * View type hierarchy
 * Open Type; C-T
+* Saving preferences
+* Commanding buffer
 
 
 Stories
 
 
 > Public Release 0.4 : February 4, 2007
-
-
-* Saving preferences
-
-
-* Commanding buffer
-
-
-> Public Release 0.4rc1 : January 28, 2007

docs/dev/workingon.txt

-File History
-============
-
-- Including the changes that a dependency depends on
-- Changed folders for undoing change
-- Changed folders for dependants
-- Adding a UI dialog
-- What if the change to be undone is not in the undo_list
-- Not adding save files when the file has not been changed
-
-
 Remaining Small Stories
 =======================
 
+* Saving diffs in `ChangeContents`
+* Inlining one line return functions with returns
 * Adding imports eats the blank lines after it
 * Undoing `RemoveResource`; It's not used by refactorings
 * Lambdas as functions; consider their parameters

rope/base/change.py

 
     def get_description(self):
         result = self.description + ':\n\n\n' + \
-                 '\n------\n'.join([change.get_description()
-                                    for change in self.changes])
+                 '\n------\n'.join(
+            [(str(change) + ':\n\n' + change.get_description())
+             for change in self.changes])
         return result
 
     def __str__(self):

rope/base/project.py

     def create_folder(self, folder_name):
         self._perform_change(
             rope.base.change.CreateFolder(self, folder_name),
-            'Creating golder <%s>' % (self.path + '/' + folder_name))
+            'Creating folder <%s>' % (self.path + '/' + folder_name))
         return self.get_child(folder_name)
 
     def get_child(self, name):
         return result
 
     def contains(self, resource):
-        return self != resource and resource.path.startswith(self.path)
+        if self == resource:
+            return False
+        return self.path == '' or resource.path.startswith(self.path + '/')
 
 
 class ResourceObserver(object):
     def _calculate_new_resource(self, main, new_main, resource):
         if new_main is None:
             return None
-        diff = resource.path[:len(main.path)]
-        return new_main.path + diff
+        diff = resource.path[len(main.path):]
+        return resource.project.get_resource(new_main.path + diff)
 
 
 class Timekeeper(object):

rope/ide/codeassist.py

         element = scope_finder.get_pyname_at(self.offset)
         if element is not None:
             module, lineno = element.get_definition_location()
-            return module.get_module().get_resource(), lineno
-        else:
-            return (None, None)
+            if module is not None:
+                return module.get_module().get_resource(), lineno
+        return (None, None)
 
 
 class ProposalSorter(object):

rope/refactor/encapsulate_field.py

                 return True
         return False
 
-    def encapsulate_field(self):
+    def get_changes(self):
         changes = ChangeSet('Encapsulate field <%s>' % self.name)
         rename_in_module = GetterSetterRenameInModule(self.pycore, self.name,
                                                       [self.pyname])

rope/refactor/introduce_factory.py

 import rope.base.exceptions
 import rope.base.pyobjects
 import rope.refactor.importutils
-from rope.refactor import rename
-from rope.refactor import occurrences
-from rope.refactor import sourceutils
-
 from rope.base.change import (ChangeSet, ChangeContents)
+from rope.refactor import rename, occurrences, sourceutils
 
 
 class IntroduceFactoryRefactoring(object):
 
     def _change_resource(self, changes, factory_name, global_factory):
         class_scope = self.old_pyname.get_object().get_scope()
-        occurrence_finder = occurrences.FilteredOccurrenceFinder(
-            self.pycore, self.old_name, [self.old_pyname], only_calls=True)
-        source_code = rename.rename_in_module(
-            occurrence_finder, self._get_new_function_name(factory_name, global_factory),
-            pymodule=self.pymodule)
+        source_code = self._rename_occurrences(
+            self.resource,
+            self._get_new_function_name(factory_name, global_factory),
+            global_factory)
         if source_code is None:
             source_code = self.pymodule.source_code
+        else:
+            self.pymodule = self.pycore.get_string_module(
+                source_code, resource=self.resource)
         lines = self.pymodule.lines
         start = self._get_insertion_offset(class_scope, lines)
         result = source_code[:start]
         for file_ in self.pycore.get_python_files():
             if file_ == self.resource:
                 continue
-            occurrence_finder = occurrences.FilteredOccurrenceFinder(
-                self.pycore, self.old_name, [self.old_pyname], only_calls=True)
-            changed_code = rename.rename_in_module(occurrence_finder, changed_name, resource=file_,
-                                                   replace_primary=global_factory)
+            changed_code = self._rename_occurrences(file_, changed_name, global_factory)
             if changed_code is not None:
                 if global_factory:
                     new_pymodule = self.pycore.get_string_module(changed_code, self.resource)
                     module_with_imports.add_import(new_import)
                     changed_code = module_with_imports.get_changed_source()
                 changes.add_change(ChangeContents(file_, changed_code))
+
+    def _rename_occurrences(self, file_, changed_name, global_factory):
+        occurrence_finder = occurrences.FilteredOccurrenceFinder(
+            self.pycore, self.old_name, [self.old_pyname], only_calls=True)
+        changed_code = rename.rename_in_module(
+            occurrence_finder, changed_name, resource=file_,
+            replace_primary=global_factory)
+        return changed_code

rope/refactor/rename.py

 class RenameRefactoring(object):
 
     def __init__(self, project, resource, offset=None):
-        """If `offset` is None `resource` will be renamed"""
+        """If `offset` is None, the `resource` itself will be renamed"""
         self.pycore = project.pycore
         self.resource = resource
         if offset is not None:

rope/ui/refactor.py

 import rope.refactor.extract
 import rope.refactor.importutils
 import rope.refactor.inline
+import rope.refactor.introduce_factory
 import rope.refactor.introduce_parameter
 import rope.refactor.localtofield
 import rope.refactor.move
 class RenameDialog(RefactoringDialog):
 
     def __init__(self, context, title, is_local=False, current_module=False):
-        resource = context.get_active_editor().get_file()
+        resource = context.resource
         editor = context.get_active_editor().get_editor()
         super(RenameDialog, self).__init__(context.project, title)
         self.is_local = is_local
 def extract_method(context):
     def do_extract(new_name):
         editor = context.get_active_editor().get_editor()
-        resource = context.get_active_editor().get_file()
+        resource = context.resource
         start_offset, end_offset = editor.get_region_offset()
         return rope.refactor.extract.ExtractMethodRefactoring(
             context.project, resource, start_offset,

ropetest/projecttest.py

         self.assertEquals('', root_folder.path)
         self.assertEquals('', root_folder.name)
 
-    def testGetAllFiles(self):
+    def test_get_all_files(self):
         files = self.project.get_files()
         self.assertEquals(1, len(files))
         self.assertEquals(self.sample_file, files[0].name)
 
-    def testMultifileGetAllFiles(self):
+    def test_multifile_get_all_files(self):
         fileName = 'nestedFile.txt'
         parent = self.project.get_resource(self.sample_folder)
         parent.create_file(fileName)
         my_file.write('\n')
         my_file.close()
 
+    def test_moving_and_being_interested_about_a_folder_and_a_child(self):
+        my_folder = self.project.root.create_folder('my_folder')
+        my_file = my_folder.create_file('my_file.txt')
+        sample_observer = _SampleObserver()
+        filtered_observer = FilteredResourceObserver(
+            sample_observer, [my_folder, my_file])
+        self.project.add_observer(filtered_observer)
+        my_folder.move('new_folder')
+        self.assertEquals(2, sample_observer.change_count)
+
+    def test_contains_for_folders(self):
+        folder1 = self.project.root.create_folder('folder')
+        folder2 = self.project.root.create_folder('folder2')
+        self.assertFalse(folder1.contains(folder2))
+
 
 class _MockTimeKeepter(object):
 

ropetest/refactor/__init__.py

         testutils.remove_recursively(self.project_root)
         super(IntroduceFactoryTest, self).tearDown()
 
-    def _perform_introduce_factory(self, resource, offset, factory_name,
+    def _introduce_factory(self, resource, offset, factory_name,
                                    global_factory=False):
         factory_introducer = IntroduceFactoryRefactoring(self.project,
                                                          resource, offset)
         expected = 'class AClass(object):\n    an_attr = 10\n\n' \
                    '    @staticmethod\n    def create(*args, **kwds):\n' \
                    '        return AClass(*args, **kwds)\n'
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
         self.assertEquals(expected, mod.read())
 
     def test_changing_occurances_in_the_main_module(self):
                    '    @staticmethod\n    def create(*args, **kwds):\n' \
                    '        return AClass(*args, **kwds)\n'\
                    'a_var = AClass.create()'
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
         self.assertEquals(expected, mod.read())
 
     def test_changing_occurances_with_arguments(self):
                    '    @staticmethod\n    def create(*args, **kwds):\n' \
                    '        return AClass(*args, **kwds)\n' \
                    'a_var = AClass.create(10)\n'
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
         self.assertEquals(expected, mod.read())
 
     def test_changing_occurances_in_other_modules(self):
         mod2 = self.pycore.create_module(self.project.root, 'mod2')
         mod1.write('class AClass(object):\n    an_attr = 10\n')
         mod2.write('import mod1\na_var = mod1.AClass()\n')
-        self._perform_introduce_factory(mod1, mod1.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod1, mod1.read().index('AClass') + 1, 'create')
         expected1 = 'class AClass(object):\n    an_attr = 10\n\n' \
                    '    @staticmethod\n    def create(*args, **kwds):\n' \
                    '        return AClass(*args, **kwds)\n'
     def test_raising_exception_for_non_classes(self):
         mod = self.pycore.create_module(self.project.root, 'mod')
         mod.write('def a_func():\n    pass\n')
-        self._perform_introduce_factory(mod, mod.read().index('a_func') + 1, 'create')
+        self._introduce_factory(mod, mod.read().index('a_func') + 1, 'create')
 
     def test_undoing_introduce_factory(self):
         mod1 = self.pycore.create_module(self.project.root, 'mod1')
         mod1.write(code1)
         code2 = 'from mod1 import AClass\na_var = AClass()\n'
         mod2.write(code2)
-        self._perform_introduce_factory(mod1, mod1.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod1, mod1.read().index('AClass') + 1, 'create')
         self.project.history.undo()
         self.assertEquals(code1, mod1.read())
         self.assertEquals(code2, mod2.read())
         mod2 = self.pycore.create_module(self.project.root, 'mod2')
         mod1.write('class AClass(object):\n    an_attr = 10\n')
         mod2.write('import mod1\na_var = mod1.AClass()\n')
-        self._perform_introduce_factory(mod2, mod2.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod2, mod2.read().index('AClass') + 1, 'create')
         expected1 = 'class AClass(object):\n    an_attr = 10\n\n' \
                    '    @staticmethod\n    def create(*args, **kwds):\n' \
                    '        return AClass(*args, **kwds)\n'
                    '        @staticmethod\n        def create(*args, **kwds):\n'\
                    '            return AClass(*args, **kwds)\n'\
                    '    return AClass.create()\n'
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
+        self._introduce_factory(mod, mod.read().index('AClass') + 1, 'create')
         self.assertEquals(expected, mod.read())
 
     def test_adding_factory_for_global_factories(self):
         expected = 'class AClass(object):\n    an_attr = 10\n\n' \
                    'def create(*args, **kwds):\n' \
                    '    return AClass(*args, **kwds)\n'
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1,
+        self._introduce_factory(mod, mod.read().index('AClass') + 1,
                                         'create', global_factory=True)
         self.assertEquals(expected, mod.read())
 
                '    return AClass()\n'
         mod = self.pycore.create_module(self.project.root, 'mod')
         mod.write(code)
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1,
+        self._introduce_factory(mod, mod.read().index('AClass') + 1,
                                            'create', global_factory=True)
 
     def test_changing_occurances_in_the_main_module_for_global_factories(self):
                    'def create(*args, **kwds):\n' \
                    '    return AClass(*args, **kwds)\n'\
                    'a_var = create()'
-        self._perform_introduce_factory(mod, mod.read().index('AClass') + 1,
+        self._introduce_factory(mod, mod.read().index('AClass') + 1,
                                            'create', global_factory=True)
         self.assertEquals(expected, mod.read())
 
         mod2 = self.pycore.create_module(self.project.root, 'mod2')
         mod1.write('class AClass(object):\n    an_attr = 10\n')
         mod2.write('import mod1\na_var = mod1.AClass()\n')
-        self._perform_introduce_factory(mod1, mod1.read().index('AClass') + 1,
+        self._introduce_factory(mod1, mod1.read().index('AClass') + 1,
                                            'create', global_factory=True)
         expected1 = 'class AClass(object):\n    an_attr = 10\n\n' \
                     'def create(*args, **kwds):\n' \
         mod2 = self.pycore.create_module(self.project.root, 'mod2')
         mod1.write('class AClass(object):\n    an_attr = 10\n')
         mod2.write('from mod1 import AClass\npair = AClass(), AClass\n')
-        self._perform_introduce_factory(mod1, mod1.read().index('AClass') + 1,
+        self._introduce_factory(mod1, mod1.read().index('AClass') + 1,
                                            'create', global_factory=True)
         expected1 = 'class AClass(object):\n    an_attr = 10\n\n' \
                     'def create(*args, **kwds):\n' \
                    '        return AClass(*args, **kwds)\n' \
                    'a_class = AClass\n' \
                    'a_var = a_class()'
-        self._perform_introduce_factory(mod, mod.read().index('a_class') + 1, 'create')
+        self._introduce_factory(mod, mod.read().index('a_class') + 1, 'create')
         self.assertEquals(expected, mod.read())
 
+    def test_changing_occurrences_in_the_same_module_with_conflicting_ranges(self):
+        mod = self.pycore.create_module(self.project.root, 'mod')
+        mod.write('class C(object):\n'
+                  '    def create(self):\n        return C()\n')
+        self._introduce_factory(mod, mod.read().index('C'), 'create_c', True)
+        self.assertTrue(mod.read().startswith(
+                        'class C(object):\n    def create(self):\n        return create_c()\n'))
+
     def _transform_module_to_package(self, resource):
         self.project.do(rope.refactor.TransformModuleToPackage(
                         self.project, resource).get_changes())
         self.assertEquals('import pkg.mod2\nfrom pkg.mod2 import AClass\n',
                           new_init.read())
 
+
 class EncapsulateFieldTest(unittest.TestCase):
 
     def setUp(self):
 
     def _perform_encapsulate_field(self, resource, offset):
         changes = EncapsulateFieldRefactoring(self.project, resource, offset).\
-                  encapsulate_field()
+                  get_changes()
         self.project.do(changes)
 
     def test_adding_getters_and_setters(self):

ropetest/ui/mockeditor.py

 from rope.ui.editor import EditorFactory
 
+
 class MockEditorFactory(EditorFactory):
 
     def create(self, *args, **kws):