Commits

Anonymous committed 4a31a93

Handling lambdas
Removing PythonRefactoring

Comments (0)

Files changed (44)

docs/dev/done.txt

 ===========
 
 
+- Handling lambdas : January 16, 2007
+
+
 - Handling builtin `property` : January 14, 2007
 
 

docs/dev/issues.txt

 * Undo unification
 
 
-Handling builtin `property`
-===========================
-
-Problems:
-
-* Class scopes does not reference class methods
-* Properties are overwritten in assignments
-
-We can add a new `Property` builtin type and when exploring attributes
-of a class we prevent `PyNames` with `Property` objects from being
-overwritten.  But the problem here is that we don't want the
-`PyObject`\s of `PyName`\s to be calculated for all attributes of a
-class.
-
-Another easier approach seems possible, too.  We only change static OI
-to give up when it encounters a property assigned to an attribute.
-When that happens, we can get the value it holds by getting the return
-value of the getting function for that property.
-
-
 Local History
 =============
 
 
 * Unanticipated changes
 * Having version control systems in parallel
+* Memory inefficiency
 * Saving removed files and folders
-* Memory inefficiency
 
 Each project change consists of a few basic changes.
 
 * create folder
 * remove resource
 
+A sample implementation is::
+
+  class History(object):
+
+      def __init__(self):
+          self.undo_list = []
+
+      def do(self, change):
+          pass
+
+      def undo(self):
+          pass
+
+      def redo(self):
+          pass
+
+There is a big question.  Can we do this?::
+
+  change = history.get_change()
+  change.undo()
+
+Or maybe this::
+
+  change = history.get_change()
+  history.do(change)
+  history.undo(change)
+  history.redo(change)
+
+Now what if we call undo with a change that is not done, yet.  Another
+problem is how to gain undo information before committing.
+
+Changes should be changed not to use `Resource` operations since
+resource operations should be undoable themselves.
+
+We can move all file related operations in one place.  The operations
+include:
+
+* write file
+* move resource
+* create file
+* create folder
+* remove resource
+
 
 Removing `PythonRefactoring` Facade
 ===================================
 
 * Changing uses of `PythonRefactoring` to use `rope.refactor` modules
 * Adding a place for saving undo information
-* What to do with import utils
 * Documenting the refactorings `rope.refactor` submodules provide
 * Updating references to `PythonRefactoring` and
   `PyCore.get_refactoring` to use the new objects.

docs/dev/library.txt

 
 * `rope.base.project.Project`
 * `rope.base.pycore.PyCore`
-* `rope.refactor.PythonRefactoring`
-* `rope.refactor` sub-modules
+* `rope.refactor`
 * `rope.ide.codeassist.PythonCodeAssist`
 
 
   >>> from rope.base.project import Project
   >>> project = Project('.')
 
-  >>> pycore = project.get_pycore()
-  >>> refactoring = project.get_pycore().get_refactoring()
-
   # Working with files to create a module
   >>> root = project.get_root_folder()
   >>> mod1 = root.create_file('mod1.py')
   >>> mod1.write('a_var = 10\n')
 
   # Creating modules and packages using `PyCore` objects
+  >>> pycore = project.get_pycore()
   >>> pkg = pycore.create_package(root, 'pkg')
   >>> mod2 = pycore.create_module(pkg, 'mod2')
   >>> mod2.write('import mod1\nprint mod1.a_var\n')
   # Performing rename refactoring on `mod1.a_var`
   >>> from rope.refactor.rename import RenameRefactoring
   >>> changes = RenameRefactoring(pycore, mod1, 1).get_changes('new_var')
-  >>> refactoring.add_and_commit_changes(changes)
+  >>> project.do(changes)
   >>> mod1.read()
   u'new_var = 10\n'
   >>> mod2.read()
   u'import mod1\nprint mod1.new_var\n'
 
   # Undoing rename refactoring
-  >>> refactoring.undo()
+  >>> project.history.undo()
   >>> mod1.read()
   u'a_var = 10\n'
   >>> mod2.read()

docs/dev/workingon.txt

-Handling Builtin `property`
-===========================
+Project History
+===============
 
-- Adding builtin `Property` type
-- Handling properties in static object inference
-- Fixing class scope lookup
-- Testing properties with methods
-- Handling `ClassScope.lookup` for ::
+- Handling Lambdas
+- Remove `Resource.observers`
+- Renaming `Resource._get_real_path()` to `Resource.real_path`
+- Undoing `MoveResource` when moving a file to a folder
+- Adding `historytest` to `runtests`
+- Removing `rope.ui.refactor._get_refactoring`
+- Removing `PythonRefactoring` occurrences
+- Changing refactorings to get project instead of pycore
+- Forcing `ChangeSets` to have a description
+- Import addition removes spaces after imports
 
-    class C(object):
-        a_var = 10
-        another_var = a_var
-
-* Handle lambdas
+* Adding imports eats the blank lines after it
+* Returning None from builtin:312 and builtins:292
+* evaluate:184 builtins:329
+* inline:41 inline:26 refactor:633 None and get_pyname
+* Moving `rope.refactor.change` to `rope.base`
+* Performing import actions after moving modules
+* Updating docs
+* Limiting the number of changes in the undo list
+* Showing the description of a change when undoing/redoing
+* Undo/redo list underflow
+* Undoing `Resource.remove()`
+* Renaming `Resource.get_path()` to `Resource.path`?
+* Renaming `Resource.get_name()` to `Resource.name`?
+* Separating undos; Editing history and file history
+* Docs for properties
+* Lambdas as functions
 
 
 Remaining Small Stories

rope/base/builtins.py

     return pyobjects.PyObject(Property(parameters[0]))
 
 
+class Lambda(pyobjects.PyObject):
+
+    def __init__(self, node, scope):
+        super(Lambda, self).__init__(pyobjects.PyObject.get_base_type('Function'))
+        self.node = node
+        self.scope = scope
+
+    def get_returned_object(self, args=None):
+        return evaluate.get_statement_result(
+            self.scope, self.node.code).get_object()
+
+    def get_pattributes(self):
+        return {}
+
+
 def _infer_sequence_type(seq):
     if '__iter__' in seq.get_attributes():
         iter = seq.get_attribute('__iter__').get_object().\

rope/base/evaluate.py

             self.result = rope.base.pynames.AssignedName(
                 pyobject=call_function.get_object().get_returned_object())
 
+    def visitLambda(self, node):
+        self.result = rope.base.pynames.AssignedName(
+            pyobject=rope.base.builtins.Lambda(node, self.scope))
+
 
 def get_statement_result(scope, node):
     evaluator = StatementEvaluator(scope)

rope/base/history.py

+from rope.refactor import change
+
+
+class History(object):
+
+    def __init__(self):
+        self._undo_list = []
+        self._redo_list = []
+
+    def do(self, changes):
+        self._undo_list.append(changes)
+        changes.do()
+
+    def undo(self):
+        change = self._undo_list.pop()
+        self._redo_list.append(change)
+        change.undo()
+
+    def redo(self):
+        change = self._redo_list.pop()
+        self._undo_list.append(change)
+        change.do()

rope/base/oi/dynamicoi.py

         resource = pyobject.get_module().get_resource()
         if resource is None:
             return
-        path = os.path.abspath(resource._get_real_path())
+        path = os.path.abspath(resource.real_path)
         lineno = pyobject._get_ast().lineno
         if path in self.files and lineno in self.files[path]:
             organizer = self.files[path][lineno]
 
     def _get_pymodule_path(self, pymodule):
         resource = pymodule.get_resource()
