Index: tools/telemetry/third_party/rope/rope/base/history.py |
diff --git a/tools/telemetry/third_party/rope/rope/base/history.py b/tools/telemetry/third_party/rope/rope/base/history.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d3c523d310c41b42740253818ce4f94f04e6b988 |
--- /dev/null |
+++ b/tools/telemetry/third_party/rope/rope/base/history.py |
@@ -0,0 +1,235 @@ |
+from rope.base import exceptions, change, taskhandle |
+ |
+ |
+class History(object): |
+ """A class that holds project history""" |
+ |
+ def __init__(self, project, maxundos=None): |
+ self.project = project |
+ self._undo_list = [] |
+ self._redo_list = [] |
+ self._maxundos = maxundos |
+ self._load_history() |
+ self.project.data_files.add_write_hook(self.write) |
+ self.current_change = None |
+ |
+ def _load_history(self): |
+ if self.save: |
+ result = self.project.data_files.read_data( |
+ 'history', compress=self.compress, import_=True) |
+ if result is not None: |
+ to_change = change.DataToChange(self.project) |
+ for data in result[0]: |
+ self._undo_list.append(to_change(data)) |
+ for data in result[1]: |
+ self._redo_list.append(to_change(data)) |
+ |
+ def do(self, changes, task_handle=taskhandle.NullTaskHandle()): |
+ """Perform the change and add it to the `self.undo_list` |
+ |
+ Note that uninteresting changes (changes to ignored files) |
+ will not be appended to `self.undo_list`. |
+ |
+ """ |
+ try: |
+ self.current_change = changes |
+ changes.do(change.create_job_set(task_handle, changes)) |
+ finally: |
+ self.current_change = None |
+ if self._is_change_interesting(changes): |
+ self.undo_list.append(changes) |
+ self._remove_extra_items() |
+ del self.redo_list[:] |
+ |
+ def _remove_extra_items(self): |
+ if len(self.undo_list) > self.max_undos: |
+ del self.undo_list[0:len(self.undo_list) - self.max_undos] |
+ |
+ def _is_change_interesting(self, changes): |
+ for resource in changes.get_changed_resources(): |
+ if not self.project.is_ignored(resource): |
+ return True |
+ return False |
+ |
+ def undo(self, change=None, drop=False, |
+ task_handle=taskhandle.NullTaskHandle()): |
+ """Redo done changes from the history |
+ |
+ When `change` is `None`, the last done change will be undone. |
+ If change is not `None` it should be an item from |
+ `self.undo_list`; this change and all changes that depend on |
+ it will be undone. In both cases the list of undone changes |
+ will be returned. |
+ |
+ If `drop` is `True`, the undone change will not be appended to |
+ the redo list. |
+ |
+ """ |
+ if not self._undo_list: |
+ raise exceptions.HistoryError('Undo list is empty') |
+ if change is None: |
+ change = self.undo_list[-1] |
+ dependencies = self._find_dependencies(self.undo_list, change) |
+ self._move_front(self.undo_list, dependencies) |
+ self._perform_undos(len(dependencies), task_handle) |
+ result = self.redo_list[-len(dependencies):] |
+ if drop: |
+ del self.redo_list[-len(dependencies):] |
+ return result |
+ |
+ def redo(self, change=None, task_handle=taskhandle.NullTaskHandle()): |
+ """Redo undone changes from the history |
+ |
+ When `change` is `None`, the last undone change will be |
+ redone. If change is not `None` it should be an item from |
+ `self.redo_list`; this change and all changes that depend on |
+ it will be redone. In both cases the list of redone changes |
+ will be returned. |
+ |
+ """ |
+ if not self.redo_list: |
+ raise exceptions.HistoryError('Redo list is empty') |
+ if change is None: |
+ change = self.redo_list[-1] |
+ dependencies = self._find_dependencies(self.redo_list, change) |
+ self._move_front(self.redo_list, dependencies) |
+ self._perform_redos(len(dependencies), task_handle) |
+ return self.undo_list[-len(dependencies):] |
+ |
+ def _move_front(self, change_list, changes): |
+ for change in changes: |
+ change_list.remove(change) |
+ change_list.append(change) |
+ |
+ def _find_dependencies(self, change_list, change): |
+ index = change_list.index(change) |
+ return _FindChangeDependencies(change_list[index:])() |
+ |
+ def _perform_undos(self, count, task_handle): |
+ for i in range(count): |
+ self.current_change = self.undo_list[-1] |
+ try: |
+ job_set = change.create_job_set(task_handle, |
+ self.current_change) |
+ self.current_change.undo(job_set) |
+ finally: |
+ self.current_change = None |
+ self.redo_list.append(self.undo_list.pop()) |
+ |
+ def _perform_redos(self, count, task_handle): |
+ for i in range(count): |
+ self.current_change = self.redo_list[-1] |
+ try: |
+ job_set = change.create_job_set(task_handle, |
+ self.current_change) |
+ self.current_change.do(job_set) |
+ finally: |
+ self.current_change = None |
+ self.undo_list.append(self.redo_list.pop()) |
+ |
+ def contents_before_current_change(self, file): |
+ if self.current_change is None: |
+ return None |
+ result = self._search_for_change_contents([self.current_change], file) |
+ if result is not None: |
+ return result |
+ if file.exists() and not file.is_folder(): |
+ return file.read() |
+ else: |
+ return None |
+ |
+ def _search_for_change_contents(self, change_list, file): |
+ for change_ in reversed(change_list): |
+ if isinstance(change_, change.ChangeSet): |
+ result = self._search_for_change_contents(change_.changes, |
+ file) |
+ if result is not None: |
+ return result |
+ if isinstance(change_, change.ChangeContents) and \ |
+ change_.resource == file: |
+ return change_.old_contents |
+ |
+ def write(self): |
+ if self.save: |
+ data = [] |
+ to_data = change.ChangeToData() |
+ self._remove_extra_items() |
+ data.append([to_data(change_) for change_ in self.undo_list]) |
+ data.append([to_data(change_) for change_ in self.redo_list]) |
+ self.project.data_files.write_data('history', data, |
+ compress=self.compress) |
+ |
+ def get_file_undo_list(self, resource): |
+ result = [] |
+ for change in self.undo_list: |
+ if resource in change.get_changed_resources(): |
+ result.append(change) |
+ return result |
+ |
+ def __str__(self): |
+ return 'History holds %s changes in memory' % \ |
+ (len(self.undo_list) + len(self.redo_list)) |
+ |
+ undo_list = property(lambda self: self._undo_list) |
+ redo_list = property(lambda self: self._redo_list) |
+ |
+ @property |
+ def tobe_undone(self): |
+ """The last done change if available, `None` otherwise""" |
+ if self.undo_list: |
+ return self.undo_list[-1] |
+ |
+ @property |
+ def tobe_redone(self): |
+ """The last undone change if available, `None` otherwise""" |
+ if self.redo_list: |
+ return self.redo_list[-1] |
+ |
+ @property |
+ def max_undos(self): |
+ if self._maxundos is None: |
+ return self.project.prefs.get('max_history_items', 100) |
+ else: |
+ return self._maxundos |
+ |
+ @property |
+ def save(self): |
+ return self.project.prefs.get('save_history', False) |
+ |
+ @property |
+ def compress(self): |
+ return self.project.prefs.get('compress_history', False) |
+ |
+ def clear(self): |
+ """Forget all undo and redo information""" |
+ del self.undo_list[:] |
+ del self.redo_list[:] |
+ |
+ |
+class _FindChangeDependencies(object): |
+ |
+ def __init__(self, change_list): |
+ self.change = change_list[0] |
+ self.change_list = change_list |
+ self.changed_resources = set(self.change.get_changed_resources()) |
+ |
+ def __call__(self): |
+ result = [self.change] |
+ for change in self.change_list[1:]: |
+ if self._depends_on(change, result): |
+ result.append(change) |
+ self.changed_resources.update(change.get_changed_resources()) |
+ return result |
+ |
+ def _depends_on(self, changes, result): |
+ for resource in changes.get_changed_resources(): |
+ if resource is None: |
+ continue |
+ if resource in self.changed_resources: |
+ return True |
+ for changed in self.changed_resources: |
+ if resource.is_folder() and resource.contains(changed): |
+ return True |
+ if changed.is_folder() and changed.contains(resource): |
+ return True |
+ return False |