| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Utility for outputting a HTML diff of two multi-line strings. | 5 """Utility for outputting a HTML diff of two multi-line strings. |
| 6 | 6 |
| 7 The main purpose of this utility is to show the difference between | 7 The main purpose of this utility is to show the difference between |
| 8 text baselines (-expected.txt files) and actual text results. | 8 text baselines (-expected.txt files) and actual text results. |
| 9 | 9 |
| 10 Note, in the standard library module difflib, there is also a HtmlDiff class, | 10 Note, in the standard library module difflib, there is also a HtmlDiff class, |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 | 43 |
| 44 def __init__(self): | 44 def __init__(self): |
| 45 self.a_line_no = None | 45 self.a_line_no = None |
| 46 self.b_line_no = None | 46 self.b_line_no = None |
| 47 self.a_lines_len = None | 47 self.a_lines_len = None |
| 48 | 48 |
| 49 def generate_tbody(self, a_lines, b_lines): | 49 def generate_tbody(self, a_lines, b_lines): |
| 50 self.a_line_no = 0 | 50 self.a_line_no = 0 |
| 51 self.b_line_no = 0 | 51 self.b_line_no = 0 |
| 52 self.a_lines_len = len(a_lines) | 52 self.a_lines_len = len(a_lines) |
| 53 self.b_lines_len = len(b_lines) |
| 53 matcher = difflib.SequenceMatcher(None, a_lines, b_lines) | 54 matcher = difflib.SequenceMatcher(None, a_lines, b_lines) |
| 54 output = [] | 55 output = [] |
| 55 for tag, a_start, a_end, b_start, b_end in matcher.get_opcodes(): | 56 for tag, a_start, a_end, b_start, b_end in matcher.get_opcodes(): |
| 56 output.append(self._format_chunk(tag, a_lines[a_start:a_end], b_line
s[b_start:b_end])) | 57 output.append(self._format_chunk(tag, a_lines[a_start:a_end], b_line
s[b_start:b_end])) |
| 57 return ''.join(output) | 58 return ''.join(output) |
| 58 | 59 |
| 59 def _format_chunk(self, tag, a_chunk, b_chunk): | 60 def _format_chunk(self, tag, a_chunk, b_chunk): |
| 60 if tag == 'delete': | 61 if tag == 'delete': |
| 61 return self._format_delete(a_chunk) | 62 return self._format_delete(a_chunk) |
| 62 if tag == 'insert': | 63 if tag == 'insert': |
| 63 return self._format_insert(b_chunk) | 64 return self._format_insert(b_chunk) |
| 64 if tag == 'replace': | 65 if tag == 'replace': |
| 65 return self._format_delete(a_chunk) + self._format_insert(b_chunk) | 66 return self._format_delete(a_chunk) + self._format_insert(b_chunk) |
| 66 assert tag == 'equal' | 67 assert tag == 'equal' |
| 67 return self._format_equal(a_chunk) | 68 return self._format_equal(a_chunk) |
| 68 | 69 |
| 69 def _format_equal(self, common_chunk): | 70 def _format_equal(self, common_chunk): |
| 70 output = '' | 71 output = '' |
| 71 if len(common_chunk) <= 7: | 72 if len(common_chunk) <= 7: |
| 72 for line in common_chunk: | 73 for line in common_chunk: |
| 73 output += self._format_equal_line(line) | 74 output += self._format_equal_line(line) |
| 74 else: | 75 else: |
| 75 # Do not show context lines at the beginning of the file. | 76 # Do not show context lines at the beginning of the file. |
| 76 if self.a_line_no == 0: | 77 if self.a_line_no == 0 and self.b_line_no == 0: |
| 77 self.a_line_no += 3 | 78 self.a_line_no += 3 |
| 78 self.b_line_no += 3 | 79 self.b_line_no += 3 |
| 79 else: | 80 else: |
| 80 for line in common_chunk[0:3]: | 81 for line in common_chunk[0:3]: |
| 81 output += self._format_equal_line(line) | 82 output += self._format_equal_line(line) |
| 82 self.a_line_no += len(common_chunk) - 6 | 83 self.a_line_no += len(common_chunk) - 6 |
| 83 self.b_line_no += len(common_chunk) - 6 | 84 self.b_line_no += len(common_chunk) - 6 |
| 84 output += '<tr><td colspan=3>\n\n</tr>' | 85 output += '<tr><td colspan=3>\n\n</tr>' |
| 85 # Do not show context lines at the end of the file. | 86 # Do not show context lines at the end of the file. |
| 86 if self.a_line_no + 3 != self.a_lines_len: | 87 if self.a_line_no + 3 != self.a_lines_len or self.b_line_no + 3 != s
elf.b_lines_len: |
| 87 for line in common_chunk[len(common_chunk) - 3:len(common_chunk)
]: | 88 for line in common_chunk[len(common_chunk) - 3:len(common_chunk)
]: |
| 88 output += self._format_equal_line(line) | 89 output += self._format_equal_line(line) |
| 89 return output | 90 return output |
| 90 | 91 |
| 91 def _format_equal_line(self, line): | 92 def _format_equal_line(self, line): |
| 92 self.a_line_no += 1 | 93 self.a_line_no += 1 |
| 93 self.b_line_no += 1 | 94 self.b_line_no += 1 |
| 94 return '<tr><th>%d<th>%d<td>%s</tr>' % (self.a_line_no, self.b_line_no,
cgi.escape(line)) | 95 return '<tr><th>%d<th>%d<td>%s</tr>' % (self.a_line_no, self.b_line_no,
cgi.escape(line)) |
| 95 | 96 |
| 96 def _format_insert(self, chunk): | 97 def _format_insert(self, chunk): |
| 97 output = '' | 98 output = '' |
| 98 for line in chunk: | 99 for line in chunk: |
| 99 self.b_line_no += 1 | 100 self.b_line_no += 1 |
| 100 output += '<tr><th><th>%d<td class="add">%s</tr>' % (self.b_line_no,
cgi.escape(line)) | 101 output += '<tr><th><th>%d<td class="add">%s</tr>' % (self.b_line_no,
cgi.escape(line)) |
| 101 return output | 102 return output |
| 102 | 103 |
| 103 def _format_delete(self, chunk): | 104 def _format_delete(self, chunk): |
| 104 output = '' | 105 output = '' |
| 105 for line in chunk: | 106 for line in chunk: |
| 106 self.a_line_no += 1 | 107 self.a_line_no += 1 |
| 107 output += '<tr><th>%d<th><td class="del">%s</tr>' % (self.a_line_no,
cgi.escape(line)) | 108 output += '<tr><th>%d<th><td class="del">%s</tr>' % (self.a_line_no,
cgi.escape(line)) |
| 108 return output | 109 return output |
| OLD | NEW |