-        resource_path = resource.get_path()
+        resource_path = resource.path
         if os.path.isabs(resource_path):
             return resource_path
         return os.path.abspath(
         """Execute the process"""
         env = dict(os.environ)
         source_folders = []
-        file_path = self.file._get_real_path()
+        file_path = self.file.real_path
         for folder in self.pycore.get_source_folders():
-            source_folders.append(os.path.abspath(folder._get_real_path()))
+            source_folders.append(os.path.abspath(folder.real_path))
         env['PYTHONPATH'] = env.get('PYTHONPATH', '') + os.pathsep + \
                             os.pathsep.join(source_folders)
-        runmod_path = self.pycore.find_module('rope.base.oi.runmod')._get_real_path()
+        runmod_path = self.pycore.find_module('rope.base.oi.runmod').real_path
         self.receiver = None
         self._init_data_receiving()
         send_info = '-'
             send_info = self.receiver.get_send_info()
         args = [sys.executable, runmod_path, send_info,
                 os.path.abspath(self.pycore.project.address),
-                os.path.abspath(self.file._get_real_path())]
+                os.path.abspath(self.file.real_path)]
         if self.args is not None:
             args.extend(self.args)
         self.process = subprocess.Popen(

rope/base/oi/staticoi.py

                                              resulting_pyobject)
 
     def _call_function(self, pyfunction, function_name):
-        if function_name in pyfunction.get_attributes():
+        if pyfunction is not None and \
+           function_name in pyfunction.get_attributes():
             call_function = pyfunction.get_attribute(function_name)
             return call_function.get_object().get_returned_object()
 

rope/base/project.py

 
 import rope.base.fscommands
 import rope.base.pycore
+import rope.base.history
 from rope.base.exceptions import RopeException
+from rope.refactor import change
 
 
 class _Project(object):
 
     def __init__(self, fscommands):
-        self.fscommands = fscommands
-        self.robservers = {}
         self.observers = set()
+        self.file_access = rope.base.fscommands.FileAccess()
+        self._history = rope.base.history.History()
+        self.operations = change._ResourceOperations(self, fscommands)
 
     def get_resource(self, resource_name):
         """Get a resource in a project.
         if observer in self.observers:
             self.observers.remove(observer)
 
-    def _create_file(self, file_name):
-        file_path = self._get_resource_path(file_name)
-        if os.path.exists(file_path):
-            if os.path.isfile(file_path):
-                raise RopeException('File already exists')
-            else:
-                raise RopeException('A folder with the same name'
-                                    ' as this file already exists')
-        try:
-            self.fscommands.create_file(file_path)
-        except IOError, e:
-            raise RopeException(e)
-
-    def _create_folder(self, folder_name):
-        folder_path = self._get_resource_path(folder_name)
-        if os.path.exists(folder_path):
-            if not os.path.isdir(folder_path):
-                raise RopeException('A file with the same name as'
-                                    ' this folder already exists')
-            else:
-                raise RopeException('Folder already exists')
-        self.fscommands.create_folder(folder_path)
-
     def _get_resource_path(self, name):
         pass
 
-    def remove_recursively(self, path):
-        self.fscommands.remove(path)
+    history = property(lambda self: self._history)
 
 
 class Project(_Project):
     def get_out_of_project_resource(self, path):
         return self.no_project.get_resource(path)
 
+    def do(self, changes):
+        """Apply the changes in a `ChangeSet`
+
+        Most of the time you call this function for committing the
+        changes for a refactoring.
+        """
+        self.history.do(changes)
+
 
 class NoProject(_Project):
     """A null object for holding out of project files"""
 class Resource(object):
     """Represents files and folders in a project"""
 
-    def __init__(self, project, name):
+    def __init__(self, project, path):
         self.project = project
-        self.name = name
-
-    def _get_observers(self):
-        return self.project.observers
-
-    observers = property(_get_observers)
+        self._path = path
 
     def get_path(self):
         """Return the path of this resource relative to the project root
         The path is the list of parent directories separated by '/' followed
         by the resource name.
         """
-        return self.name
+        return self._path
 
     def get_name(self):
         """Return the name of this resource"""
-        return self.name.split('/')[-1]
+        return self.path.split('/')[-1]
 
     def move(self, new_location):
-        """Move resource to new_lcation"""
-        destination = self._get_destination_for_move(new_location)
-        self.project.fscommands.move(self._get_real_path(),
-                                     self.project._get_resource_path(destination))
-        new_resource = self.project.get_resource(destination)
-        for observer in list(self.observers):
-            observer.resource_removed(self, new_resource)
+        """Move resource to `new_location`"""
+        self._perform_change(change.MoveResource(self, new_location),
+                             'Moving <%s> to <%s>' % (self.path, new_location))
 
     def remove(self):
         """Remove resource from the project"""
-        self.project.remove_recursively(self._get_real_path())
-        for observer in list(self.observers):
-            observer.resource_removed(self)
+        self._perform_change(change.RemoveResource(self),
+                             'Removing <%s>' % self.path)
 
     def is_folder(self):
         """Return true if the resource is a folder"""
 
     def exists(self):
-        return os.path.exists(self._get_real_path())
+        return os.path.exists(self.real_path)
 
     def get_parent(self):
-        parent = '/'.join(self.name.split('/')[0:-1])
+        parent = '/'.join(self.path.split('/')[0:-1])
         return self.project.get_resource(parent)
 
     def _get_real_path(self):
         """Return the file system path of this resource"""
-        return self.project._get_resource_path(self.name)
+        return self.project._get_resource_path(self.path)
 
     def _get_destination_for_move(self, destination):
         dest_path = self.project._get_resource_path(destination)
         return destination
 
     def __eq__(self, obj):
-        return self.__class__ == obj.__class__ and self.name == obj.name
+        return self.__class__ == obj.__class__ and self.path == obj.path
 
     def __hash__(self):
-        return hash(self.name)
+        return hash(self.path)
+
+    name = property(get_name)
+    path = property(get_path)
+    real_path = property(_get_real_path)
+
+    def _perform_change(self, change_, description):
+        changes = change.ChangeSet(description)
+        changes.add_change(change_)
+        self.project.do(changes)
 
 
 class File(Resource):
 
     def __init__(self, project, name):
         super(File, self).__init__(project, name)
-        self.file_access = rope.base.fscommands.FileAccess()
 
     def read(self):
-        return self.file_access.read(self._get_real_path())
+        return self.project.file_access.read(self.real_path)
 
     def write(self, contents):
-        self.file_access.write(self._get_real_path(), contents)
-        for observer in list(self.observers):
-            observer.resource_changed(self)
+        self._perform_change(change.ChangeContents(self, contents),
+                             'Writing file <%s>' % self.path)
 
     def is_folder(self):
         return False
 
     def get_children(self):
         """Return the children of this folder"""
-        path = self._get_real_path()
+        path = self.real_path
         result = []
         content = os.listdir(path)
         for name in content:
             if name.endswith('.pyc') or name == '.svn' or name.endswith('~'):
                 continue
-            if self.get_path() != '':
-                resource_name = self.get_path() + '/' + name
+            if self.path != '':
+                resource_name = self.path + '/' + name
             else:
                 resource_name = name
             result.append(self.project.get_resource(resource_name))
         return result
 
     def create_file(self, file_name):
-        if self.get_path():
-            file_path = self.get_path() + '/' + file_name
-        else:
-            file_path = file_name
-        self.project._create_file(file_path)
-        child = self.get_child(file_name)
-        for observer in list(self.observers):
-            observer.resource_changed(child)
-        return child
+        self._perform_change(
+            change.CreateFile(self, file_name),
+            'Creating file <%s>' % (self.path + '/' + file_name))
+        return self.get_child(file_name)
 
     def create_folder(self, folder_name):
-        if self.get_path():
-            folder_path = self.get_path() + '/' + folder_name
-        else:
-            folder_path = folder_name
-        self.project._create_folder(folder_path)
-        child = self.get_child(folder_name)
-        for observer in list(self.observers):
-            observer.resource_changed(child)
-        return child
+        self._perform_change(
+            change.CreateFolder(self, folder_name),
+            'Creating golder <%s>' % (self.path + '/' + folder_name))
+        return self.get_child(folder_name)
 
     def get_child(self, name):
-        if self.get_path():
-            child_path = self.get_path() + '/' + name
+        if self.path:
+            child_path = self.path + '/' + name
         else:
             child_path = name
         return self.project.get_resource(child_path)
         return result
 
     def contains(self, resource):
-        return self != resource and resource.get_path().startswith(self.get_path())
-
-    def _child_changed(self, child):
-        if child != self:
-            for observer in list(self.observers):
-                observer.resource_changed(self)
+        return self != resource and 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.get_path()[:len(main.get_path())]
-        return new_main.get_path() + diff
+        diff = resource.path[:len(main.path)]
+        return new_main.path + diff
 
 
 class Timekeeper(object):
 
     def getmtime(self, resource):
         """Return the modification time of a `Resource`."""
-        return os.path.getmtime(resource._get_real_path())
+        return os.path.getmtime(resource.real_path)
 
 
 class _Changes(object):

rope/base/pycore.py

         self.module_map = {}
         self.call_info = rope.base.oi.dynamicoi.CallInformationCollector(self)
         self.object_infer = rope.base.oi.objectinfer.ObjectInfer(self)
-        self.refactoring = rope.refactor.PythonRefactoring(self)
         self.classes = None
         observer = rope.base.project.ResourceObserver(
             self._invalidate_resource_cache, self._invalidate_resource_cache)
     def _get_object_infer(self):
         return self.object_infer
 
-    def get_refactoring(self):
-        return self.refactoring
-
     def _invalidate_all_concluded_data(self):
         for module in self.module_map.values():
             module._invalidate_concluded_data()

rope/ide/formatter.py

 #
 #  So a formatter would be added some time in the future,
 #  maybe by rope's contributing users.
+
+
 class Formatter(object):
 
     def format(self, source_code):

rope/refactor/__init__.py

 """rope refactor package
 
 This package contains modules that perform python
