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/change.py
1415 views
1
import datetime
2
import difflib
3
import os
4
import time
5
import warnings
6
7
import rope.base.fscommands
8
from rope.base import taskhandle, exceptions, utils
9
10
11
class Change(object):
12
"""The base class for changes
13
14
Rope refactorings return `Change` objects. They can be previewed,
15
committed or undone.
16
"""
17
18
def do(self, job_set=None):
19
"""Perform the change
20
21
.. note:: Do use this directly. Use `Project.do()` instead.
22
"""
23
24
def undo(self, job_set=None):
25
"""Perform the change
26
27
.. note:: Do use this directly. Use `History.undo()` instead.
28
"""
29
30
def get_description(self):
31
"""Return the description of this change
32
33
This can be used for previewing the changes.
34
"""
35
return str(self)
36
37
def get_changed_resources(self):
38
"""Return the list of resources that will be changed"""
39
return []
40
41
@property
42
@utils.saveit
43
def _operations(self):
44
return _ResourceOperations(self.resource.project)
45
46
47
class ChangeSet(Change):
48
"""A collection of `Change` objects
49
50
This class holds a collection of changes. This class provides
51
these fields:
52
53
* `changes`: the list of changes
54
* `description`: the goal of these changes
55
"""
56
57
def __init__(self, description, timestamp=None):
58
self.changes = []
59
self.description = description
60
self.time = timestamp
61
62
def do(self, job_set=taskhandle.NullJobSet()):
63
try:
64
done = []
65
for change in self.changes:
66
change.do(job_set)
67
done.append(change)
68
self.time = time.time()
69
except Exception:
70
for change in done:
71
change.undo()
72
raise
73
74
def undo(self, job_set=taskhandle.NullJobSet()):
75
try:
76
done = []
77
for change in reversed(self.changes):
78
change.undo(job_set)
79
done.append(change)
80
except Exception:
81
for change in done:
82
change.do()
83
raise
84
85
def add_change(self, change):
86
self.changes.append(change)
87
88
def get_description(self):
89
result = [str(self) + ':\n\n\n']
90
for change in self.changes:
91
result.append(change.get_description())
92
result.append('\n')
93
return ''.join(result)
94
95
def __str__(self):
96
if self.time is not None:
97
date = datetime.datetime.fromtimestamp(self.time)
98
if date.date() == datetime.date.today():
99
string_date = 'today'
100
elif date.date() == (datetime.date.today() - datetime.timedelta(1)):
101
string_date = 'yesterday'
102
elif date.year == datetime.date.today().year:
103
string_date = date.strftime('%b %d')
104
else:
105
string_date = date.strftime('%d %b, %Y')
106
string_time = date.strftime('%H:%M:%S')
107
string_time = '%s %s ' % (string_date, string_time)
108
return self.description + ' - ' + string_time
109
return self.description
110
111
def get_changed_resources(self):
112
result = set()
113
for change in self.changes:
114
result.update(change.get_changed_resources())
115
return result
116
117
118
def _handle_job_set(function):
119
"""A decorator for handling `taskhandle.JobSet`\s
120
121
A decorator for handling `taskhandle.JobSet`\s for `do` and `undo`
122
methods of `Change`\s.
123
"""
124
def call(self, job_set=taskhandle.NullJobSet()):
125
job_set.started_job(str(self))
126
function(self)
127
job_set.finished_job()
128
return call
129
130
131
class ChangeContents(Change):
132
"""A class to change the contents of a file
133
134
Fields:
135
136
* `resource`: The `rope.base.resources.File` to change
137
* `new_contents`: What to write in the file
138
"""
139
140
def __init__(self, resource, new_contents, old_contents=None):
141
self.resource = resource
142
# IDEA: Only saving diffs; possible problems when undo/redoing
143
self.new_contents = new_contents
144
self.old_contents = old_contents
145
146
@_handle_job_set
147
def do(self):
148
if self.old_contents is None:
149
self.old_contents = self.resource.read()
150
self._operations.write_file(self.resource, self.new_contents)
151
152
@_handle_job_set
153
def undo(self):
154
if self.old_contents is None:
155
raise exceptions.HistoryError(
156
'Undoing a change that is not performed yet!')
157
self._operations.write_file(self.resource, self.old_contents)
158
159
def __str__(self):
160
return 'Change <%s>' % self.resource.path
161
162
def get_description(self):
163
new = self.new_contents
164
old = self.old_contents
165
if old is None:
166
if self.resource.exists():
167
old = self.resource.read()
168
else:
169
old = ''
170
result = difflib.unified_diff(
171
old.splitlines(True), new.splitlines(True),
172
'a/' + self.resource.path, 'b/' + self.resource.path)
173
return ''.join(list(result))
174
175
def get_changed_resources(self):
176
return [self.resource]
177
178
179
class MoveResource(Change):
180
"""Move a resource to a new location
181
182
Fields:
183
184
* `resource`: The `rope.base.resources.Resource` to move
185
* `new_resource`: The destination for move; It is the moved
186
resource not the folder containing that resource.
187
"""
188
189
def __init__(self, resource, new_location, exact=False):
190
self.project = resource.project
191
self.resource = resource
192
if not exact:
193
new_location = _get_destination_for_move(resource, new_location)
194
if resource.is_folder():
195
self.new_resource = self.project.get_folder(new_location)
196
else:
197
self.new_resource = self.project.get_file(new_location)
198
199
@_handle_job_set
200
def do(self):
201
self._operations.move(self.resource, self.new_resource)
202
203
@_handle_job_set
204
def undo(self):
205
self._operations.move(self.new_resource, self.resource)
206
207
def __str__(self):
208
return 'Move <%s>' % self.resource.path
209
210
def get_description(self):
211
return 'rename from %s\nrename to %s' % (self.resource.path,
212
self.new_resource.path)
213
214
def get_changed_resources(self):
215
return [self.resource, self.new_resource]
216
217
218
class CreateResource(Change):
219
"""A class to create a resource
220
221
Fields:
222
223
* `resource`: The resource to create
224
"""
225
226
def __init__(self, resource):
227
self.resource = resource
228
229
@_handle_job_set
230
def do(self):
231
self._operations.create(self.resource)
232
233
@_handle_job_set
234
def undo(self):
235
self._operations.remove(self.resource)
236
237
def __str__(self):
238
return 'Create Resource <%s>' % (self.resource.path)
239
240
def get_description(self):
241
return 'new file %s' % (self.resource.path)
242
243
def get_changed_resources(self):
244
return [self.resource]
245
246
def _get_child_path(self, parent, name):
247
if parent.path == '':
248
return name
249
else:
250
return parent.path + '/' + name
251
252
253
class CreateFolder(CreateResource):
254
"""A class to create a folder
255
256
See docs for `CreateResource`.
257
"""
258
259
def __init__(self, parent, name):
260
resource = parent.project.get_folder(self._get_child_path(parent, name))
261
super(CreateFolder, self).__init__(resource)
262
263
264
class CreateFile(CreateResource):
265
"""A class to create a file
266
267
See docs for `CreateResource`.
268
"""
269
270
def __init__(self, parent, name):
271
resource = parent.project.get_file(self._get_child_path(parent, name))
272
super(CreateFile, self).__init__(resource)
273
274
275
class RemoveResource(Change):
276
"""A class to remove a resource
277
278
Fields:
279
280
* `resource`: The resource to be removed
281
"""
282
283
def __init__(self, resource):
284
self.resource = resource
285
286
@_handle_job_set
287
def do(self):
288
self._operations.remove(self.resource)
289
290
# TODO: Undoing remove operations
291
@_handle_job_set
292
def undo(self):
293
raise NotImplementedError(
294
'Undoing `RemoveResource` is not implemented yet.')
295
296
def __str__(self):
297
return 'Remove <%s>' % (self.resource.path)
298
299
def get_changed_resources(self):
300
return [self.resource]
301
302
303
def count_changes(change):
304
"""Counts the number of basic changes a `Change` will make"""
305
if isinstance(change, ChangeSet):
306
result = 0
307
for child in change.changes:
308
result += count_changes(child)
309
return result
310
return 1
311
312
def create_job_set(task_handle, change):
313
return task_handle.create_jobset(str(change), count_changes(change))
314
315
316
class _ResourceOperations(object):
317
318
def __init__(self, project):
319
self.project = project
320
self.fscommands = project.fscommands
321
self.direct_commands = rope.base.fscommands.FileSystemCommands()
322
323
def _get_fscommands(self, resource):
324
if self.project.is_ignored(resource):
325
return self.direct_commands
326
return self.fscommands
327
328
def write_file(self, resource, contents):
329
data = rope.base.fscommands.unicode_to_file_data(contents)
330
fscommands = self._get_fscommands(resource)
331
fscommands.write(resource.real_path, data)
332
for observer in list(self.project.observers):
333
observer.resource_changed(resource)
334
335
def move(self, resource, new_resource):
336
fscommands = self._get_fscommands(resource)
337
fscommands.move(resource.real_path, new_resource.real_path)
338
for observer in list(self.project.observers):
339
observer.resource_moved(resource, new_resource)
340
341
def create(self, resource):
342
if resource.is_folder():
343
self._create_resource(resource.path, kind='folder')
344
else:
345
self._create_resource(resource.path)
346
for observer in list(self.project.observers):
347
observer.resource_created(resource)
348
349
def remove(self, resource):
350
fscommands = self._get_fscommands(resource)
351
fscommands.remove(resource.real_path)
352
for observer in list(self.project.observers):
353
observer.resource_removed(resource)
354
355
def _create_resource(self, file_name, kind='file'):
356
resource_path = self.project._get_resource_path(file_name)
357
if os.path.exists(resource_path):
358
raise exceptions.RopeError('Resource <%s> already exists'
359
% resource_path)
360
resource = self.project.get_file(file_name)
361
if not resource.parent.exists():
362
raise exceptions.ResourceNotFoundError(
363
'Parent folder of <%s> does not exist' % resource.path)
364
fscommands = self._get_fscommands(resource)
365
try:
366
if kind == 'file':
367
fscommands.create_file(resource_path)
368
else:
369
fscommands.create_folder(resource_path)
370
except IOError, e:
371
raise exceptions.RopeError(e)
372
373
374
def _get_destination_for_move(resource, destination):
375
dest_path = resource.project._get_resource_path(destination)
376
if os.path.isdir(dest_path):
377
if destination != '':
378
return destination + '/' + resource.name
379
else:
380
return resource.name
381
return destination
382
383
384
class ChangeToData(object):
385
386
def convertChangeSet(self, change):
387
description = change.description
388
changes = []
389
for child in change.changes:
390
changes.append(self(child))
391
return (description, changes, change.time)
392
393
def convertChangeContents(self, change):
394
return (change.resource.path, change.new_contents, change.old_contents)
395
396
def convertMoveResource(self, change):
397
return (change.resource.path, change.new_resource.path)
398
399
def convertCreateResource(self, change):
400
return (change.resource.path, change.resource.is_folder())
401
402
def convertRemoveResource(self, change):
403
return (change.resource.path, change.resource.is_folder())
404
405
def __call__(self, change):
406
change_type = type(change)
407
if change_type in (CreateFolder, CreateFile):
408
change_type = CreateResource
409
method = getattr(self, 'convert' + change_type.__name__)
410
return (change_type.__name__, method(change))
411
412
413
class DataToChange(object):
414
415
def __init__(self, project):
416
self.project = project
417
418
def makeChangeSet(self, description, changes, time=None):
419
result = ChangeSet(description, time)
420
for child in changes:
421
result.add_change(self(child))
422
return result
423
424
def makeChangeContents(self, path, new_contents, old_contents):
425
resource = self.project.get_file(path)
426
return ChangeContents(resource, new_contents, old_contents)
427
428
def makeMoveResource(self, old_path, new_path):
429
resource = self.project.get_file(old_path)
430
return MoveResource(resource, new_path, exact=True)
431
432
def makeCreateResource(self, path, is_folder):
433
if is_folder:
434
resource = self.project.get_folder(path)
435
else:
436
resource = self.project.get_file(path)
437
return CreateResource(resource)
438
439
def makeRemoveResource(self, path, is_folder):
440
if is_folder:
441
resource = self.project.get_folder(path)
442
else:
443
resource = self.project.get_file(path)
444
return RemoveResource(resource)
445
446
def __call__(self, data):
447
method = getattr(self, 'make' + data[0])
448
return method(*data[1])
449
450