Merge lp:~nmb/qbzr/diffview-refactor into lp:qbzr

Proposed by Neil Martinsen-Burrell
Status: Merged
Merged at revision: 1393
Proposed branch: lp:~nmb/qbzr/diffview-refactor
Merge into: lp:qbzr
Diff against target: 308 lines (+179/-104)
1 file modified
lib/diffwindow.py (+179/-104)
To merge this branch: bzr merge lp:~nmb/qbzr/diffview-refactor
Reviewer Review Type Date Requested Status
Alexander Belchenko Needs Information
Review via email: mp+60971@code.launchpad.net

Description of the change

This abstracts the diff loading code from out of DiffWindow so that it can be use in other windows which want to use diff previews. I cherrypicked these changes from lp:~hid-iwata/qbzr/qshelve as a separate revision. This is a standalone change which is separate from the added shelve functionality of any toolbar changes.

To post a comment you must log in.
Revision history for this message
IWATA Hidetaka (hid-iwata) wrote :

This changes are separated from qshelve code originally.

You can merge it simply by doing::
  bzr merge lp:~hid-iwata/qbzr/qshelve -r revid:<email address hidden>

And it would make merging qshelve code more easier.

Regards.

Revision history for this message
Alexander Belchenko (bialix) wrote :

If this is just mechanical extract of the Iwata code, then I think that's ok to land it.

Revision history for this message
Alexander Belchenko (bialix) wrote :

Neil, as I can see the same refactoring is available in qshelve branch, and this is really the first revision in that branch so we can merge it preserving the history.

Revision history for this message
Alexander Belchenko (bialix) wrote :

Neil, as I can see your code is different because you've also added the load method:

+ def load(self):
+ if self._lines is None:
+ self._load_lines()

Why it's needed for you?

review: Needs Information
Revision history for this message
IWATA Hidetaka (hid-iwata) wrote :

It's only used from ShelveListWidget. (it was added by r1360 of qshelve branch.)

> Neil, as I can see your code is different because you've also added the load
> method:
>
>
> + def load(self):
> + if self._lines is None:
> + self._load_lines()
>
> Why it's needed for you?

Revision history for this message
Alexander Belchenko (bialix) wrote :

iwata пишет:
> It's only used from ShelveListWidget. (it was added by r1360 of qshelve branch.)