-refactorings.
+refactorings.  Refactoring classes perform refactorings
+in 4 steps:
+
+1. Collect some data for performing the refactoring and use them
+   to construct a refactoring class.  Like::
+
+     renamer = RenameRefactoring(project, resource, offset)
+
+2. Some refactorings give you useful information about the
+   refactoring after their construction.  Like::
+
+     print renamer.get_old_name()
+
+3. Give the refactoring class more information about how to
+   perform the refactoring and get the changes this refactoring
+   is going to make.  This is done by calling `get_changes`
+   method of the refactoring class.  Like::
+
+     changes = renamer.get_changes(new_name)
+
+4. You can commit the changes.  Like::
+
+     project.do(changes)
+
+These steps are like the steps IDEs usually do for performing
+a refactoring.  These are the things an IDE does in each step:
+
+1. Construct a refactoring object by giving it information like
+   resource, offset and ... .  Some of the refactoring problems
+   (like performing rename refactoring on keywords) can be
+   reported here.
+2. Print some information about the refactoring and ask the user
+   about the information that are necessary for completing the
+   refactoring (like new name).
+3. Call the `get_changes` by passing it information asked from
+   the user (if necessary) and get and preview the changes returned
+   by it.
+4. perform the refactoring.
+
 
 """
 import rope.refactor.importutils
-from rope.refactor.change import (ChangeSet, ChangeContents,
-                                  MoveResource, CreateFolder)
-from rope.refactor.encapsulate_field import EncapsulateFieldRefactoring
-from rope.refactor.extract import (ExtractMethodRefactoring,
-                                   ExtractVariableRefactoring)
+from rope.refactor.change import ChangeSet, ChangeContents, MoveResource, CreateFolder
 from rope.refactor.importutils import module_imports
-from rope.refactor.inline import InlineRefactoring
-from rope.refactor.introduce_factory import IntroduceFactoryRefactoring
-from rope.refactor.localtofield import ConvertLocalToFieldRefactoring
-from rope.refactor.move import MoveRefactoring
-from rope.refactor.rename import RenameRefactoring
 
 
-class PythonRefactoring(object):
-    """A facade for Rope refactorings
+class TransformModuleToPackage(object):
 
-    This class acts as a facade for refactorings supported by rope.
-    But this interface is not designed for IDEs.  The methods
-    perform a refactoring in one step, while IDEs usually do
-    refactorings in these steps:
+    def __init__(self, project, resource):
+        self.project = project
+        self.pycore = project.pycore
+        self.resource = resource
 
