| Index: tools/telemetry/third_party/rope/rope/base/change.py
|
| diff --git a/tools/telemetry/third_party/rope/rope/base/change.py b/tools/telemetry/third_party/rope/rope/base/change.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fe2ebf435adb36e28509d56f82ca5a707f78df1a
|
| --- /dev/null
|
| +++ b/tools/telemetry/third_party/rope/rope/base/change.py
|
| @@ -0,0 +1,450 @@
|
| +import datetime
|
| +import difflib
|
| +import os
|
| +import time
|
| +
|
| +import rope.base.fscommands
|
| +from rope.base import taskhandle, exceptions, utils
|
| +
|
| +
|
| +class Change(object):
|
| + """The base class for changes
|
| +
|
| + Rope refactorings return `Change` objects. They can be previewed,
|
| + committed or undone.
|
| + """
|
| +
|
| + def do(self, job_set=None):
|
| + """Perform the change
|
| +
|
| + .. note:: Do use this directly. Use `Project.do()` instead.
|
| + """
|
| +
|
| + def undo(self, job_set=None):
|
| + """Perform the change
|
| +
|
| + .. note:: Do use this directly. Use `History.undo()` instead.
|
| + """
|
| +
|
| + def get_description(self):
|
| + """Return the description of this change
|
| +
|
| + This can be used for previewing the changes.
|
| + """
|
| + return str(self)
|
| +
|
| + def get_changed_resources(self):
|
| + """Return the list of resources that will be changed"""
|
| + return []
|
| +
|
| + @property
|
| + @utils.saveit
|
| + def _operations(self):
|
| + return _ResourceOperations(self.resource.project)
|
| +
|
| +
|
| +class ChangeSet(Change):
|
| + """A collection of `Change` objects
|
| +
|
| + This class holds a collection of changes. This class provides
|
| + these fields:
|
| +
|
| + * `changes`: the list of changes
|
| + * `description`: the goal of these changes
|
| + """
|
| +
|
| + def __init__(self, description, timestamp=None):
|
| + self.changes = []
|
| + self.description = description
|
| + self.time = timestamp
|
| +
|
| + def do(self, job_set=taskhandle.NullJobSet()):
|
| + try:
|
| + done = []
|
| + for change in self.changes:
|
| + change.do(job_set)
|
| + done.append(change)
|
| + self.time = time.time()
|
| + except Exception:
|
| + for change in done:
|
| + change.undo()
|
| + raise
|
| +
|
| + def undo(self, job_set=taskhandle.NullJobSet()):
|
| + try:
|
| + done = []
|
| + for change in reversed(self.changes):
|
| + change.undo(job_set)
|
| + done.append(change)
|
| + except Exception:
|
| + for change in done:
|
| + change.do()
|
| + raise
|
| +
|
| + def add_change(self, change):
|
| + self.changes.append(change)
|
| +
|
| + def get_description(self):
|
| + result = [str(self) + ':\n\n\n']
|
| + for change in self.changes:
|
| + result.append(change.get_description())
|
| + result.append('\n')
|
| + return ''.join(result)
|
| +
|
| + def __str__(self):
|
| + if self.time is not None:
|
| + date = datetime.datetime.fromtimestamp(self.time)
|
| + if date.date() == datetime.date.today():
|
| + string_date = 'today'
|
| + elif date.date() == (datetime.date.today() -
|
| + datetime.timedelta(1)):
|
| + string_date = 'yesterday'
|
| + elif date.year == datetime.date.today().year:
|
| + string_date = date.strftime('%b %d')
|
| + else:
|
| + string_date = date.strftime('%d %b, %Y')
|
| + string_time = date.strftime('%H:%M:%S')
|
| + string_time = '%s %s ' % (string_date, string_time)
|
| + return self.description + ' - ' + string_time
|
| + return self.description
|
| +
|
| + def get_changed_resources(self):
|
| + result = set()
|
| + for change in self.changes:
|
| + result.update(change.get_changed_resources())
|
| + return result
|
| +
|
| +
|
| +def _handle_job_set(function):
|
| + """A decorator for handling `taskhandle.JobSet`\s
|
| +
|
| + A decorator for handling `taskhandle.JobSet`\s for `do` and `undo`
|
| + methods of `Change`\s.
|
| + """
|
| + def call(self, job_set=taskhandle.NullJobSet()):
|
| + job_set.started_job(str(self))
|
| + function(self)
|
| + job_set.finished_job()
|
| + return call
|
| +
|
| +
|
| +class ChangeContents(Change):
|
| + """A class to change the contents of a file
|
| +
|
| + Fields:
|
| +
|
| + * `resource`: The `rope.base.resources.File` to change
|
| + * `new_contents`: What to write in the file
|
| + """
|
| +
|
| + def __init__(self, resource, new_contents, old_contents=None):
|
| + self.resource = resource
|
| + # IDEA: Only saving diffs; possible problems when undo/redoing
|
| + self.new_contents = new_contents
|
| + self.old_contents = old_contents
|
| +
|
| + @_handle_job_set
|
| + def do(self):
|
| + if self.old_contents is None:
|
| + self.old_contents = self.resource.read()
|
| + self._operations.write_file(self.resource, self.new_contents)
|
| +
|
| + @_handle_job_set
|
| + def undo(self):
|
| + if self.old_contents is None:
|
| + raise exceptions.HistoryError(
|
| + 'Undoing a change that is not performed yet!')
|
| + self._operations.write_file(self.resource, self.old_contents)
|
| +
|
| + def __str__(self):
|
| + return 'Change <%s>' % self.resource.path
|
| +
|
| + def get_description(self):
|
| + new = self.new_contents
|
| + old = self.old_contents
|
| + if old is None:
|
| + if self.resource.exists():
|
| + old = self.resource.read()
|
| + else:
|
| + old = ''
|
| + result = difflib.unified_diff(
|
| + old.splitlines(True), new.splitlines(True),
|
| + 'a/' + self.resource.path, 'b/' + self.resource.path)
|
| + return ''.join(list(result))
|
| +
|
| + def get_changed_resources(self):
|
| + return [self.resource]
|
| +
|
| +
|
| +class MoveResource(Change):
|
| + """Move a resource to a new location
|
| +
|
| + Fields:
|
| +
|
| + * `resource`: The `rope.base.resources.Resource` to move
|
| + * `new_resource`: The destination for move; It is the moved
|
| + resource not the folder containing that resource.
|
| + """
|
| +
|
| + def __init__(self, resource, new_location, exact=False):
|
| + self.project = resource.project
|
| + self.resource = resource
|
| + if not exact:
|
| + new_location = _get_destination_for_move(resource, new_location)
|
| + if resource.is_folder():
|
| + self.new_resource = self.project.get_folder(new_location)
|
| + else:
|
| + self.new_resource = self.project.get_file(new_location)
|
| +
|
| + @_handle_job_set
|
| + def do(self):
|
| + self._operations.move(self.resource, self.new_resource)
|
| +
|
| + @_handle_job_set
|
| + def undo(self):
|
| + self._operations.move(self.new_resource, self.resource)
|
| +
|
| + def __str__(self):
|
| + return 'Move <%s>' % self.resource.path
|
| +
|
| + def get_description(self):
|
| + return 'rename from %s\nrename to %s' % (self.resource.path,
|
| + self.new_resource.path)
|
| +
|
| + def get_changed_resources(self):
|
| + return [self.resource, self.new_resource]
|
| +
|
| +
|
| +class CreateResource(Change):
|
| + """A class to create a resource
|
| +
|
| + Fields:
|
| +
|
| + * `resource`: The resource to create
|
| + """
|
| +
|
| + def __init__(self, resource):
|
| + self.resource = resource
|
| +
|
| + @_handle_job_set
|
| + def do(self):
|
| + self._operations.create(self.resource)
|
| +
|
| + @_handle_job_set
|
| + def undo(self):
|
| + self._operations.remove(self.resource)
|
| +
|
| + def __str__(self):
|
| + return 'Create Resource <%s>' % (self.resource.path)
|
| +
|
| + def get_description(self):
|
| + return 'new file %s' % (self.resource.path)
|
| +
|
| + def get_changed_resources(self):
|
| + return [self.resource]
|
| +
|
| + def _get_child_path(self, parent, name):
|
| + if parent.path == '':
|
| + return name
|
| + else:
|
| + return parent.path + '/' + name
|
| +
|
| +
|
| +class CreateFolder(CreateResource):
|
| + """A class to create a folder
|
| +
|
| + See docs for `CreateResource`.
|
| + """
|
| +
|
| + def __init__(self, parent, name):
|
| + resource = parent.project.get_folder(
|
| + self._get_child_path(parent, name))
|
| + super(CreateFolder, self).__init__(resource)
|
| +
|
| +
|
| +class CreateFile(CreateResource):
|
| + """A class to create a file
|
| +
|
| + See docs for `CreateResource`.
|
| + """
|
| +
|
| + def __init__(self, parent, name):
|
| + resource = parent.project.get_file(self._get_child_path(parent, name))
|
| + super(CreateFile, self).__init__(resource)
|
| +
|
| +
|
| +class RemoveResource(Change):
|
| + """A class to remove a resource
|
| +
|
| + Fields:
|
| +
|
| + * `resource`: The resource to be removed
|
| + """
|
| +
|
| + def __init__(self, resource):
|
| + self.resource = resource
|
| +
|
| + @_handle_job_set
|
| + def do(self):
|
| + self._operations.remove(self.resource)
|
| +
|
| + # TODO: Undoing remove operations
|
| + @_handle_job_set
|
| + def undo(self):
|
| + raise NotImplementedError(
|
| + 'Undoing `RemoveResource` is not implemented yet.')
|
| +
|
| + def __str__(self):
|
| + return 'Remove <%s>' % (self.resource.path)
|
| +
|
| + def get_changed_resources(self):
|
| + return [self.resource]
|
| +
|
| +
|
| +def count_changes(change):
|
| + """Counts the number of basic changes a `Change` will make"""
|
| + if isinstance(change, ChangeSet):
|
| + result = 0
|
| + for child in change.changes:
|
| + result += count_changes(child)
|
| + return result
|
| + return 1
|
| +
|
| +
|
| +def create_job_set(task_handle, change):
|
| + return task_handle.create_jobset(str(change), count_changes(change))
|
| +
|
| +
|
| +class _ResourceOperations(object):
|
| +
|
| + def __init__(self, project):
|
| + self.project = project
|
| + self.fscommands = project.fscommands
|
| + self.direct_commands = rope.base.fscommands.FileSystemCommands()
|
| +
|
| + def _get_fscommands(self, resource):
|
| + if self.project.is_ignored(resource):
|
| + return self.direct_commands
|
| + return self.fscommands
|
| +
|
| + def write_file(self, resource, contents):
|
| + data = rope.base.fscommands.unicode_to_file_data(contents)
|
| + fscommands = self._get_fscommands(resource)
|
| + fscommands.write(resource.real_path, data)
|
| + for observer in list(self.project.observers):
|
| + observer.resource_changed(resource)
|
| +
|
| + def move(self, resource, new_resource):
|
| + fscommands = self._get_fscommands(resource)
|
| + fscommands.move(resource.real_path, new_resource.real_path)
|
| + for observer in list(self.project.observers):
|
| + observer.resource_moved(resource, new_resource)
|
| +
|
| + def create(self, resource):
|
| + if resource.is_folder():
|
| + self._create_resource(resource.path, kind='folder')
|
| + else:
|
| + self._create_resource(resource.path)
|
| + for observer in list(self.project.observers):
|
| + observer.resource_created(resource)
|
| +
|
| + def remove(self, resource):
|
| + fscommands = self._get_fscommands(resource)
|
| + fscommands.remove(resource.real_path)
|
| + for observer in list(self.project.observers):
|
| + observer.resource_removed(resource)
|
| +
|
| + def _create_resource(self, file_name, kind='file'):
|
| + resource_path = self.project._get_resource_path(file_name)
|
| + if os.path.exists(resource_path):
|
| + raise exceptions.RopeError('Resource <%s> already exists'
|
| + % resource_path)
|
| + resource = self.project.get_file(file_name)
|
| + if not resource.parent.exists():
|
| + raise exceptions.ResourceNotFoundError(
|
| + 'Parent folder of <%s> does not exist' % resource.path)
|
| + fscommands = self._get_fscommands(resource)
|
| + try:
|
| + if kind == 'file':
|
| + fscommands.create_file(resource_path)
|
| + else:
|
| + fscommands.create_folder(resource_path)
|
| + except IOError as e:
|
| + raise exceptions.RopeError(e)
|
| +
|
| +
|
| +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.name
|
| + else:
|
| + return resource.name
|
| + return destination
|
| +
|
| +
|
| +class ChangeToData(object):
|
| +
|
| + def convertChangeSet(self, change):
|
| + description = change.description
|
| + changes = []
|
| + for child in change.changes:
|
| + changes.append(self(child))
|
| + return (description, changes, change.time)
|
| +
|
| + def convertChangeContents(self, change):
|
| + return (change.resource.path, change.new_contents, change.old_contents)
|
| +
|
| + def convertMoveResource(self, change):
|
| + return (change.resource.path, change.new_resource.path)
|
| +
|
| + def convertCreateResource(self, change):
|
| + return (change.resource.path, change.resource.is_folder())
|
| +
|
| + def convertRemoveResource(self, change):
|
| + return (change.resource.path, change.resource.is_folder())
|
| +
|
| + def __call__(self, change):
|
| + change_type = type(change)
|
| + if change_type in (CreateFolder, CreateFile):
|
| + change_type = CreateResource
|
| + method = getattr(self, 'convert' + change_type.__name__)
|
| + return (change_type.__name__, method(change))
|
| +
|
| +
|
| +class DataToChange(object):
|
| +
|
| + def __init__(self, project):
|
| + self.project = project
|
| +
|
| + def makeChangeSet(self, description, changes, time=None):
|
| + result = ChangeSet(description, time)
|
| + for child in changes:
|
| + result.add_change(self(child))
|
| + return result
|
| +
|
| + def makeChangeContents(self, path, new_contents, old_contents):
|
| + resource = self.project.get_file(path)
|
| + return ChangeContents(resource, new_contents, old_contents)
|
| +
|
| + def makeMoveResource(self, old_path, new_path):
|
| + resource = self.project.get_file(old_path)
|
| + return MoveResource(resource, new_path, exact=True)
|
| +
|
| + def makeCreateResource(self, path, is_folder):
|
| + if is_folder:
|
| + resource = self.project.get_folder(path)
|
| + else:
|
| + resource = self.project.get_file(path)
|
| + return CreateResource(resource)
|
| +
|
| + def makeRemoveResource(self, path, is_folder):
|
| + if is_folder:
|
| + resource = self.project.get_folder(path)
|
| + else:
|
| + resource = self.project.get_file(path)
|
| + return RemoveResource(resource)
|
| +
|
| + def __call__(self, data):
|
| + method = getattr(self, 'make' + data[0])
|
| + return method(*data[1])
|
|
|