UI/DiffDialog.py

changeset 945
8cd4d08fa9f6
parent 882
34b86be88bf0
child 1112
8a7d1b9d18db
equal deleted inserted replaced
944:1b59c4ba121e 945:8cd4d08fa9f6
21 21
22 from difflib import SequenceMatcher 22 from difflib import SequenceMatcher
23 23
24 # This function is copied from python 2.3 and slightly modified. 24 # This function is copied from python 2.3 and slightly modified.
25 # The header lines contain a tab after the filename. 25 # The header lines contain a tab after the filename.
26
27
26 def unified_diff(a, b, fromfile='', tofile='', fromfiledate='', 28 def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
27 tofiledate='', n=3, lineterm='\n'): 29 tofiledate='', n=3, lineterm='\n'):
28 """ 30 """
29 Compare two sequences of lines; generate the delta as a unified diff. 31 Compare two sequences of lines; generate the delta as a unified diff.
30 32
74 @param n number of lines of context (integer) 76 @param n number of lines of context (integer)
75 @param lineterm line termination string (string) 77 @param lineterm line termination string (string)
76 @return a generator yielding lines of differences 78 @return a generator yielding lines of differences
77 """ 79 """
78 started = False 80 started = False
79 for group in SequenceMatcher(None,a,b).get_grouped_opcodes(n): 81 for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n):
80 if not started: 82 if not started:
81 yield '--- {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm) 83 yield '--- {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm)
82 yield '+++ {0}\t{1}{2}'.format(tofile, tofiledate, lineterm) 84 yield '+++ {0}\t{1}{2}'.format(tofile, tofiledate, lineterm)
83 started = True 85 started = True
84 i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] 86 i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4]
85 yield "@@ -{0:d},{1:d} +{2:d},{3:d} @@{4}".format( 87 yield "@@ -{0:d},{1:d} +{2:d},{3:d} @@{4}".format(
86 i1+1, i2-i1, j1+1, j2-j1, lineterm) 88 i1 + 1, i2 - i1, j1 + 1, j2 - j1, lineterm)
87 for tag, i1, i2, j1, j2 in group: 89 for tag, i1, i2, j1, j2 in group:
88 if tag == 'equal': 90 if tag == 'equal':
89 for line in a[i1:i2]: 91 for line in a[i1:i2]:
90 yield ' ' + line 92 yield ' ' + line
91 continue 93 continue
96 for line in b[j1:j2]: 98 for line in b[j1:j2]:
97 yield '+' + line 99 yield '+' + line
98 100
99 # This function is copied from python 2.3 and slightly modified. 101 # This function is copied from python 2.3 and slightly modified.
100 # The header lines contain a tab after the filename. 102 # The header lines contain a tab after the filename.
103
104
101 def context_diff(a, b, fromfile='', tofile='', 105 def context_diff(a, b, fromfile='', tofile='',
102 fromfiledate='', tofiledate='', n=3, lineterm='\n'): 106 fromfiledate='', tofiledate='', n=3, lineterm='\n'):
103 """ 107 """
104 Compare two sequences of lines; generate the delta as a context diff. 108 Compare two sequences of lines; generate the delta as a context diff.
105 109
153 @param lineterm line termination string (string) 157 @param lineterm line termination string (string)
154 @return a generator yielding lines of differences 158 @return a generator yielding lines of differences
155 """ 159 """
156 160
157 started = False 161 started = False
158 prefixmap = {'insert':'+ ', 'delete':'- ', 'replace':'! ', 'equal':' '} 162 prefixmap = {'insert': '+ ', 'delete': '- ', 'replace': '! ', 'equal': ' '}
159 for group in SequenceMatcher(None,a,b).get_grouped_opcodes(n): 163 for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n):
160 if not started: 164 if not started:
161 yield '*** {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm) 165 yield '*** {0}\t{1}{2}'.format(fromfile, fromfiledate, lineterm)
162 yield '--- {0}\t{1}{2}'.format(tofile, tofiledate, lineterm) 166 yield '--- {0}\t{1}{2}'.format(tofile, tofiledate, lineterm)
163 started = True 167 started = True
164 168
165 yield '***************{0}'.format(lineterm) 169 yield '***************{0}'.format(lineterm)
166 if group[-1][2] - group[0][1] >= 2: 170 if group[-1][2] - group[0][1] >= 2:
167 yield '*** {0:d},{1:d} ****{2}'.format(group[0][1]+1, group[-1][2], lineterm) 171 yield '*** {0:d},{1:d} ****{2}'.format(group[0][1] + 1, group[-1][2], lineterm)
168 else: 172 else:
169 yield '*** {0:d} ****{1}'.format(group[-1][2], lineterm) 173 yield '*** {0:d} ****{1}'.format(group[-1][2], lineterm)
170 visiblechanges = [e for e in group if e[0] in ('replace', 'delete')] 174 visiblechanges = [e for e in group if e[0] in ('replace', 'delete')]
171 if visiblechanges: 175 if visiblechanges:
172 for tag, i1, i2, _, _ in group: 176 for tag, i1, i2, _, _ in group:
173 if tag != 'insert': 177 if tag != 'insert':
174 for line in a[i1:i2]: 178 for line in a[i1:i2]:
175 yield prefixmap[tag] + line 179 yield prefixmap[tag] + line
176 180
177 if group[-1][4] - group[0][3] >= 2: 181 if group[-1][4] - group[0][3] >= 2:
178 yield '--- {0:d},{1:d} ----{2}'.format(group[0][3]+1, group[-1][4], lineterm) 182 yield '--- {0:d},{1:d} ----{2}'.format(group[0][3] + 1, group[-1][4], lineterm)
179 else: 183 else:
180 yield '--- {0:d} ----{1}'.format(group[-1][4], lineterm) 184 yield '--- {0:d} ----{1}'.format(group[-1][4], lineterm)
181 visiblechanges = [e for e in group if e[0] in ('replace', 'insert')] 185 visiblechanges = [e for e in group if e[0] in ('replace', 'insert')]
182 if visiblechanges: 186 if visiblechanges:
183 for tag, _, _, j1, j2 in group: 187 for tag, _, _, j1, j2 in group:
184 if tag != 'delete': 188 if tag != 'delete':
185 for line in b[j1:j2]: 189 for line in b[j1:j2]:
186 yield prefixmap[tag] + line 190 yield prefixmap[tag] + line
187 191
192
188 class DiffDialog(QWidget, Ui_DiffDialog): 193 class DiffDialog(QWidget, Ui_DiffDialog):
189 """ 194 """
190 Class implementing a dialog to compare two files. 195 Class implementing a dialog to compare two files.
191 """ 196 """
192 def __init__(self,parent = None): 197 def __init__(self, parent=None):
193 """ 198 """
194 Constructor 199 Constructor
195 """ 200 """
196 QWidget.__init__(self,parent) 201 QWidget.__init__(self, parent)
197 self.setupUi(self) 202 self.setupUi(self)
198 203
199 self.file1Completer = E5FileCompleter(self.file1Edit) 204 self.file1Completer = E5FileCompleter(self.file1Edit)
200 self.file2Completer = E5FileCompleter(self.file2Edit) 205 self.file2Completer = E5FileCompleter(self.file2Edit)
201 206
232 237
233 # connect some of our widgets explicitly 238 # connect some of our widgets explicitly
234 self.file1Edit.textChanged.connect(self.__fileChanged) 239 self.file1Edit.textChanged.connect(self.__fileChanged)
235 self.file2Edit.textChanged.connect(self.__fileChanged) 240 self.file2Edit.textChanged.connect(self.__fileChanged)
236 241
237 def show(self, filename = None): 242 def show(self, filename=None):
238 """ 243 """
239 Public slot to show the dialog. 244 Public slot to show the dialog.
240 245
241 @param filename name of a file to use as the first file (string) 246 @param filename name of a file to use as the first file (string)
242 """ 247 """
272 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( 277 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
273 self, 278 self,
274 self.trUtf8("Save Diff"), 279 self.trUtf8("Save Diff"),
275 fname, 280 fname,
276 self.trUtf8("Patch Files (*.diff)"), 281 self.trUtf8("Patch Files (*.diff)"),
277 None, 282 None,
278 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) 283 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
279 284
280 if not fname: 285 if not fname:
281 return 286 return
282 287
288 if QFileInfo(fname).exists(): 293 if QFileInfo(fname).exists():
289 res = E5MessageBox.yesNo(self, 294 res = E5MessageBox.yesNo(self,
290 self.trUtf8("Save Diff"), 295 self.trUtf8("Save Diff"),
291 self.trUtf8("<p>The patch file <b>{0}</b> already exists." 296 self.trUtf8("<p>The patch file <b>{0}</b> already exists."
292 " Overwrite it?</p>").format(fname), 297 " Overwrite it?</p>").format(fname),
293 icon = E5MessageBox.Warning) 298 icon=E5MessageBox.Warning)
294 if not res: 299 if not res:
295 return 300 return
296 fname = Utilities.toNativeSeparators(fname) 301 fname = Utilities.toNativeSeparators(fname)
297 302
298 try: 303 try:
299 f = open(fname, "w", encoding = "utf-8") 304 f = open(fname, "w", encoding="utf-8")
300 txt = self.contents.toPlainText() 305 txt = self.contents.toPlainText()
301 try: 306 try:
302 f.write(txt) 307 f.write(txt)
303 except UnicodeError: 308 except UnicodeError:
304 pass 309 pass
318 try: 323 try:
319 filemtime1 = time.ctime(os.stat(self.filename1).st_mtime) 324 filemtime1 = time.ctime(os.stat(self.filename1).st_mtime)
320 except IOError: 325 except IOError:
321 filemtime1 = "" 326 filemtime1 = ""
322 try: 327 try:
323 f1 = open(self.filename1, "r", encoding = "utf-8") 328 f1 = open(self.filename1, "r", encoding="utf-8")
324 lines1 = f1.readlines() 329 lines1 = f1.readlines()
325 f1.close() 330 f1.close()
326 except IOError: 331 except IOError:
327 E5MessageBox.critical(self, 332 E5MessageBox.critical(self,
328 self.trUtf8("Compare Files"), 333 self.trUtf8("Compare Files"),
334 try: 339 try:
335 filemtime2 = time.ctime(os.stat(self.filename2).st_mtime) 340 filemtime2 = time.ctime(os.stat(self.filename2).st_mtime)
336 except IOError: 341 except IOError:
337 filemtime2 = "" 342 filemtime2 = ""
338 try: 343 try:
339 f2 = open(self.filename2, "r", encoding = "utf-8") 344 f2 = open(self.filename2, "r", encoding="utf-8")
340 lines2 = f2.readlines() 345 lines2 = f2.readlines()
341 f2.close() 346 f2.close()
342 except IOError: 347 except IOError:
343 E5MessageBox.critical(self, 348 E5MessageBox.critical(self,
344 self.trUtf8("Compare Files"), 349 self.trUtf8("Compare Files"),
478 """ 483 """
479 Private slot to handle the file 2 file selection button press. 484 Private slot to handle the file 2 file selection button press.
480 """ 485 """
481 self.__selectFile(self.file2Edit) 486 self.__selectFile(self.file2Edit)
482 487
488
483 class DiffWindow(QMainWindow): 489 class DiffWindow(QMainWindow):
484 """ 490 """
485 Main window class for the standalone dialog. 491 Main window class for the standalone dialog.
486 """ 492 """
487 def __init__(self, parent = None): 493 def __init__(self, parent=None):
488 """ 494 """
489 Constructor 495 Constructor
490 496
491 @param parent reference to the parent widget (QWidget) 497 @param parent reference to the parent widget (QWidget)
492 """ 498 """

eric ide

mercurial