Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/base/history.py
1440 views
1
from rope.base import exceptions, change, taskhandle
2
3
4
class History(object):
5
"""A class that holds project history"""
6
7
def __init__(self, project, maxundos=None):
8
self.project = project
9
self._undo_list = []
10
self._redo_list = []
11
self._maxundos = maxundos
12
self._load_history()
13
self.project.data_files.add_write_hook(self.write)
14
self.current_change = None
15
16
def _load_history(self):
17
if self.save:
18
result = self.project.data_files.read_data(
19
'history', compress=self.compress, import_=True)
20
if result is not None:
21
to_change = change.DataToChange(self.project)
22
for data in result[0]:
23
self._undo_list.append(to_change(data))
24
for data in result[1]:
25
self._redo_list.append(to_change(data))
26
27
def do(self, changes, task_handle=taskhandle.NullTaskHandle()):
28
"""Perform the change and add it to the `self.undo_list`
29
30
Note that uninteresting changes (changes to ignored files)
31
will not be appended to `self.undo_list`.
32
33
"""
34
try:
35
self.current_change = changes
36
changes.do(change.create_job_set(task_handle, changes))
37
finally:
38
self.current_change = None
39
if self._is_change_interesting(changes):
40
self.undo_list.append(changes)
41
self._remove_extra_items()
42
del self.redo_list[:]
43
44
def _remove_extra_items(self):
45
if len(self.undo_list) > self.max_undos:
46
del self.undo_list[0:len(self.undo_list) - self.max_undos]
47
48
def _is_change_interesting(self, changes):
49
for resource in changes.get_changed_resources():
50
if not self.project.is_ignored(resource):
51
return True
52
return False
53
54
def undo(self, change=None, drop=False,
55
task_handle=taskhandle.NullTaskHandle()):
56
"""Redo done changes from the history
57
58
When `change` is `None`, the last done change will be undone.
59
If change is not `None` it should be an item from
60
`self.undo_list`; this change and all changes that depend on
61
it will be undone. In both cases the list of undone changes
62
will be returned.
63
64
If `drop` is `True`, the undone change will not be appended to
65
the redo list.
66
67
"""
68
if not self._undo_list:
69
raise exceptions.HistoryError('Undo list is empty')
70
if change is None:
71
change = self.undo_list[-1]
72
dependencies = self._find_dependencies(self.undo_list, change)
73
self._move_front(self.undo_list, dependencies)
74
self._perform_undos(len(dependencies), task_handle)
75
result = self.redo_list[-len(dependencies):]
76
if drop:
77
del self.redo_list[-len(dependencies):]
78
return result
79
80
def redo(self, change=None, task_handle=taskhandle.NullTaskHandle()):
81
"""Redo undone changes from the history
82
83
When `change` is `None`, the last undone change will be
84
redone. If change is not `None` it should be an item from
85
`self.redo_list`; this change and all changes that depend on
86
it will be redone. In both cases the list of redone changes
87
will be returned.
88
89
"""
90
if not self.redo_list:
91
raise exceptions.HistoryError('Redo list is empty')
92
if change is None:
93
change = self.redo_list[-1]
94
dependencies = self._find_dependencies(self.redo_list, change)
95
self._move_front(self.redo_list, dependencies)
96
self._perform_redos(len(dependencies), task_handle)
97
return self.undo_list[-len(dependencies):]
98
99
def _move_front(self, change_list, changes):
100
for change in changes:
101
change_list.remove(change)
102
change_list.append(change)
103
104
def _find_dependencies(self, change_list, change):
105
index = change_list.index(change)
106
return _FindChangeDependencies(change_list[index:])()
107
108
def _perform_undos(self, count, task_handle):
109
for i in range(count):
110
self.current_change = self.undo_list[-1]
111
try:
112
job_set = change.create_job_set(task_handle,
113
self.current_change)
114
self.current_change.undo(job_set)
115
finally:
116
self.current_change = None
117
self.redo_list.append(self.undo_list.pop())
118
119
def _perform_redos(self, count, task_handle):
120
for i in range(count):
121
self.current_change = self.redo_list[-1]
122
try:
123
job_set = change.create_job_set(task_handle,
124
self.current_change)
125
self.current_change.do(job_set)
126
finally:
127
self.current_change = None
128
self.undo_list.append(self.redo_list.pop())
129
130
def contents_before_current_change(self, file):
131
if self.current_change is None:
132
return None
133
result = self._search_for_change_contents([self.current_change], file)
134
if result is not None:
135
return result
136
if file.exists() and not file.is_folder():
137
return file.read()
138
else:
139
return None
140
141
def _search_for_change_contents(self, change_list, file):
142
for change_ in reversed(change_list):
143
if isinstance(change_, change.ChangeSet):
144
result = self._search_for_change_contents(change_.changes,
145
file)
146
if result is not None:
147
return result
148
if isinstance(change_, change.ChangeContents) and \
149
change_.resource == file:
150
return change_.old_contents
151
152
def write(self):
153
if self.save:
154
data = []
155
to_data = change.ChangeToData()
156
self._remove_extra_items()
157
data.append([to_data(change_) for change_ in self.undo_list])
158
data.append([to_data(change_) for change_ in self.redo_list])
159
self.project.data_files.write_data('history', data,
160
compress=self.compress)
161
162
def get_file_undo_list(self, resource):
163
result = []
164
for change in self.undo_list:
165
if resource in change.get_changed_resources():
166
result.append(change)
167
return result
168
169
def __str__(self):
170
return 'History holds %s changes in memory' % \
171
(len(self.undo_list) + len(self.redo_list))
172
173
undo_list = property(lambda self: self._undo_list)
174
redo_list = property(lambda self: self._redo_list)
175
176
@property
177
def tobe_undone(self):
178
"""The last done change if available, `None` otherwise"""
179
if self.undo_list:
180
return self.undo_list[-1]
181
182
@property
183
def tobe_redone(self):
184
"""The last undone change if available, `None` otherwise"""
185
if self.redo_list:
186
return self.redo_list[-1]
187
188
@property
189
def max_undos(self):
190
if self._maxundos is None:
191
return self.project.prefs.get('max_history_items', 100)
192
else:
193
return self._maxundos
194
195
@property
196
def save(self):
197
return self.project.prefs.get('save_history', False)
198
199
@property
200
def compress(self):
201
return self.project.prefs.get('compress_history', False)
202
203
def clear(self):
204
"""Forget all undo and redo information"""
205
del self.undo_list[:]
206
del self.redo_list[:]
207
208
209
class _FindChangeDependencies(object):
210
211
def __init__(self, change_list):
212
self.change = change_list[0]
213
self.change_list = change_list
214
self.changed_resources = set(self.change.get_changed_resources())
215
216
def __call__(self):
217
result = [self.change]
218
for change in self.change_list[1:]:
219
if self._depends_on(change, result):
220
result.append(change)
221
self.changed_resources.update(change.get_changed_resources())
222
return result
223
224
def _depends_on(self, changes, result):
225
for resource in changes.get_changed_resources():
226
if resource is None:
227
continue
228
if resource in self.changed_resources:
229
return True
230
for changed in self.changed_resources:
231
if resource.is_folder() and resource.contains(changed):
232
return True
233
if changed.is_folder() and changed.contains(resource):
234
return True
235
return False
236
237