-    1. Collect some initial data, like initial name in rename
-       refactoring, and report found problems.
-    2. Ask needed information, like new name in rename refactoring,
-       from the user.
-    3. Analyze the the refactoring and preview the changes this
-       refactoring makes or report problems.
-    4. Commit the changes.
-
-    If you need these steps you may use the modules under `rope.
-    refactor` yourself.  The refactoring classes have a `get_changes`
-    method that returns the changes this refactoring makes (step 3).
-    you should use `add_and_commit_changes` to use refactoring undo/
-    redo.
-
-    Note: This class will probably be removed sometime before ``0.4``
-    release.
-
-    """
-
-    def __init__(self, pycore):
-        self.pycore = pycore
-        self._undo = Undo()
-
-    def local_rename(self, resource, offset, new_name):
-        changes = RenameRefactoring(self.pycore, resource, offset).\
-                  get_changes(new_name, in_file=True)
-        self.add_and_commit_changes(changes)
-
-    def rename(self, resource, offset, new_name):
-        changes = RenameRefactoring(self.pycore, resource, offset).\
-                  get_changes(new_name)
-        self.add_and_commit_changes(changes)
-
-    def extract_method(self, resource, start_offset, end_offset,
-                       extracted_name):
-        changes = ExtractMethodRefactoring(self.pycore, resource,
-                                           start_offset, end_offset).\
-                                           get_changes(extracted_name)
-        self.add_and_commit_changes(changes)
-
-    def extract_variable(self, resource, start_offset, end_offset,
-                         extracted_name):
-        changes = ExtractVariableRefactoring(self.pycore, resource,
-                                             start_offset, end_offset).\
-                                             get_changes(extracted_name)
-        self.add_and_commit_changes(changes)
-
-    def transform_module_to_package(self, resource):
-        changes = ChangeSet()
-        new_content = self._transform_relatives_to_absolute(resource)
+    def get_changes(self):
+        changes = ChangeSet('Transform <%s> module to package' %
+                            self.resource.path)
+        new_content = self._transform_relatives_to_absolute(self.resource)
         if new_content is not None:
-            changes.add_change(ChangeContents(resource, new_content))
-        parent = resource.get_parent()
-        name = resource.get_name()[:-3]
+            changes.add_change(ChangeContents(self.resource, new_content))
+        parent = self.resource.get_parent()
+        name = self.resource.get_name()[:-3]
         changes.add_change(CreateFolder(parent, name))
-        new_path = parent.get_path() + '/%s/__init__.py' % name
-        changes.add_change(MoveResource(resource, new_path))
-        self.add_and_commit_changes(changes)
+        new_path = parent.path + '/%s/__init__.py' % name
+        changes.add_change(MoveResource(self.resource, new_path))
+        return changes
 
     def _transform_relatives_to_absolute(self, resource):
         pymodule = self.pycore.resource_to_pyobject(resource)
         import_tools = rope.refactor.importutils.ImportTools(self.pycore)
         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)
-        changes = factory_introducer.get_changes(factory_name, global_factory)
-        self.add_and_commit_changes(changes)
-
-    def move(self, resource, offset, dest_resource):
-        changes = MoveRefactoring(self.pycore, resource, offset).\
-                  get_changes(dest_resource)
-        self.add_and_commit_changes(changes)
-
-    def inline(self, resource, offset):
-        changes = InlineRefactoring(self.pycore, resource, offset).get_changes()
-        self.add_and_commit_changes(changes)
-
-    def encapsulate_field(self, resource, offset):
-        changes = EncapsulateFieldRefactoring(self.pycore, resource, offset).\
-                  encapsulate_field()
-        self.add_and_commit_changes(changes)
-
-    def convert_local_variable_to_field(self, 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):
-        """Commit the changes and add them to undo list"""
-        self._undo.add_change(changes)
-        changes.do()
-
-    def undo(self):
-        self._undo.undo()
-
-    def redo(self):
-        self._undo.redo()
-
-    def get_import_organizer(self):
-        return ImportOrganizer(self)
-
 
 class ImportOrganizer(object):
 
-    def __init__(self, refactoring):
-        self.refactoring = refactoring
-        self.pycore = refactoring.pycore
+    def __init__(self, project):
+        self.project = project
+        self.pycore = project.pycore
         self.import_tools = rope.refactor.importutils.ImportTools(self.pycore)
 
     def _perform_command_on_module_with_imports(self, resource, method):
         before_performing = pymodule.source_code
         result = method(pymodule)
         if result is not None and result != before_performing:
-            changes = ChangeSet()
+            changes = ChangeSet(method.__name__.replace('_', ' ') +
+                                ' in <%s>' % resource.path)
             changes.add_change(ChangeContents(resource, result))
-            self.refactoring.add_and_commit_changes(changes)
+            return changes
 
     def organize_imports(self, resource):
         return self._perform_command_on_import_tools(
         source = self._perform_command_on_module_with_imports(
             resource, module_imports.ModuleImports.expand_stars)
         if source is not None:
-            changes = ChangeSet()
+            changes = ChangeSet('Expanding stars for <%s>' % resource.path)
             changes.add_change(ChangeContents(resource, source))
-            self.refactoring.add_and_commit_changes(changes)
+            return changes
 
     def transform_froms_to_imports(self, resource):
         return self._perform_command_on_import_tools(
         return self._perform_command_on_import_tools(
             self.import_tools.handle_long_imports, resource)
 
-
-class Undo(object):
-
-    def __init__(self):
-        self._undo_list = []
-        self._redo_list = []
-
-    def add_change(self, change):
-        self._undo_list.append(change)
-        del self._redo_list[:]
-
-    def undo(self):
-        change = self._undo_list.pop()
-        self._redo_list.append(change)
-        change.undo()
-
-    def redo(self):
-        change = self._redo_list.pop()
-        self._undo_list.append(change)
-        change.do()

rope/refactor/change.py

 import difflib
+import os
+
+from rope.base.exceptions import RopeException
 
 
 class Change(object):
 
-    def do(self):
+    def do(self, resource_operations):
         pass
 
-    def undo(self):
+    def undo(self, resource_operations):
         pass
 
     def get_description(self):
 
 class ChangeSet(Change):
 
-    def __init__(self):
+    def __init__(self, description):
         self.changes = []
+        self.description = description
 
     def do(self):
         try:
     def add_change(self, change):
         self.changes.append(change)
 
+    def get_description(self):
+        return self.description
+
 
 class ChangeContents(Change):
 
         self.resource = resource
         self.new_content = new_content
         self.old_content = None
+        self.operations = self.resource.project.operations
 
     def do(self):
         self.old_content = self.resource.read()
-        self.resource.write(self.new_content)
+        self.operations.write_file(self.resource, self.new_content)
 
     def undo(self):
-        self.resource.write(self.old_content)
+        self.operations.write_file(self.resource, self.old_content)
 
     def __str__(self):
-        return 'Change <%s>' % self.resource.get_path()
+        return 'Change <%s>' % self.resource.path
 
     def get_description(self):
         differ = difflib.Differ()
 
     def __init__(self, resource, new_location):
         self.project = resource.project
-        self.new_location = new_location
-        self.old_location = resource.get_path()
+        self.new_location = _get_destination_for_move(resource, new_location)
+        self.old_location = resource.path
+        self.operations = resource.project.operations
 
     def do(self):
         resource = self.project.get_resource(self.old_location)
-        resource.move(self.new_location)
+        self.operations.move(resource, self.new_location)
 
     def undo(self):
         resource = self.project.get_resource(self.new_location)
-        resource.move(self.old_location)
+        self.operations.move(resource, self.old_location)
 
     def __str__(self):
         return 'Move <%s>' % self.old_location
         return 'Move <%s> to <%s>' % (self.old_location, self.new_location)
 
 
-class CreateFolder(Change):
+class _CreateResource(Change):
 
     def __init__(self, parent, name):
         self.parent = parent
         self.name = name
-        self.new_folder = None
+        self.new_resource = None
+        self.operations = self.parent.project.operations
+
+    def undo(self):
+        self.operations.remove(self.new_resource)
+
+    def __str__(self):
+        return 'Create <%s>' % (self.parent.path + '/' + self.name)
+
+
+class CreateFolder(_CreateResource):
 
     def do(self):
-        self.new_folder = self.parent.create_folder(self.name)
+        self.new_resource = self.operations.create_folder(self.parent,
+                                                          self.name)
 
+
+class CreateFile(_CreateResource):
+
+    def do(self):
+        self.new_resource = self.operations.create_file(self.parent,
+                                                        self.name)
+
+
+class RemoveResource(Change):
+
+    def __init__(self, resource):
+        self.resource = resource
+        self.operations = resource.project.operations
+
+    def do(self):
+        self.operations.remove(self.resource)
+
+    # TODO: Undoing remove operations
     def undo(self):
-        self.new_folder.remove()
+        pass
 
     def __str__(self):
-        return 'Create Folder <%s>' % (self.parent.get_path() + '/' + self.name)
+        return 'Remove <%s>' % (self.resource.path)
 
+
+class _ResourceOperations(object):
+
+    def __init__(self, project, fscommands):
+        self.project = project
+        self.fscommands = fscommands
+
+    def write_file(self, resource, contents):
+        self.project.file_access.write(resource.real_path, contents)
+        for observer in list(self.project.observers):
+            observer.resource_changed(resource)
+
+    def move(self, resource, new_location):
+        destination = _get_destination_for_move(resource, new_location)
+        self.fscommands.move(resource.real_path,
+                             self.project._get_resource_path(destination))
+        new_resource = self.project.get_resource(destination)
+        for observer in list(self.project.observers):
+            observer.resource_removed(resource, new_resource)
+
+    def create_file(self, folder, file_name):
+        if folder.path:
+            file_path = folder.path + '/' + file_name
+        else:
+            file_path = file_name
+        self._create_file(file_path)
+        child = folder.get_child(file_name)
+        for observer in list(self.project.observers):
+            observer.resource_changed(child)
+        return child
+
+    def create_folder(self, folder, folder_name):
+        if folder.path:
+            folder_path = folder.path + '/' + folder_name
+        else:
+            folder_path = folder_name
+        self._create_folder(folder_path)
+        child = folder.get_child(folder_name)
+        for observer in list(self.project.observers):
+            observer.resource_changed(child)
+        return child
+
+    def remove(self, resource):
+        self.fscommands.remove(resource.real_path)
+        for observer in list(self.project.observers):
+            observer.resource_removed(resource)
+
+    def _create_file(self, file_name):
+        file_path = self.project._get_resource_path(file_name)
+        if os.path.exists(file_path):
+            if os.path.isfile(file_path):
+                raise RopeException('File already exists')
+            else:
+                raise RopeException('A folder with the same name'
+                                    ' as this file already exists')
+        try:
+            self.fscommands.create_file(file_path)
+        except IOError, e:
+            raise RopeException(e)
+
+    def _create_folder(self, folder_name):
+        folder_path = self.project._get_resource_path(folder_name)
+        if os.path.exists(folder_path):
+            if not os.path.isdir(folder_path):
+                raise RopeException('A file with the same name as'
+                                    ' this folder already exists')
+            else:
+                raise RopeException('Folder already exists')
+        self.fscommands.create_folder(folder_path)
+
+
+def _get_destination_for_move(resource, destination):
+    dest_path = resource.project._get_resource_path(destination)
+    if os.path.isdir(dest_path):
+        if destination != '':
+            return destination + '/' + resource.get_name()
+        else:
+            return resource.get_name()
+    return destination
+

rope/refactor/change_signature.py

 
 class ChangeSignature(object):
 
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset):
+        self.pycore = project.pycore
         self.resource = resource
         self.offset = offset
         self.name = codeanalyze.get_name_at(resource, offset)
-        self.pyname = codeanalyze.get_pyname_at(pycore, resource, offset)
+        self.pyname = codeanalyze.get_pyname_at(self.pycore, resource, offset)
         if self.pyname is None or self.pyname.get_object() is None or \
            not isinstance(self.pyname.get_object(), rope.base.pyobjects.PyFunction):
             raise rope.base.exceptions.RefactoringException(
                 'Change method signature should be performed on functions')
 
     def _change_calls(self, call_changer):
-        changes = ChangeSet()
+        changes = ChangeSet('Changing signature of <%s>' % self.name)
         finder = rope.refactor.occurrences.FilteredOccurrenceFinder(
             self.pycore, self.name, [self.pyname])
         for file in self.pycore.get_python_files():

rope/refactor/encapsulate_field.py

 
 class EncapsulateFieldRefactoring(object):
 
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset):
+        self.pycore = project.pycore
         self.name = rope.base.codeanalyze.get_name_at(resource, offset)
-        self.pyname = rope.base.codeanalyze.get_pyname_at(pycore, resource, offset)
+        self.pyname = rope.base.codeanalyze.get_pyname_at(self.pycore,
+                                                          resource, offset)
         if not self._is_an_attribute(self.pyname):
             raise rope.base.exceptions.RefactoringException(
                 'Encapsulate field should be performed on class attributes.')
         return False
 
     def encapsulate_field(self):
-        changes = ChangeSet()
+        changes = ChangeSet('Encapsulate field <%s>' % self.name)
         rename_in_module = GetterSetterRenameInModule(self.pycore, self.name,
                                                       [self.pyname])
 

rope/refactor/extract.py

 
 class _ExtractRefactoring(object):
 
-    def __init__(self, pycore, resource, start_offset, end_offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, start_offset, end_offset):
+        self.pycore = project.pycore
         self.resource = resource
         self.start_offset = start_offset
         self.end_offset = end_offset
 
+
 class ExtractMethodRefactoring(_ExtractRefactoring):
 
     def get_changes(self, extracted_name):
         else:
             new_contents = _MultiLineExtractPerformer(self.pycore, self.resource, info,
                                                       extracted_name).extract()
-        changes = ChangeSet()
+        changes = ChangeSet('Extract method <%s>' % extracted_name)
         changes.add_change(ChangeContents(self.resource, new_contents))
         return changes
 
                                    self.start_offset, self.end_offset)
         new_contents = _OneLineExtractPerformer(self.pycore, self.resource, info,
                                                 extracted_name, True).extract()
-        changes = ChangeSet()
+        changes = ChangeSet('Extract variable <%s>' % extracted_name)
         changes.add_change(ChangeContents(self.resource, new_contents))
         return changes
 

rope/refactor/importutils/actions.py

 
 from rope.base import pyobjects, exceptions
 from rope.refactor.importutils import importinfo
+from rope.base import exceptions
 
 
 class ImportInfoVisitor(object):
 
     def dispatch(self, import_):
-        method = getattr(self, 'visit' + import_.import_info.__class__.__name__)
-        return method(import_, import_.import_info)
+        try:
+            method = getattr(self, 'visit' + import_.import_info.__class__.__name__)
+            return method(import_, import_.import_info)
+        except exceptions.ModuleNotFoundException:
+            pass
 
     def visitEmptyImport(self, import_stmt, import_info):
         pass

rope/refactor/importutils/importinfo.py

 
     def empty_import(self):
         self.import_info = ImportInfo.get_empty_import()
-    
+
     def move(self, lineno, blank_lines=0):
         self.new_start = lineno
         self.blank_lines = blank_lines

rope/refactor/importutils/module_imports.py

         imports = [stmt for stmt in self.get_import_statements()
                    if stmt.is_changed()]
         after_removing = self._remove_imports(imports)
-        
+
         result = []
         last_index = 0
         for stmt in sorted(imports, self._compare_import_locations):
             last_index = self._first_non_blank_line(after_removing, start - 1)
             if not stmt.import_info.is_empty():
                 result.append(stmt.get_import_statement() + '\n')
-                for i in range(stmt.blank_lines):
-                    result.append('\n')
+                if self._first_non_blank_line(after_removing, last_index) < \
+                   len(after_removing):
+                    result.append('\n' * stmt.blank_lines)
         result.extend(after_removing[last_index:])
         return ''.join(result)
 
         else:
             all_imports = self.get_import_statements()
             last_line = 1
+            blank_lines = 0
             if all_imports:
                 last_line = all_imports[-1].end_line
+                all_imports[-1].move(last_line)
+                blank_lines = all_imports[-1].blank_lines
             all_imports.append(importinfo.ImportStatement(
-                               import_info, last_line, last_line))
+                               import_info, last_line, last_line,
+                               blank_lines=blank_lines))
 
     def filter_names(self, can_select):
         visitor = actions.RemovingVisitor(self.pycore, can_select)

rope/refactor/inline.py

 
 class InlineRefactoring(object):
 
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset):
+        self.pycore = project.pycore
         self.pyname = codeanalyze.get_pyname_at(self.pycore, resource, offset)
         self.name = codeanalyze.get_name_at(resource, offset)
         if self.name is None:
             raise rope.base.exceptions.RefactoringException(
                 'Inline refactoring should be performed on a method/local variable.')
         if self._is_variable():
-            self.performer = _VariableInliner(pycore, self.name, self.pyname)
+            self.performer = _VariableInliner(self.pycore, self.name,
+                                              self.pyname)
         elif self._is_method():
-            self.performer = _MethodInliner(pycore, self.name, self.pyname)
+            self.performer = _MethodInliner(self.pycore, self.name,
+                                            self.pyname)
         else:
             raise rope.base.exceptions.RefactoringException(
                 'Inline refactoring should be performed on a method/local variable.')
         return (start_offset, end_offset)
 
     def get_changes(self):
-        changes = ChangeSet()
+        changes = ChangeSet('Inline method <%s>' % self.name)
         self._change_defining_file(changes)
         self._change_other_files(changes)
         return changes
 
     def get_changes(self):
         source = self._get_changed_module()
-        changes = ChangeSet()
+        changes = ChangeSet('Inline variable <%s>' % self.name)
         changes.add_change(ChangeContents(self.resource, source))
         return changes
 
             if not occurrence.is_called():
                     raise rope.base.exceptions.RefactoringException(
                         'Reference to inlining function other than function call'
-                        ' in <file: %s, offset: %d>' % (self.resource.get_path(),
+                        ' in <file: %s, offset: %d>' % (self.resource.path,
                                                         start))
             end_parens = self._find_end_parens(self.source,
                                                self.source.index('(', end))

rope/refactor/introduce_factory.py

 
 class IntroduceFactoryRefactoring(object):
 
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset):
+        self.pycore = project.pycore
         self.offset = offset
 
         self.old_pyname = \
         self.resource = self.pymodule.get_resource()
 
     def get_changes(self, factory_name, global_factory=False):
-        changes = ChangeSet()
+        changes = ChangeSet('Introduce factory method <%s>' % factory_name)
         self._change_occurrences_in_other_modules(changes, factory_name,
                                                   global_factory)
         self._change_resource(changes, factory_name, global_factory)

rope/refactor/introduce_parameter.py

 
 class IntroduceParameter(object):
 
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset):
+        self.pycore = project.pycore
         self.resource = resource
         self.offset = offset
         self.pymodule = self.pycore.resource_to_pyobject(self.resource)
         change_collector.add_change(start, header_end, definition_info.to_string())
         self._change_function_occurances(change_collector, header_end,
                                          end, new_parameter)
-        changes = change.ChangeSet()
+        changes = change.ChangeSet('Introduce parameter <%s>' % new_parameter)
         changes.add_change(change.ChangeContents(self.resource,
                                                  change_collector.get_changed()))
         return changes

rope/refactor/localtofield.py

 
 class ConvertLocalToFieldRefactoring(object):
 
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset):
+        self.project = project
+        self.pycore = project.pycore
         self.resource = resource
         self.offset = offset
 
-    def convert_local_variable_to_field(self):
+    def get_changes(self):
         name = rope.base.codeanalyze.get_name_at(self.resource, self.offset)
         pyname = rope.base.codeanalyze.get_pyname_at(self.pycore, self.resource, self.offset)
         if not self._is_a_method_local(pyname):
                 'The field %s already exists' % name)
 
         new_name = self._get_field_name(function_scope.pyobject, name)
-        changes = RenameRefactoring(self.pycore, self.resource, self.offset).\
+        changes = RenameRefactoring(self.project, self.resource, self.offset).\
                   get_changes(new_name, in_file=True)
         return changes
 

rope/refactor/move.py

 class MoveRefactoring(object):
     """A class for moving modules, packages, global functions and classes."""
 
-    def __init__(self, pycore, resource, offset=None):
-        self.pycore = pycore
+    def __init__(self, project, resource, offset=None):
+        self.pycore = project.pycore
         if offset is not None:
-            self.pyname = rope.base.codeanalyze.get_pyname_at(self.pycore, resource, offset)
+            self.pyname = rope.base.codeanalyze.get_pyname_at(
+                self.pycore, resource, offset)
             if self.pyname is None:
                 raise rope.base.exceptions.RefactoringException(
                     'Move works on classes,functions or modules.')
             if not resource.is_folder() and resource.get_name() == '__init__.py':
                 resource = resource.get_parent()
             dummy_pymodule = self.pycore.get_string_module('')
-            self.pyname = rope.base.pynames.ImportedModule(dummy_pymodule, resource=resource)
+            self.pyname = rope.base.pynames.ImportedModule(
+                dummy_pymodule, resource=resource)
 
     def get_changes(self, dest_resource):
         moving_object = self.pyname.get_object()
     def _remove_old_pyname_imports(self, pymodule):
         old_source = pymodule.source_code
         module_with_imports = self.import_tools.get_module_with_imports(pymodule)
-        def can_select(name):
-            try:
-                if name == self.old_name and \
-                   pymodule.get_attribute(name).get_object() == self.old_pyname.get_object():
-                    return False
-            except rope.base.exceptions.AttributeNotFoundException:
-                pass
-            return True
+        class CanSelect(object):
+            changed = False
+            old_name = self.old_name
+            old_pyname = self.old_pyname
+            def __call__(self, name):
+                try:
+                    if name == self.old_name and \
+                       pymodule.get_attribute(name).get_object() == \
+                       self.old_pyname.get_object():
+                        self.changed = True
+                        return False
+                except rope.base.exceptions.AttributeNotFoundException:
+                    pass
+                return True
+        can_select = CanSelect()
         module_with_imports.filter_names(can_select)
         new_source = module_with_imports.get_changed_source()
         if old_source != new_source:
-            pymodule = self.pycore.get_string_module(new_source, pymodule.get_resource())
-            return pymodule, True
-        return pymodule, False
+            pymodule = self.pycore.get_string_module(new_source,
+                                                     pymodule.get_resource())
+        return pymodule, can_select.changed
 
     def _rename_in_module(self, pymodule, new_name, imports=False):
         occurrence_finder = occurrences.FilteredOccurrenceFinder(
         return pyobject.get_scope().parent == pyobject.get_module().get_scope()
 
     def move(self):
-        changes = ChangeSet()
+        changes = ChangeSet('Moving global <%s>' % self.old_name)
         self._change_destination_module(changes)
         self._change_source_module(changes)
         self._change_other_modules(changes)
                 'Move destination for modules should be packages.')
 
     def move(self):
-        changes = ChangeSet()
+        changes = ChangeSet('Moving module <%s>' % self.old_name)
         self._change_other_modules(changes)
         self._change_moving_module(changes)
         return changes
             if is_changed:
                 changes.add_change(ChangeContents(self.source, source))
         changes.add_change(MoveResource(self.source,
-                                        self.destination.get_path()))
+                                        self.destination.path))
 
     def _change_other_modules(self, changes):
         for module in self.pycore.get_python_files():

rope/refactor/rename.py

 import rope.base.exceptions
 import rope.base.pynames
 import rope.base.pyobjects
-from rope.refactor import occurrences
-from rope.refactor import sourceutils
+from rope.refactor import occurrences, sourceutils
 from rope.refactor.change import ChangeSet, ChangeContents, MoveResource
 
 
 class RenameRefactoring(object):
 
-    def __init__(self, pycore, resource, offset=None):
+    def __init__(self, project, resource, offset=None):
         """If `offset` is None `resource` will be renamed"""
-        self.pycore = pycore
+        self.pycore = project.pycore
         self.resource = resource
         if offset is not None:
             self.old_name = rope.base.codeanalyze.get_name_at(self.resource, offset)
-            self.old_pyname = rope.base.codeanalyze.get_pyname_at(self.pycore, resource,
-                                                                  offset)
+            self.old_pyname = rope.base.codeanalyze.get_pyname_at(
+                self.pycore, resource, offset)
             if self.old_pyname is None:
                 raise rope.base.exceptions.RefactoringException(
                     'Rename refactoring should be performed on python identifiers.')
            self._is_renaming_a_function_local_name():
             in_file = True
         files = self._get_interesting_files(in_file)
-        changes = ChangeSet()
+        changes = ChangeSet('Renaming <%s> to <%s>' %
+                            (self.old_name, new_name))
         for file_ in files:
-            occurance_finder = occurrences.FilteredOccurrenceFinder(self.pycore, self.old_name, old_pynames)
-            new_content = rename_in_module(occurance_finder, new_name, resource=file_)
+            occurance_finder = occurrences.FilteredOccurrenceFinder(
+                self.pycore, self.old_name, old_pynames)
+            new_content = rename_in_module(occurance_finder, new_name,
+                                           resource=file_)
             if new_content is not None:
                 changes.add_change(ChangeContents(file_, new_content))
 
         resource = pyobject.get_resource()
         if not resource.is_folder():
             new_name = new_name + '.py'
-        parent_path = resource.get_parent().get_path()
+        parent_path = resource.get_parent().path
         if parent_path == '':
             new_location = new_name
         else:

rope/ui/codeassist.py

         self.focus_set = focus_set
 
     def entry_to_string(self, entry):
-        return entry[0].get_path() + ' : ' + str(entry[1])
+        return entry[0].path + ' : ' + str(entry[1])
 
     def canceled(self):
         self.toplevel.destroy()
         toplevel = Toplevel()
         toplevel.title('Closing Unsaved Editor')
         label = Label(toplevel, text='Closing Unsaved Editor for <%s>' %
-                      active_editor.get_file().get_path())
+                      active_editor.get_file().path)
         def save():
             active_editor.save()
             self.close_active_editor()
         int_vars = []
         for i, editor in enumerate(modified_editors):
             int_var = IntVar()
-            button = Checkbutton(toplevel, text=editor.get_file().get_path(),
+            button = Checkbutton(toplevel, text=editor.get_file().path,
                                  variable=int_var, onvalue=1, offvalue=0)
             int_vars.append(int_var)
             button.grid(row=i+1, columnspan=2)

rope/ui/editorpile.py

         self.editors.append(editor)
         title = Radiobutton(self.editor_list, text=file_.get_name(),
                             variable=self.active_file_path,
-                            value=file_.get_path(), indicatoron=0, bd=2,
+                            value=file_.path, indicatoron=0, bd=2,
                             command=lambda: self.activate_editor(editor),
                             selectcolor='#99A', relief=GROOVE)
         self.buttons[editor] = title

rope/ui/fileactions.py

     parent_entry = Tkinter.Entry(create_dialog)
     def do_select(folder):
         parent_entry.delete(0, Tkinter.END)
-        parent_entry.insert(0, folder.get_path())
+        parent_entry.insert(0, folder.path)
     def show_directory_view():
         toplevel = Tkinter.Toplevel()
         toplevel.title('Select ' + parent_name)
                 return 1
             if not self._is_init_dot_py(file1) and self._is_init_dot_py(file2):
                 return -1
-            return cmp(file1.get_path(), file2.get_path())
+            return cmp(file1.path, file2.path)
         if file1.get_name() != file2.get_name():
             return cmp(file1.get_name(), file2.get_name())
-        return cmp(file1.get_path(), file2.get_path())
+        return cmp(file1.path, file2.path)
 
 
 def _find_file_dialog(core):
             result = file_finder.find_files_starting_with(name.get())
         found.delete(0, Tkinter.END)
         for file_ in result:
-            found.insert(Tkinter.END, file_.get_path())
+            found.insert(Tkinter.END, file_.path)
         if result:
             found.selection_set(0)
     def open_selected():

rope/ui/refactor.py

 import Tkinter
 
+import rope.refactor.change_signature
+import rope.refactor.encapsulate_field
+import rope.refactor.extract
 import rope.refactor.importutils
-import rope.refactor.change_signature
+import rope.refactor.inline
 import rope.refactor.introduce_parameter
+import rope.refactor.localtofield
+import rope.refactor.move
+import rope.refactor.rename
 import rope.ui.core
+from rope.refactor import ImportOrganizer
+from rope.ui.extension import SimpleAction
 from rope.ui.menubar import MenuAddress
-from rope.ui.extension import SimpleAction
-from rope.ui.uihelpers import TreeViewHandle, TreeView, DescriptionList
-from rope.ui.uihelpers import EnhancedListHandle, VolatileList
+from rope.ui.uihelpers import (TreeViewHandle, TreeView,
+                               DescriptionList, EnhancedListHandle,
+                               VolatileList)
 
 
 class ConfirmAllEditorsAreSaved(object):
 
 class PreviewAndCommitChanges(object):
 
-    def __init__(self, refactoring, changes):
-        self.refactoring = refactoring
+    def __init__(self, project, changes):
+        self.project = project
         self.changes = changes
 
     def preview(self):
         frame.grid()
 
     def commit(self):
-        self.refactoring.add_and_commit_changes(self.changes)
+        self.project.do(self.changes)
 
 
 class RefactoringDialog(object):
 
-    def __init__(self, refactoring, title):
-        self.refactoring = refactoring
+    def __init__(self, project, title):
+        self.project = project
         self.title = title
 
     def show(self):
         frame.grid(row=0, columnspan=3)
 
     def _ok(self, event=None):
-        PreviewAndCommitChanges(self.refactoring, self._get_changes()).commit()
+        PreviewAndCommitChanges(self.project, self._get_changes()).commit()
         self.toplevel.destroy()
 
     def _preview(self, event=None):
-        PreviewAndCommitChanges(self.refactoring, self._get_changes()).preview()
+        PreviewAndCommitChanges(self.project, self._get_changes()).preview()
         self.toplevel.destroy()
 
     def _cancel(self, event=None):
     def __init__(self, context, title, is_local=False, current_module=False):
         resource = context.get_active_editor().get_file()
         editor = context.get_active_editor().get_editor()
-        super(RenameDialog, self).__init__(_get_refactoring(context), title)
+        super(RenameDialog, self).__init__(context.project, title)
         self.is_local = is_local
         offset = editor.get_current_offset()
         if current_module:
             offset = None
         self.renamer = rope.refactor.rename.RenameRefactoring(
-            context.get_core().get_open_project().get_pycore(),
-            resource, offset)
+            context.project, resource, offset)
 
     def _get_changes(self):
         new_name = self.new_name_entry.get()
         fileeditor = context.get_active_editor()
         resource = fileeditor.get_file()
         editor = fileeditor.get_editor()
-        _get_refactoring(context).transform_module_to_package(resource)
+        changes = rope.refactor.TransformModuleToPackage(
+            context.project, resource).get_changes()
+        self.project.do(changes)
+
 
 class ExtractDialog(RefactoringDialog):
 
     def __init__(self, context, do_extract, kind):
         editor = context.get_active_editor().get_editor()
-        super(ExtractDialog, self).__init__(_get_refactoring(context),
+        super(ExtractDialog, self).__init__(context.project,
                                             'Extract ' + kind)
         self.do_extract = do_extract
         self.kind = kind
         resource = context.get_active_editor().get_file()
         start_offset, end_offset = editor.get_region_offset()
         return rope.refactor.extract.ExtractMethodRefactoring(
-            context.get_core().get_open_project().get_pycore(),
-            resource, start_offset, end_offset).get_changes(new_name)
+            context.project, resource, start_offset,
+            end_offset).get_changes(new_name)
     ExtractDialog(context, do_extract, 'Method').show()
 
 def extract_variable(context):
         resource = context.get_active_editor().get_file()
         start_offset, end_offset = editor.get_region_offset()
         return rope.refactor.extract.ExtractVariableRefactoring(
-            context.get_core().get_open_project().get_pycore(),
-            resource, start_offset, end_offset).get_changes(new_name)
+            context.project, resource, start_offset,
+            end_offset).get_changes(new_name)
     ExtractDialog(context, do_extract, 'Variable').show()
 
 
         resource = context.get_active_editor().get_file()
         editor = context.get_active_editor().get_editor()
         super(IntroduceFactoryDialog, self).__init__(
-            _get_refactoring(context), 'Introduce Factory Method Refactoring')
+            context.project, 'Introduce Factory Method Refactoring')
         self.introducer = rope.refactor.introduce_factory.IntroduceFactoryRefactoring(
-            context.get_core().get_open_project().get_pycore(),
-            resource, editor.get_current_offset())
+            context.project, resource, editor.get_current_offset())
 
     def _get_changes(self):
         return self.introducer.get_changes(
 def undo_refactoring(context):
     if context.project:
         def undo():
-            context.project.get_pycore().get_refactoring().undo()
+            context.project.history.undo()
         _confirm_action('Undoing Refactoring',
                         'Undo refactoring might change many files. Proceed?',
                         undo)
 def redo_refactoring(context):
     if context.project:
         def redo():
-            context.project.get_pycore().get_refactoring().redo()
+            context.project.history.redo()
         _confirm_action('Redoing Refactoring',
                         'Redo refactoring might change many files. Proceed?',
                         redo)
         resource = context.get_active_editor().get_file()
         editor = context.get_active_editor().get_editor()
         self.project = context.get_core().get_open_project()
-        super(MoveDialog, self).__init__(_get_refactoring(context),
+        super(MoveDialog, self).__init__(context.project,
                                          'Move Refactoring')
         offset = editor.get_current_offset()
         if current_module:
             offset = None
         self.mover = rope.refactor.move.MoveRefactoring(
-            context.get_core().get_open_project().get_pycore(),
-            resource, offset)
+            context.project, resource, offset)
 
     def _get_changes(self):
         destination = self.project.get_pycore().find_module(self.new_name_entry.get())
         editor = context.get_active_editor().get_editor()
         self.project = context.get_core().get_open_project()
         super(ChangeMethodSignatureDialog, self).__init__(
-            _get_refactoring(context), 'Change Method Signature Refactoring')
+            context.project, 'Change Method Signature Refactoring')
         self.signature = rope.refactor.change_signature.ChangeSignature(
-            context.get_core().get_open_project().get_pycore(),
-            resource, editor.get_current_offset())
+            context.project, resource, editor.get_current_offset())
         self.definition_info = self.signature.get_definition_info()
         self.to_be_removed = []
 
         editor = context.get_active_editor().get_editor()
         self.project = context.get_core().get_open_project()
         super(InlineArgumentDefaultDialog, self).__init__(
-            _get_refactoring(context), 'Inline Argument Default')
+            context.project, 'Inline Argument Default')
         self.signature = rope.refactor.change_signature.ChangeSignature(
-            context.get_core().get_open_project().get_pycore(),
-            resource, editor.get_current_offset())
+            context.project, resource, editor.get_current_offset())
         self.definition_info = self.signature.get_definition_info()
 
     def _get_changes(self):
 
     def __init__(self, context, title):
         editor = context.get_active_editor().get_editor()
-        super(IntroduceParameterDialog, self).__init__(_get_refactoring(context), title)
+        super(IntroduceParameterDialog, self).__init__(context.project, title)
         self.renamer = rope.refactor.introduce_parameter.IntroduceParameter(
-            context.project.get_pycore(), context.resource, editor.get_current_offset())
+            context.project, context.resource, editor.get_current_offset())
 
     def _get_changes(self):
         new_name = self.new_name_entry.get()
 def introduce_parameter(context):
     IntroduceParameterDialog(context, 'Introduce Parameter').show()
 
-def organize_imports(context):
+
+def _import_action(context, method):
     if not context.get_active_editor():
         return
     file_editor = context.get_active_editor()
-    import_organizer = _get_refactoring(context).get_import_organizer()
+    import_organizer = ImportOrganizer(context.project)
     if import_organizer:
-        import_organizer.organize_imports(file_editor.get_file())
+        changes = method(import_organizer, file_editor.get_file())
+        if changes is not None:
+            context.project.do(changes)
+
+
+def organize_imports(context):
+    _import_action(context, ImportOrganizer.organize_imports)
+
 
 def expand_star_imports(context):
-    if not context.get_active_editor():
-        return
-    file_editor = context.get_active_editor()
-    import_organizer = _get_refactoring(context).get_import_organizer()
-    if import_organizer:
-        import_organizer.expand_star_imports(file_editor.get_file())
+    _import_action(context, ImportOrganizer.expand_star_imports)
+
 
 def transform_froms_to_imports(context):
-    if not context.get_active_editor():
-        return
-    file_editor = context.get_active_editor()
-    import_organizer = _get_refactoring(context).get_import_organizer()
-    if import_organizer:
-        import_organizer.transform_froms_to_imports(file_editor.get_file())
+    _import_action(context, ImportOrganizer.transform_froms_to_imports)
+
 
 def transform_relatives_to_absolute(context):
-    if not context.get_active_editor():
-        return
-    file_editor = context.get_active_editor()
-    import_organizer = _get_refactoring(context).get_import_organizer()
-    if import_organizer:
-        import_organizer.transform_relatives_to_absolute(file_editor.get_file())
+    _import_action(context, ImportOrganizer.transform_relatives_to_absolute)
+
 
 def handle_long_imports(context):
-    if not context.get_active_editor():
-        return
-    file_editor = context.get_active_editor()
-    import_organizer = _get_refactoring(context).get_import_organizer()
-    if import_organizer:
-        import_organizer.handle_long_imports(file_editor.get_file())
+    _import_action(context, ImportOrganizer.handle_long_imports)
+
 
 def inline(context):
     if context.get_active_editor():
         fileeditor = context.get_active_editor()
         resource = fileeditor.get_file()
         editor = fileeditor.get_editor()
-        _get_refactoring(context).inline(resource, editor.get_current_offset())
+        changes = rope.refactor.inline.InlineRefactoring(
+            context.project, resource,
+            editor.get_current_offset()).get_changes()
+        context.project.do(changes)
+
 
 def encapsulate_field(context):
     if context.get_active_editor():
         fileeditor = context.get_active_editor()
         resource = fileeditor.get_file()
         editor = fileeditor.get_editor()
-        _get_refactoring(context).encapsulate_field(
-            resource, editor.get_current_offset())
+        changes = rope.refactor.encapsulate_field.EncapsulateFieldRefactoring(
+            context.project, resource, editor.get_current_offset()).get_changes()
+        context.project.do(context)
+
 
 def convert_local_to_field(context):
     if context.get_active_editor():
         fileeditor = context.get_active_editor()
         resource = fileeditor.get_file()
         editor = fileeditor.get_editor()
-        _get_refactoring(context).convert_local_variable_to_field(
-            resource, editor.get_current_offset())
-
-def _get_refactoring(context):
-    return context.project.get_pycore().get_refactoring()
+        changes = rope.refactor.localtofield.ConvertLocalToFieldRefactoring(
+            context.project, resource,
+            editor.get_current_offset()).get_changes()
+        context.project.do(context)
 
 
 actions = []

rope/ui/testview.py

         self.process = None
         self.is_stopped = False
         self.toplevel = Tkinter.Toplevel()
-        self.toplevel.title('Running Unit Tests in <%s>' % resource.get_path())
+        self.toplevel.title('Running Unit Tests in <%s>' % resource.path)
         label = Tkinter.Label(self.toplevel,
-                              text='Running Unit Tests in <%s>' % resource.get_path())
+                              text='Running Unit Tests in <%s>' % resource.path)
         label.grid(row=0)
         self.test_name = Tkinter.Label(self.toplevel, width=80)
         self.test_name.grid(row=1)
             run_test_py = self.project.get_out_of_project_resource(
                 inspect.getsourcefile(rope.ui.runtest))
             self.process = self.project.get_pycore().run_module(
-                run_test_py, args=[str(rpc_port), self.resource._get_real_path()])
+                run_test_py, args=[str(rpc_port), self.resource.real_path])
             while not self.result._is_finished() and not self.is_stopped:
                 server.handle_request()
         finally:

ropetest/__init__.py

 import ropetest.objectinfertest
 import ropetest.runmodtest
 import ropetest.builtintest
+import ropetest.historytest
 
 
 def suite():
     result.addTests(ropetest.objectinfertest.suite())
     result.addTests(ropetest.runmodtest.suite())
     result.addTests(unittest.makeSuite(ropetest.builtintest.BuiltinTypesTest))
+    result.addTests(unittest.makeSuite(ropetest.historytest.HistoryTest))
     return result
 
 

ropetest/builtintest.py

 import unittest
+
 import rope.base.project
 import ropetest
+from rope.base import pyobjects
 
 
 class BuiltinTypesTest(unittest.TestCase):
         p_var = pymod.get_attribute('p').get_object()
         self.assertTrue('fget' in p_var.get_attributes())
 
+    def test_lambda_functions(self):
+        self.mod.write('l = lambda: 1\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        l_var = pymod.get_attribute('l').get_object()
+        self.assertEquals(pyobjects.PyObject.get_base_type('Function'),
+                          l_var.get_type())
+
 
 if __name__ == '__main__':
     unittest.main()

ropetest/historytest.py

+import unittest
+
+from rope.base import project
+from ropetest import testutils
+from rope.refactor.change import *
+
+
+class HistoryTest(unittest.TestCase):
+
+    def setUp(self):
+        super(HistoryTest, self).setUp()
+        self.project_root = 'sample_project'
+        testutils.remove_recursively(self.project_root)
+        self.project = project.Project(self.project_root)
+        self.history = self.project.history
+
+    def tearDown(self):
+        testutils.remove_recursively(self.project_root)
+        super(HistoryTest, self).tearDown()
+
+    def test_undoing_writes(self):
+        my_file = self.project.root.create_file('my_file.txt')
+        my_file.write('text1')
+        self.history.undo()
+        self.assertEquals('', my_file.read())
+
+    def test_moving_files(self):
+        my_file = self.project.root.create_file('my_file.txt')
+        my_file.move('new_file.txt')
+        self.history.undo()
+        self.assertEquals('', my_file.read())
+
+    def test_moving_files_to_folders(self):
+        my_file = self.project.root.create_file(&