131 by time.ctime(). If not specified, the strings default to blanks. |
131 by time.ctime(). If not specified, the strings default to blanks. |
132 |
132 |
133 Example: |
133 Example: |
134 |
134 |
135 <pre> |
135 <pre> |
136 >>> print ''.join(context_diff('one\ntwo\nthree\nfour\n'.splitlines(1), |
136 >>> print ''.join( |
|
137 ... context_diff('one\ntwo\nthree\nfour\n'.splitlines(1), |
137 ... 'zero\none\ntree\nfour\n'.splitlines(1), 'Original', 'Current', |
138 ... 'zero\none\ntree\nfour\n'.splitlines(1), 'Original', 'Current', |
138 ... 'Sat Jan 26 23:30:50 1991', 'Fri Jun 06 10:22:46 2003')), |
139 ... 'Sat Jan 26 23:30:50 1991', 'Fri Jun 06 10:22:46 2003')), |
139 *** Original Sat Jan 26 23:30:50 1991 |
140 *** Original Sat Jan 26 23:30:50 1991 |
140 --- Current Fri Jun 06 10:22:46 2003 |
141 --- Current Fri Jun 06 10:22:46 2003 |
141 *************** |
142 *************** |
160 @param n number of lines of context (integer) |
161 @param n number of lines of context (integer) |
161 @param lineterm line termination string (string) |
162 @param lineterm line termination string (string) |
162 @return a generator yielding lines of differences |
163 @return a generator yielding lines of differences |
163 """ |
164 """ |
164 started = False |
165 started = False |
165 prefixmap = {'insert': '+ ', 'delete': '- ', 'replace': '! ', 'equal': ' '} |
166 prefixmap = {'insert': '+ ', 'delete': '- ', 'replace': '! ', |
|
167 'equal': ' '} |
166 for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n): |
168 for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n): |
167 if not started: |
169 if not started: |
168 yield '*** {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm) |
170 yield '*** {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm) |
169 yield '--- {0}\t{1}{2}'.format(tofile, tofiledate, lineterm) |
171 yield '--- {0}\t{1}{2}'.format(tofile, tofiledate, lineterm) |
170 started = True |
172 started = True |
171 |
173 |
172 yield '***************{0}'.format(lineterm) |
174 yield '***************{0}'.format(lineterm) |
173 if group[-1][2] - group[0][1] >= 2: |
175 if group[-1][2] - group[0][1] >= 2: |
174 yield '*** {0:d},{1:d} ****{2}'.format(group[0][1] + 1, group[-1][2], lineterm) |
176 yield '*** {0:d},{1:d} ****{2}'.format( |
|
177 group[0][1] + 1, group[-1][2], lineterm) |
175 else: |
178 else: |
176 yield '*** {0:d} ****{1}'.format(group[-1][2], lineterm) |
179 yield '*** {0:d} ****{1}'.format(group[-1][2], lineterm) |
177 visiblechanges = [e for e in group if e[0] in ('replace', 'delete')] |
180 visiblechanges = [e for e in group if e[0] in ('replace', 'delete')] |
178 if visiblechanges: |
181 if visiblechanges: |
179 for tag, i1, i2, _, _ in group: |
182 for tag, i1, i2, _, _ in group: |
180 if tag != 'insert': |
183 if tag != 'insert': |
181 for line in a[i1:i2]: |
184 for line in a[i1:i2]: |
182 yield prefixmap[tag] + line |
185 yield prefixmap[tag] + line |
183 |
186 |
184 if group[-1][4] - group[0][3] >= 2: |
187 if group[-1][4] - group[0][3] >= 2: |
185 yield '--- {0:d},{1:d} ----{2}'.format(group[0][3] + 1, group[-1][4], lineterm) |
188 yield '--- {0:d},{1:d} ----{2}'.format( |
|
189 group[0][3] + 1, group[-1][4], lineterm) |
186 else: |
190 else: |
187 yield '--- {0:d} ----{1}'.format(group[-1][4], lineterm) |
191 yield '--- {0:d} ----{1}'.format(group[-1][4], lineterm) |
188 visiblechanges = [e for e in group if e[0] in ('replace', 'insert')] |
192 visiblechanges = [e for e in group if e[0] in ('replace', 'insert')] |
189 if visiblechanges: |
193 if visiblechanges: |
190 for tag, _, _, j1, j2 in group: |
194 for tag, _, _, j1, j2 in group: |
207 self.setupUi(self) |
211 self.setupUi(self) |
208 |
212 |
209 self.file1Completer = E5FileCompleter(self.file1Edit) |
213 self.file1Completer = E5FileCompleter(self.file1Edit) |
210 self.file2Completer = E5FileCompleter(self.file2Edit) |
214 self.file2Completer = E5FileCompleter(self.file2Edit) |
211 |
215 |
212 self.diffButton = \ |
216 self.diffButton = self.buttonBox.addButton( |
213 self.buttonBox.addButton(self.trUtf8("Compare"), QDialogButtonBox.ActionRole) |
217 self.trUtf8("Compare"), QDialogButtonBox.ActionRole) |
214 self.diffButton.setToolTip( |
218 self.diffButton.setToolTip( |
215 self.trUtf8("Press to perform the comparison of the two files")) |
219 self.trUtf8("Press to perform the comparison of the two files")) |
216 self.saveButton = \ |
220 self.saveButton = self.buttonBox.addButton( |
217 self.buttonBox.addButton(self.trUtf8("Save"), QDialogButtonBox.ActionRole) |
221 self.trUtf8("Save"), QDialogButtonBox.ActionRole) |
218 self.saveButton.setToolTip(self.trUtf8("Save the output to a patch file")) |
222 self.saveButton.setToolTip( |
|
223 self.trUtf8("Save the output to a patch file")) |
219 self.diffButton.setEnabled(False) |
224 self.diffButton.setEnabled(False) |
220 self.saveButton.setEnabled(False) |
225 self.saveButton.setEnabled(False) |
221 self.diffButton.setDefault(True) |
226 self.diffButton.setDefault(True) |
222 |
227 |
223 self.filename1 = '' |
228 self.filename1 = '' |
311 f.write(txt) |
316 f.write(txt) |
312 except UnicodeError: |
317 except UnicodeError: |
313 pass |
318 pass |
314 f.close() |
319 f.close() |
315 except IOError as why: |
320 except IOError as why: |
316 E5MessageBox.critical(self, self.trUtf8('Save Diff'), |
321 E5MessageBox.critical( |
317 self.trUtf8('<p>The patch file <b>{0}</b> could not be saved.<br />' |
322 self, self.trUtf8('Save Diff'), |
318 'Reason: {1}</p>') |
323 self.trUtf8( |
319 .format(fname, str(why))) |
324 '<p>The patch file <b>{0}</b> could not be saved.<br />' |
|
325 'Reason: {1}</p>').format(fname, str(why))) |
320 |
326 |
321 @pyqtSlot() |
327 @pyqtSlot() |
322 def on_diffButton_clicked(self): |
328 def on_diffButton_clicked(self): |
323 """ |
329 """ |
324 Private slot to handle the Compare button press. |
330 Private slot to handle the Compare button press. |
349 lines2 = f2.readlines() |
356 lines2 = f2.readlines() |
350 f2.close() |
357 f2.close() |
351 except IOError: |
358 except IOError: |
352 E5MessageBox.critical(self, |
359 E5MessageBox.critical(self, |
353 self.trUtf8("Compare Files"), |
360 self.trUtf8("Compare Files"), |
354 self.trUtf8("""<p>The file <b>{0}</b> could not be read.</p>""") |
361 self.trUtf8( |
|
362 """<p>The file <b>{0}</b> could not be read.</p>""") |
355 .format(self.filename2)) |
363 .format(self.filename2)) |
356 return |
364 return |
357 |
365 |
358 self.contents.clear() |
366 self.contents.clear() |
359 self.saveButton.setEnabled(False) |
367 self.saveButton.setEnabled(False) |
360 |
368 |
361 if self.unifiedRadioButton.isChecked(): |
369 if self.unifiedRadioButton.isChecked(): |
362 self.__generateUnifiedDiff(lines1, lines2, self.filename1, self.filename2, |
370 self.__generateUnifiedDiff( |
363 filemtime1, filemtime2) |
371 lines1, lines2, self.filename1, self.filename2, |
|
372 filemtime1, filemtime2) |
364 else: |
373 else: |
365 self.__generateContextDiff(lines1, lines2, self.filename1, self.filename2, |
374 self.__generateContextDiff( |
366 filemtime1, filemtime2) |
375 lines1, lines2, self.filename1, self.filename2, |
|
376 filemtime1, filemtime2) |
367 |
377 |
368 tc = self.contents.textCursor() |
378 tc = self.contents.textCursor() |
369 tc.movePosition(QTextCursor.Start) |
379 tc.movePosition(QTextCursor.Start) |
370 self.contents.setTextCursor(tc) |
380 self.contents.setTextCursor(tc) |
371 self.contents.ensureCursorVisible() |
381 self.contents.ensureCursorVisible() |
412 paras += 1 |
422 paras += 1 |
413 if not (paras % self.updateInterval): |
423 if not (paras % self.updateInterval): |
414 QApplication.processEvents() |
424 QApplication.processEvents() |
415 |
425 |
416 if paras == 0: |
426 if paras == 0: |
417 self.__appendText(self.trUtf8('There is no difference.'), self.cNormalFormat) |
427 self.__appendText( |
|
428 self.trUtf8('There is no difference.'), self.cNormalFormat) |
418 |
429 |
419 def __generateContextDiff(self, a, b, fromfile, tofile, |
430 def __generateContextDiff(self, a, b, fromfile, tofile, |
420 fromfiledate, tofiledate): |
431 fromfiledate, tofiledate): |
421 """ |
432 """ |
422 Private slot to generate a context diff output. |
433 Private slot to generate a context diff output. |
435 format = self.cAddedFormat |
446 format = self.cAddedFormat |
436 elif line.startswith('- '): |
447 elif line.startswith('- '): |
437 format = self.cRemovedFormat |
448 format = self.cRemovedFormat |
438 elif line.startswith('! '): |
449 elif line.startswith('! '): |
439 format = self.cReplacedFormat |
450 format = self.cReplacedFormat |
440 elif (line.startswith('*** ') or line.startswith('--- ')) and paras > 1: |
451 elif (line.startswith('*** ') or line.startswith('--- ')) and \ |
|
452 paras > 1: |
441 format = self.cLineNoFormat |
453 format = self.cLineNoFormat |
442 else: |
454 else: |
443 format = self.cNormalFormat |
455 format = self.cNormalFormat |
444 self.__appendText(line, format) |
456 self.__appendText(line, format) |
445 paras += 1 |
457 paras += 1 |
446 if not (paras % self.updateInterval): |
458 if not (paras % self.updateInterval): |
447 QApplication.processEvents() |
459 QApplication.processEvents() |
448 |
460 |
449 if paras == 0: |
461 if paras == 0: |
450 self.__appendText(self.trUtf8('There is no difference.'), self.cNormalFormat) |
462 self.__appendText( |
|
463 self.trUtf8('There is no difference.'), self.cNormalFormat) |
451 |
464 |
452 def __fileChanged(self): |
465 def __fileChanged(self): |
453 """ |
466 """ |
454 Private slot to enable/disable the Compare button. |
467 Private slot to enable/disable the Compare button. |
455 """ |
468 """ |
500 |
513 |
501 @param parent reference to the parent widget (QWidget) |
514 @param parent reference to the parent widget (QWidget) |
502 """ |
515 """ |
503 super().__init__(parent) |
516 super().__init__(parent) |
504 |
517 |
505 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) |
518 self.setStyle(Preferences.getUI("Style"), |
|
519 Preferences.getUI("StyleSheet")) |
506 |
520 |
507 self.cw = DiffDialog(self) |
521 self.cw = DiffDialog(self) |
508 self.cw.installEventFilter(self) |
522 self.cw.installEventFilter(self) |
509 size = self.cw.size() |
523 size = self.cw.size() |
510 self.setCentralWidget(self.cw) |
524 self.setCentralWidget(self.cw) |