OK, thanks for clarifications Hidetaka.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/diffwindow.py'
--- lib/diffwindow.py 2011-05-11 11:49:14 +0000
+++ lib/diffwindow.py 2011-05-13 21:40:03 +0000
@@ -126,6 +126,168 @@
126 return 'Unknown tree'126 return 'Unknown tree'
127127
128128
129class DiffItem(object):
130
131 @classmethod
132 def create(klass, trees, file_id, paths, changed_content, versioned,
133 parent, name, kind, executable, filter = None):
134
135 if parent == (None, None): # filter out TREE_ROOT (?)
136 return None
137
138 # check for manually deleted files (w/o using bzr rm commands)
139 if kind[1] is None:
140 if versioned == (False, True):
141 # added and missed
142 return None
143 if versioned == (True, True):
144 versioned = (True, False)
145 paths = (paths[0], None)
146
147 renamed = (parent[0], name[0]) != (parent[1], name[1])
148
149 dates = [None, None]
150 for ix in range(2):
151 if versioned[ix]:
152 try:
153 dates[ix] = trees[ix].get_file_mtime(file_id, paths[ix])
154 except OSError, e:
155 if not renamed or e.errno != errno.ENOENT:
156 raise
157 # If we get ENOENT error then probably we trigger
158 # bug #251532 in bzrlib. Take current time instead
159 dates[ix] = time.time()
160 except FileTimestampUnavailable:
161 # ghosts around us (see Bug #513096)
162 dates[ix] = 0 # using 1970/1/1 instead
163
164 properties_changed = []
165 if bool(executable[0]) != bool(executable[1]):
166 descr = {True: "+x", False: "-x", None: None}
167 properties_changed.append((descr[executable[0]],
168 descr[executable[1]]))
169
170 if versioned == (True, False):
171 status = N_('removed')
172 elif versioned == (False, True):
173 status = N_('added')
174 elif renamed and changed_content:
175 status = N_('renamed and modified')
176 elif renamed:
177 status = N_('renamed')
178 else:
179 status = N_('modified')
180 # check filter options
181 if filter and not filter(status):
182 return None
183
184 return klass(trees, file_id, paths, changed_content, versioned, kind,
185 properties_changed, dates, status)
186
187 def __init__(self, trees, file_id, paths, changed_content, versioned, kind,
188 properties_changed, dates, status):
189 self.trees = trees
190 self.file_id = file_id
191 self.paths = paths
192 self.changed_content = changed_content
193 self.versioned = versioned
194 self.kind = kind
195 self.properties_changed = properties_changed
196 self.dates = dates
197 self.status = status
198
199 self._lines = None
200 self._binary = None
201 self._group_cache = {}
202 self._encodings = [None, None]
203 self._ulines = [None, None]
204
205 def load(self):
206 if self._lines is None:
207 self._load_lines()
208
209 def _load_lines(self):
210 if ((self.versioned[0] != self.versioned[1] or self.changed_content)
211 and (self.kind[0] == 'file' or self.kind[1] == 'file')):
212 lines = []
213 binary = False
214 for ix, tree in enumerate(self.trees):
215 content = ()
216 if self.versioned[ix] and self.kind[ix] == 'file':
217 content = get_file_lines_from_tree(tree, self.file_id)
218 lines.append(content)
219 binary = binary or is_binary_content(content)
220 self._lines = lines
221 self._binary = binary
222 else:
223 self._lines = ((),())
224 self._binary = False
225
226 @property
227 def lines(self):
228 if self._lines is None:
229 self._load_lines()
230 return self._lines
231
232 @property
233 def binary(self):
234 if self._binary is None:
235 self._load_lines()
236 return self._binary
237
238 def groups(self, complete, ignore_whitespace):
239 key = (complete, ignore_whitespace)
240 groups = self._group_cache.get(key)
241 if groups is not None:
242 return groups
243
244 lines = self.lines
245
246 if not self.binary:
247 if self.versioned == (True, False):
248 groups = [[('delete', 0, len(lines[0]), 0, 0)]]
249 elif self.versioned == (False, True):
250 groups = [[('insert', 0, 0, 0, len(lines[1]))]]
251 else:
252 groups = self.difference_groups(lines, complete, ignore_whitespace)
253 else:
254 groups = []
255
256 self._group_cache[key] = groups
257 return groups
258
259 def difference_groups(self, lines, complete, ignore_whitespace):
260 left, right = lines
261 if ignore_whitespace:
262 table = string.maketrans("", "")
263 strip = lambda l : l.translate(table, string.whitespace)
264 left = (line.translate(table, string.whitespace) for line in left)
265 right = (line.translate(table, string.whitespace) for line in right)
266 matcher = SequenceMatcher(None, left, right)
267 if complete:
268 groups = list([matcher.get_opcodes()])
269 else:
270 groups = list(matcher.get_grouped_opcodes())
271
272 return groups
273
274
275 def encode(self, encodings):
276 lines = self.lines
277 ulines = self._ulines
278 for i in range(2):
279 if encodings[i] != self._encodings[i]:
280 self._encodings[i] = encodings[i]
281 if self.binary:
282 ulines[i] = lines[i][:]
283 else:
284 try:
285 ulines[i] = [l.decode(encodings[i]) for l in lines[i]]
286 except UnicodeDecodeError, e:
287 trace.note('Failed to decode using %s, falling back to latin1', e.encoding)
288 ulines[i] = [l.decode('latin1') for l in lines[i]]
289 return ulines
290
129class DiffWindow(QBzrWindow):291class DiffWindow(QBzrWindow):
130292
131 def __init__(self, arg_provider, parent=None,293 def __init__(self, arg_provider, parent=None,
@@ -459,96 +621,24 @@
459 # tree, e.g. for added/deleted file621 # tree, e.g. for added/deleted file
460622
461 self.processEvents()623 self.processEvents()
462624 di = DiffItem.create(self.trees, file_id, paths, changed_content,
463 if parent == (None, None): # filter out TREE_ROOT (?)625 versioned, parent, name, kind, executable,
464 continue626 filter = self.filter_options.check)
465627 if not di:
466 # check for manually deleted files (w/o using bzr rm commands)628 continue
467 if kind[1] is None:629
468 if versioned == (False, True):630 lines = di.lines
469 # added and missed631 self.processEvents()
470 continue632 groups = di.groups(self.complete, self.ignore_whitespace)
471 if versioned == (True, True):633 self.processEvents()
472 versioned = (True, False)634 ulines = di.encode([self.encoding_selector_left.encoding,
473 paths = (paths[0], None)635 self.encoding_selector_right.encoding])
474636 data = [''.join(l) for l in ulines]
475 renamed = (parent[0], name[0]) != (parent[1], name[1])637
476
477 dates = [None, None]
478 for ix in range(2):
479 if versioned[ix]:
480 try:
481 dates[ix] = self.trees[ix].get_file_mtime(file_id, paths[ix])
482 except OSError, e:
483 if not renamed or e.errno != errno.ENOENT:
484 raise
485 # If we get ENOENT error then probably we trigger
486 # bug #251532 in bzrlib. Take current time instead
487 dates[ix] = time.time()
488 except FileTimestampUnavailable:
489 # ghosts around us (see Bug #513096)
490 dates[ix] = 0 # using 1970/1/1 instead
491
492 properties_changed = []
493 if bool(executable[0]) != bool(executable[1]):
494 descr = {True: "+x", False: "-x", None: None}
495 properties_changed.append((descr[executable[0]],
496 descr[executable[1]]))
497
498 if versioned == (True, False):
499 status = N_('removed')
500 elif versioned == (False, True):
501 status = N_('added')
502 elif renamed and changed_content:
503 status = N_('renamed and modified')
504 elif renamed:
505 status = N_('renamed')
506 else:
507 status = N_('modified')
508 # check filter options
509 if not self.filter_options.check(status):
510 continue
511
512 if ((versioned[0] != versioned[1] or changed_content)
513 and (kind[0] == 'file' or kind[1] == 'file')):
514 lines = []
515 binary = False
516 for ix, tree in enumerate(self.trees):
517 content = ()
518 if versioned[ix] and kind[ix] == 'file':
519 content = get_file_lines_from_tree(tree, file_id)
520 lines.append(content)
521 binary = binary or is_binary_content(content)
522 self.processEvents()
523 if not binary:
524 if versioned == (True, False):
525 groups = [[('delete', 0, len(lines[0]), 0, 0)]]
526 elif versioned == (False, True):
527 groups = [[('insert', 0, 0, 0, len(lines[1]))]]
528 else:
529 groups = self.difference_groups(lines[0], lines[1])
530 ulines = []
531 for l, encoding in zip(lines, [self.encoding_selector_left.encoding,
532 self.encoding_selector_right.encoding]):
533 try:
534 ulines.append([i.decode(encoding) for i in l])
535 except UnicodeDecodeError, e:
536 trace.note('Failed to decode using %s, falling back to latin1', e.encoding)
537 ulines.append([i.decode('latin1') for i in l])
538 lines = ulines
539 data = ((),())
540 else:
541 groups = []
542 data = [''.join(l) for l in lines]
543 else:
544 binary = False
545 lines = ((),())
546 groups = ()
547 data = ("", "")
548 for view in self.views:638 for view in self.views:
549 view.append_diff(list(paths), file_id, kind, status,639 view.append_diff(list(di.paths), di.file_id, di.kind, di.status,
550 dates, versioned, binary, lines, groups,640 di.dates, di.versioned, di.binary, ulines, groups,
551 data, properties_changed)641 data, di.properties_changed)
552 self.processEvents()642 self.processEvents()
553 no_changes = False643 no_changes = False
554 except PathsNotVersionedError, e:644 except PathsNotVersionedError, e:
@@ -565,21 +655,6 @@
565 gettext('&OK'))655 gettext('&OK'))
566 self.view_refresh.setEnabled(self.can_refresh())656 self.view_refresh.setEnabled(self.can_refresh())
567657
568 def difference_groups(self, left, right):
569 if self.ignore_whitespace:
570 table = string.maketrans("", "")
571 strip = lambda l : l.translate(table, string.whitespace)
572 left = (line.translate(table, string.whitespace) for line in left)
573 right = (line.translate(table, string.whitespace) for line in right)
574 matcher = SequenceMatcher(None, left, right)
575 self.processEvents()
576 if self.complete:
577 groups = list([matcher.get_opcodes()])
578 else:
579 groups = list(matcher.get_grouped_opcodes())
580
581 return groups
582
583 def click_toggle_view_mode(self, checked):658 def click_toggle_view_mode(self, checked):
584 if checked:659 if checked:
585 view = self.sdiffview660 view = self.sdiffview

Subscribers

People subscribed via source and target branches