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