Merge lp:~nmb/qbzr/diffview-refactor into lp:qbzr
- diffview-refactor
- Merge into trunk2a
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexander Belchenko | Needs Information | ||
Review via email: mp+60971@code.launchpad.net |
Commit message
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.
IWATA Hidetaka (hid-iwata) wrote : | # |
Alexander Belchenko (bialix) wrote : | # |
If this is just mechanical extract of the Iwata code, then I think that's ok to land it.
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.
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?
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?
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
1 | === modified file 'lib/diffwindow.py' |
2 | --- lib/diffwindow.py 2011-05-11 11:49:14 +0000 |
3 | +++ lib/diffwindow.py 2011-05-13 21:40:03 +0000 |
4 | @@ -126,6 +126,168 @@ |
5 | return 'Unknown tree' |
6 | |
7 | |
8 | +class DiffItem(object): |
9 | + |
10 | + @classmethod |
11 | + def create(klass, trees, file_id, paths, changed_content, versioned, |
12 | + parent, name, kind, executable, filter = None): |
13 | + |
14 | + if parent == (None, None): # filter out TREE_ROOT (?) |
15 | + return None |
16 | + |
17 | + # check for manually deleted files (w/o using bzr rm commands) |
18 | + if kind[1] is None: |
19 | + if versioned == (False, True): |
20 | + # added and missed |
21 | + return None |
22 | + if versioned == (True, True): |
23 | + versioned = (True, False) |
24 | + paths = (paths[0], None) |
25 | + |
26 | + renamed = (parent[0], name[0]) != (parent[1], name[1]) |
27 | + |
28 | + dates = [None, None] |
29 | + for ix in range(2): |
30 | + if versioned[ix]: |
31 | + try: |
32 | + dates[ix] = trees[ix].get_file_mtime(file_id, paths[ix]) |
33 | + except OSError, e: |
34 | + if not renamed or e.errno != errno.ENOENT: |
35 | + raise |
36 | + # If we get ENOENT error then probably we trigger |
37 | + # bug #251532 in bzrlib. Take current time instead |
38 | + dates[ix] = time.time() |
39 | + except FileTimestampUnavailable: |
40 | + # ghosts around us (see Bug #513096) |
41 | + dates[ix] = 0 # using 1970/1/1 instead |
42 | + |
43 | + properties_changed = [] |
44 | + if bool(executable[0]) != bool(executable[1]): |
45 | + descr = {True: "+x", False: "-x", None: None} |
46 | + properties_changed.append((descr[executable[0]], |
47 | + descr[executable[1]])) |
48 | + |
49 | + if versioned == (True, False): |
50 | + status = N_('removed') |
51 | + elif versioned == (False, True): |
52 | + status = N_('added') |
53 | + elif renamed and changed_content: |
54 | + status = N_('renamed and modified') |
55 | + elif renamed: |
56 | + status = N_('renamed') |
57 | + else: |
58 | + status = N_('modified') |
59 | + # check filter options |
60 | + if filter and not filter(status): |
61 | + return None |
62 | + |
63 | + return klass(trees, file_id, paths, changed_content, versioned, kind, |
64 | + properties_changed, dates, status) |
65 | + |
66 | + def __init__(self, trees, file_id, paths, changed_content, versioned, kind, |
67 | + properties_changed, dates, status): |
68 | + self.trees = trees |
69 | + self.file_id = file_id |
70 | + self.paths = paths |
71 | + self.changed_content = changed_content |
72 | + self.versioned = versioned |
73 | + self.kind = kind |
74 | + self.properties_changed = properties_changed |
75 | + self.dates = dates |
76 | + self.status = status |
77 | + |
78 | + self._lines = None |
79 | + self._binary = None |
80 | + self._group_cache = {} |
81 | + self._encodings = [None, None] |
82 | + self._ulines = [None, None] |
83 | + |
84 | + def load(self): |
85 | + if self._lines is None: |
86 | + self._load_lines() |
87 | + |
88 | + def _load_lines(self): |
89 | + if ((self.versioned[0] != self.versioned[1] or self.changed_content) |
90 | + and (self.kind[0] == 'file' or self.kind[1] == 'file')): |
91 | + lines = [] |
92 | + binary = False |
93 | + for ix, tree in enumerate(self.trees): |
94 | + content = () |
95 | + if self.versioned[ix] and self.kind[ix] == 'file': |
96 | + content = get_file_lines_from_tree(tree, self.file_id) |
97 | + lines.append(content) |
98 | + binary = binary or is_binary_content(content) |
99 | + self._lines = lines |
100 | + self._binary = binary |
101 | + else: |
102 | + self._lines = ((),()) |
103 | + self._binary = False |
104 | + |
105 | + @property |
106 | + def lines(self): |
107 | + if self._lines is None: |
108 | + self._load_lines() |
109 | + return self._lines |
110 | + |
111 | + @property |
112 | + def binary(self): |
113 | + if self._binary is None: |
114 | + self._load_lines() |
115 | + return self._binary |
116 | + |
117 | + def groups(self, complete, ignore_whitespace): |
118 | + key = (complete, ignore_whitespace) |
119 | + groups = self._group_cache.get(key) |
120 | + if groups is not None: |
121 | + return groups |
122 | + |
123 | + lines = self.lines |
124 | + |
125 | + if not self.binary: |
126 | + if self.versioned == (True, False): |
127 | + groups = [[('delete', 0, len(lines[0]), 0, 0)]] |
128 | + elif self.versioned == (False, True): |
129 | + groups = [[('insert', 0, 0, 0, len(lines[1]))]] |
130 | + else: |
131 | + groups = self.difference_groups(lines, complete, ignore_whitespace) |
132 | + else: |
133 | + groups = [] |
134 | + |
135 | + self._group_cache[key] = groups |
136 | + return groups |
137 | + |
138 | + def difference_groups(self, lines, complete, ignore_whitespace): |
139 | + left, right = lines |
140 | + if ignore_whitespace: |
141 | + table = string.maketrans("", "") |
142 | + strip = lambda l : l.translate(table, string.whitespace) |
143 | + left = (line.translate(table, string.whitespace) for line in left) |
144 | + right = (line.translate(table, string.whitespace) for line in right) |
145 | + matcher = SequenceMatcher(None, left, right) |
146 | + if complete: |
147 | + groups = list([matcher.get_opcodes()]) |
148 | + else: |
149 | + groups = list(matcher.get_grouped_opcodes()) |
150 | + |
151 | + return groups |
152 | + |
153 | + |
154 | + def encode(self, encodings): |
155 | + lines = self.lines |
156 | + ulines = self._ulines |
157 | + for i in range(2): |
158 | + if encodings[i] != self._encodings[i]: |
159 | + self._encodings[i] = encodings[i] |
160 | + if self.binary: |
161 | + ulines[i] = lines[i][:] |
162 | + else: |
163 | + try: |
164 | + ulines[i] = [l.decode(encodings[i]) for l in lines[i]] |
165 | + except UnicodeDecodeError, e: |
166 | + trace.note('Failed to decode using %s, falling back to latin1', e.encoding) |
167 | + ulines[i] = [l.decode('latin1') for l in lines[i]] |
168 | + return ulines |
169 | + |
170 | class DiffWindow(QBzrWindow): |
171 | |
172 | def __init__(self, arg_provider, parent=None, |
173 | @@ -459,96 +621,24 @@ |
174 | # tree, e.g. for added/deleted file |
175 | |
176 | self.processEvents() |
177 | - |
178 | - if parent == (None, None): # filter out TREE_ROOT (?) |
179 | - continue |
180 | - |
181 | - # check for manually deleted files (w/o using bzr rm commands) |
182 | - if kind[1] is None: |
183 | - if versioned == (False, True): |
184 | - # added and missed |
185 | - continue |
186 | - if versioned == (True, True): |
187 | - versioned = (True, False) |
188 | - paths = (paths[0], None) |
189 | - |
190 | - renamed = (parent[0], name[0]) != (parent[1], name[1]) |
191 | - |
192 | - dates = [None, None] |
193 | - for ix in range(2): |
194 | - if versioned[ix]: |
195 | - try: |
196 | - dates[ix] = self.trees[ix].get_file_mtime(file_id, paths[ix]) |
197 | - except OSError, e: |
198 | - if not renamed or e.errno != errno.ENOENT: |
199 | - raise |
200 | - # If we get ENOENT error then probably we trigger |
201 | - # bug #251532 in bzrlib. Take current time instead |
202 | - dates[ix] = time.time() |
203 | - except FileTimestampUnavailable: |
204 | - # ghosts around us (see Bug #513096) |
205 | - dates[ix] = 0 # using 1970/1/1 instead |
206 | - |
207 | - properties_changed = [] |
208 | - if bool(executable[0]) != bool(executable[1]): |
209 | - descr = {True: "+x", False: "-x", None: None} |
210 | - properties_changed.append((descr[executable[0]], |
211 | - descr[executable[1]])) |
212 | - |
213 | - if versioned == (True, False): |
214 | - status = N_('removed') |
215 | - elif versioned == (False, True): |
216 | - status = N_('added') |
217 | - elif renamed and changed_content: |
218 | - status = N_('renamed and modified') |
219 | - elif renamed: |
220 | - status = N_('renamed') |
221 | - else: |
222 | - status = N_('modified') |
223 | - # check filter options |
224 | - if not self.filter_options.check(status): |
225 | - continue |
226 | - |
227 | - if ((versioned[0] != versioned[1] or changed_content) |
228 | - and (kind[0] == 'file' or kind[1] == 'file')): |
229 | - lines = [] |
230 | - binary = False |
231 | - for ix, tree in enumerate(self.trees): |
232 | - content = () |
233 | - if versioned[ix] and kind[ix] == 'file': |
234 | - content = get_file_lines_from_tree(tree, file_id) |
235 | - lines.append(content) |
236 | - binary = binary or is_binary_content(content) |
237 | - self.processEvents() |
238 | - if not binary: |
239 | - if versioned == (True, False): |
240 | - groups = [[('delete', 0, len(lines[0]), 0, 0)]] |
241 | - elif versioned == (False, True): |
242 | - groups = [[('insert', 0, 0, 0, len(lines[1]))]] |
243 | - else: |
244 | - groups = self.difference_groups(lines[0], lines[1]) |
245 | - ulines = [] |
246 | - for l, encoding in zip(lines, [self.encoding_selector_left.encoding, |
247 | - self.encoding_selector_right.encoding]): |
248 | - try: |
249 | - ulines.append([i.decode(encoding) for i in l]) |
250 | - except UnicodeDecodeError, e: |
251 | - trace.note('Failed to decode using %s, falling back to latin1', e.encoding) |
252 | - ulines.append([i.decode('latin1') for i in l]) |
253 | - lines = ulines |
254 | - data = ((),()) |
255 | - else: |
256 | - groups = [] |
257 | - data = [''.join(l) for l in lines] |
258 | - else: |
259 | - binary = False |
260 | - lines = ((),()) |
261 | - groups = () |
262 | - data = ("", "") |
263 | + di = DiffItem.create(self.trees, file_id, paths, changed_content, |
264 | + versioned, parent, name, kind, executable, |
265 | + filter = self.filter_options.check) |
266 | + if not di: |
267 | + continue |
268 | + |
269 | + lines = di.lines |
270 | + self.processEvents() |
271 | + groups = di.groups(self.complete, self.ignore_whitespace) |
272 | + self.processEvents() |
273 | + ulines = di.encode([self.encoding_selector_left.encoding, |
274 | + self.encoding_selector_right.encoding]) |
275 | + data = [''.join(l) for l in ulines] |
276 | + |
277 | for view in self.views: |
278 | - view.append_diff(list(paths), file_id, kind, status, |
279 | - dates, versioned, binary, lines, groups, |
280 | - data, properties_changed) |
281 | + view.append_diff(list(di.paths), di.file_id, di.kind, di.status, |
282 | + di.dates, di.versioned, di.binary, ulines, groups, |
283 | + data, di.properties_changed) |
284 | self.processEvents() |
285 | no_changes = False |
286 | except PathsNotVersionedError, e: |
287 | @@ -565,21 +655,6 @@ |
288 | gettext('&OK')) |
289 | self.view_refresh.setEnabled(self.can_refresh()) |
290 | |
291 | - def difference_groups(self, left, right): |
292 | - if self.ignore_whitespace: |
293 | - table = string.maketrans("", "") |
294 | - strip = lambda l : l.translate(table, string.whitespace) |
295 | - left = (line.translate(table, string.whitespace) for line in left) |
296 | - right = (line.translate(table, string.whitespace) for line in right) |
297 | - matcher = SequenceMatcher(None, left, right) |
298 | - self.processEvents() |
299 | - if self.complete: |
300 | - groups = list([matcher.get_opcodes()]) |
301 | - else: |
302 | - groups = list(matcher.get_grouped_opcodes()) |
303 | - |
304 | - return groups |
305 | - |
306 | def click_toggle_view_mode(self, checked): |
307 | if checked: |
308 | view = self.sdiffview |
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.