UI/DiffDialog.py

branch
Py2 comp.
changeset 3057
10516539f238
parent 2677
3d4277929fb3
parent 3012
d177226027e2
child 3058
0a02c433f52d
equal deleted inserted replaced
3056:9986ec0e559a 3057:10516539f238
108 # The header lines contain a tab after the filename. 108 # The header lines contain a tab after the filename.
109 109
110 110
111 def context_diff(a, b, fromfile='', tofile='', 111 def context_diff(a, b, fromfile='', tofile='',
112 fromfiledate='', tofiledate='', n=3, lineterm='\n'): 112 fromfiledate='', tofiledate='', n=3, lineterm='\n'):
113 """ 113 r"""
114 Compare two sequences of lines; generate the delta as a context diff. 114 Compare two sequences of lines; generate the delta as a context diff.
115 115
116 Context diffs are a compact way of showing line changes and a few 116 Context diffs are a compact way of showing line changes and a few
117 lines of context. The number of context lines is set by 'n' which 117 lines of context. The number of context lines is set by 'n' which
118 defaults to three. 118 defaults to three.
133 by time.ctime(). If not specified, the strings default to blanks. 133 by time.ctime(). If not specified, the strings default to blanks.
134 134
135 Example: 135 Example:
136 136
137 <pre> 137 <pre>
138 &gt;&gt;&gt; print ''.join(context_diff('one\ntwo\nthree\nfour\n'.splitlines(1), 138 &gt;&gt;&gt; print ''.join(
139 ... context_diff('one\ntwo\nthree\nfour\n'.splitlines(1),
139 ... 'zero\none\ntree\nfour\n'.splitlines(1), 'Original', 'Current', 140 ... 'zero\none\ntree\nfour\n'.splitlines(1), 'Original', 'Current',
140 ... 'Sat Jan 26 23:30:50 1991', 'Fri Jun 06 10:22:46 2003')), 141 ... 'Sat Jan 26 23:30:50 1991', 'Fri Jun 06 10:22:46 2003')),
141 *** Original Sat Jan 26 23:30:50 1991 142 *** Original Sat Jan 26 23:30:50 1991
142 --- Current Fri Jun 06 10:22:46 2003 143 --- Current Fri Jun 06 10:22:46 2003
143 *************** 144 ***************
161 @param tofiledate modification time of the second file (string) 162 @param tofiledate modification time of the second file (string)
162 @param n number of lines of context (integer) 163 @param n number of lines of context (integer)
163 @param lineterm line termination string (string) 164 @param lineterm line termination string (string)
164 @return a generator yielding lines of differences 165 @return a generator yielding lines of differences
165 """ 166 """
166
167 started = False 167 started = False
168 prefixmap = {'insert': '+ ', 'delete': '- ', 'replace': '! ', 'equal': ' '} 168 prefixmap = {'insert': '+ ', 'delete': '- ', 'replace': '! ',
169 'equal': ' '}
169 for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n): 170 for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n):
170 if not started: 171 if not started:
171 yield '*** {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm) 172 yield '*** {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm)
172 yield '--- {0}\t{1}{2}'.format(tofile, tofiledate, lineterm) 173 yield '--- {0}\t{1}{2}'.format(tofile, tofiledate, lineterm)
173 started = True 174 started = True
174 175
175 yield '***************{0}'.format(lineterm) 176 yield '***************{0}'.format(lineterm)
176 if group[-1][2] - group[0][1] >= 2: 177 if group[-1][2] - group[0][1] >= 2:
177 yield '*** {0:d},{1:d} ****{2}'.format(group[0][1] + 1, group[-1][2], lineterm) 178 yield '*** {0:d},{1:d} ****{2}'.format(
179 group[0][1] + 1, group[-1][2], lineterm)
178 else: 180 else:
179 yield '*** {0:d} ****{1}'.format(group[-1][2], lineterm) 181 yield '*** {0:d} ****{1}'.format(group[-1][2], lineterm)
180 visiblechanges = [e for e in group if e[0] in ('replace', 'delete')] 182 visiblechanges = [e for e in group if e[0] in ('replace', 'delete')]
181 if visiblechanges: 183 if visiblechanges:
182 for tag, i1, i2, _, _ in group: 184 for tag, i1, i2, _, _ in group:
183 if tag != 'insert': 185 if tag != 'insert':
184 for line in a[i1:i2]: 186 for line in a[i1:i2]:
185 yield prefixmap[tag] + line 187 yield prefixmap[tag] + line
186 188
187 if group[-1][4] - group[0][3] >= 2: 189 if group[-1][4] - group[0][3] >= 2:
188 yield '--- {0:d},{1:d} ----{2}'.format(group[0][3] + 1, group[-1][4], lineterm) 190 yield '--- {0:d},{1:d} ----{2}'.format(
191 group[0][3] + 1, group[-1][4], lineterm)
189 else: 192 else:
190 yield '--- {0:d} ----{1}'.format(group[-1][4], lineterm) 193 yield '--- {0:d} ----{1}'.format(group[-1][4], lineterm)
191 visiblechanges = [e for e in group if e[0] in ('replace', 'insert')] 194 visiblechanges = [e for e in group if e[0] in ('replace', 'insert')]
192 if visiblechanges: 195 if visiblechanges:
193 for tag, _, _, j1, j2 in group: 196 for tag, _, _, j1, j2 in group:
201 Class implementing a dialog to compare two files. 204 Class implementing a dialog to compare two files.
202 """ 205 """
203 def __init__(self, parent=None): 206 def __init__(self, parent=None):
204 """ 207 """
205 Constructor 208 Constructor
209
210 @param parent reference to the parent widget (QWidget)
206 """ 211 """
207 super(DiffDialog, self).__init__(parent) 212 super(DiffDialog, self).__init__(parent)
208 self.setupUi(self) 213 self.setupUi(self)
209 214
210 self.file1Completer = E5FileCompleter(self.file1Edit) 215 self.file1Completer = E5FileCompleter(self.file1Edit)
211 self.file2Completer = E5FileCompleter(self.file2Edit) 216 self.file2Completer = E5FileCompleter(self.file2Edit)
212 217
213 self.diffButton = \ 218 self.diffButton = self.buttonBox.addButton(
214 self.buttonBox.addButton(self.trUtf8("Compare"), QDialogButtonBox.ActionRole) 219 self.trUtf8("Compare"), QDialogButtonBox.ActionRole)
215 self.diffButton.setToolTip( 220 self.diffButton.setToolTip(
216 self.trUtf8("Press to perform the comparison of the two files")) 221 self.trUtf8("Press to perform the comparison of the two files"))
217 self.saveButton = \ 222 self.saveButton = self.buttonBox.addButton(
218 self.buttonBox.addButton(self.trUtf8("Save"), QDialogButtonBox.ActionRole) 223 self.trUtf8("Save"), QDialogButtonBox.ActionRole)
219 self.saveButton.setToolTip(self.trUtf8("Save the output to a patch file")) 224 self.saveButton.setToolTip(
225 self.trUtf8("Save the output to a patch file"))
220 self.diffButton.setEnabled(False) 226 self.diffButton.setEnabled(False)
221 self.saveButton.setEnabled(False) 227 self.saveButton.setEnabled(False)
222 self.diffButton.setDefault(True) 228 self.diffButton.setDefault(True)
223 229
224 self.filename1 = '' 230 self.filename1 = ''
312 f.write(txt) 318 f.write(txt)
313 except UnicodeError: 319 except UnicodeError:
314 pass 320 pass
315 f.close() 321 f.close()
316 except IOError as why: 322 except IOError as why:
317 E5MessageBox.critical(self, self.trUtf8('Save Diff'), 323 E5MessageBox.critical(
318 self.trUtf8('<p>The patch file <b>{0}</b> could not be saved.<br />' 324 self, self.trUtf8('Save Diff'),
319 'Reason: {1}</p>') 325 self.trUtf8(
320 .format(fname, str(why))) 326 '<p>The patch file <b>{0}</b> could not be saved.<br />'
327 'Reason: {1}</p>').format(fname, str(why)))
321 328
322 @pyqtSlot() 329 @pyqtSlot()
323 def on_diffButton_clicked(self): 330 def on_diffButton_clicked(self):
324 """ 331 """
325 Private slot to handle the Compare button press. 332 Private slot to handle the Compare button press.
334 lines1 = f1.readlines() 341 lines1 = f1.readlines()
335 f1.close() 342 f1.close()
336 except IOError: 343 except IOError:
337 E5MessageBox.critical(self, 344 E5MessageBox.critical(self,
338 self.trUtf8("Compare Files"), 345 self.trUtf8("Compare Files"),
339 self.trUtf8("""<p>The file <b>{0}</b> could not be read.</p>""") 346 self.trUtf8(
347 """<p>The file <b>{0}</b> could not be read.</p>""")
340 .format(self.filename1)) 348 .format(self.filename1))
341 return 349 return
342 350
343 self.filename2 = Utilities.toNativeSeparators(self.file2Edit.text()) 351 self.filename2 = Utilities.toNativeSeparators(self.file2Edit.text())
344 try: 352 try:
350 lines2 = f2.readlines() 358 lines2 = f2.readlines()
351 f2.close() 359 f2.close()
352 except IOError: 360 except IOError:
353 E5MessageBox.critical(self, 361 E5MessageBox.critical(self,
354 self.trUtf8("Compare Files"), 362 self.trUtf8("Compare Files"),
355 self.trUtf8("""<p>The file <b>{0}</b> could not be read.</p>""") 363 self.trUtf8(
364 """<p>The file <b>{0}</b> could not be read.</p>""")
356 .format(self.filename2)) 365 .format(self.filename2))
357 return 366 return
358 367
359 self.contents.clear() 368 self.contents.clear()
360 self.saveButton.setEnabled(False) 369 self.saveButton.setEnabled(False)
361 370
362 if self.unifiedRadioButton.isChecked(): 371 if self.unifiedRadioButton.isChecked():
363 self.__generateUnifiedDiff(lines1, lines2, self.filename1, self.filename2, 372 self.__generateUnifiedDiff(
364 filemtime1, filemtime2) 373 lines1, lines2, self.filename1, self.filename2,
374 filemtime1, filemtime2)
365 else: 375 else:
366 self.__generateContextDiff(lines1, lines2, self.filename1, self.filename2, 376 self.__generateContextDiff(
367 filemtime1, filemtime2) 377 lines1, lines2, self.filename1, self.filename2,
378 filemtime1, filemtime2)
368 379
369 tc = self.contents.textCursor() 380 tc = self.contents.textCursor()
370 tc.movePosition(QTextCursor.Start) 381 tc.movePosition(QTextCursor.Start)
371 self.contents.setTextCursor(tc) 382 self.contents.setTextCursor(tc)
372 self.contents.ensureCursorVisible() 383 self.contents.ensureCursorVisible()
413 paras += 1 424 paras += 1
414 if not (paras % self.updateInterval): 425 if not (paras % self.updateInterval):
415 QApplication.processEvents() 426 QApplication.processEvents()
416 427
417 if paras == 0: 428 if paras == 0:
418 self.__appendText(self.trUtf8('There is no difference.'), self.cNormalFormat) 429 self.__appendText(
430 self.trUtf8('There is no difference.'), self.cNormalFormat)
419 431
420 def __generateContextDiff(self, a, b, fromfile, tofile, 432 def __generateContextDiff(self, a, b, fromfile, tofile,
421 fromfiledate, tofiledate): 433 fromfiledate, tofiledate):
422 """ 434 """
423 Private slot to generate a context diff output. 435 Private slot to generate a context diff output.
436 format = self.cAddedFormat 448 format = self.cAddedFormat
437 elif line.startswith('- '): 449 elif line.startswith('- '):
438 format = self.cRemovedFormat 450 format = self.cRemovedFormat
439 elif line.startswith('! '): 451 elif line.startswith('! '):
440 format = self.cReplacedFormat 452 format = self.cReplacedFormat
441 elif (line.startswith('*** ') or line.startswith('--- ')) and paras > 1: 453 elif (line.startswith('*** ') or line.startswith('--- ')) and \
454 paras > 1:
442 format = self.cLineNoFormat 455 format = self.cLineNoFormat
443 else: 456 else:
444 format = self.cNormalFormat 457 format = self.cNormalFormat
445 self.__appendText(line, format) 458 self.__appendText(line, format)
446 paras += 1 459 paras += 1
447 if not (paras % self.updateInterval): 460 if not (paras % self.updateInterval):
448 QApplication.processEvents() 461 QApplication.processEvents()
449 462
450 if paras == 0: 463 if paras == 0:
451 self.__appendText(self.trUtf8('There is no difference.'), self.cNormalFormat) 464 self.__appendText(
465 self.trUtf8('There is no difference.'), self.cNormalFormat)
452 466
453 def __fileChanged(self): 467 def __fileChanged(self):
454 """ 468 """
455 Private slot to enable/disable the Compare button. 469 Private slot to enable/disable the Compare button.
456 """ 470 """
501 515
502 @param parent reference to the parent widget (QWidget) 516 @param parent reference to the parent widget (QWidget)
503 """ 517 """
504 super(DiffWindow, self).__init__(parent) 518 super(DiffWindow, self).__init__(parent)
505 519
506 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) 520 self.setStyle(Preferences.getUI("Style"),
521 Preferences.getUI("StyleSheet"))
507 522
508 self.cw = DiffDialog(self) 523 self.cw = DiffDialog(self)
509 self.cw.installEventFilter(self) 524 self.cw.installEventFilter(self)
510 size = self.cw.size() 525 size = self.cw.size()
511 self.setCentralWidget(self.cw) 526 self.setCentralWidget(self.cw)

eric ide

mercurial