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 |
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 |