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]) |