Skip to content

Commit 7b02333

Browse files
author
cmlenz
committed
* Implemented difference highlighting within lines. Closes #434.
* Simplified source:trunk/trac/Diff.py by using {{{util.add_to_hdf()}}} git-svn-id: http://trac.edgewall.org/intertrac/log:/trunk@906 af82e41b-90c4-0310-8c96-b1721e28e2e2
1 parent e853079 commit 7b02333

File tree

3 files changed

+64
-38
lines changed

3 files changed

+64
-38
lines changed

htdocs/css/diff.css

+5
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@
139139
.diff table.inline tbody.add tr.first td.chg { border-top-width: 1px }
140140
.diff table.inline tbody.mod tr.last td.chg,
141141
.diff table.inline tbody.add tr.last td.chg { border-bottom-width: 1px }
142+
.diff table.inline tbody.mod td del { background: #e99; color: #000 }
143+
.diff table.inline tbody.mod td ins { background: #9e9; color: #000 }
142144

143145
/* Styles for the side-by-side diff */
144146
.diff table.sidebyside colgroup.content { width: 50% }
@@ -148,3 +150,6 @@
148150
.diff table.sidebyside tbody.add td.chg { background: #cfc }
149151
.diff table.sidebyside tbody.rem td.base { background: #f88 }
150152
.diff table.sidebyside tbody.rem td.chg { background: #faa }
153+
.diff table.sidebyside tbody.mod del, .diff table.sidebyside tbody.mod ins {
154+
background: #fc0;
155+
}

templates/macros.cs

+28-28
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,22 @@
3636
each:block = change.blocks ?><?cs
3737
if:block.type == 'unmod' ?><tbody class="unmod"><?cs
3838
each:line = block.base.lines ?><tr>
39-
<th class="base"><?cs var:#block.base.offset + name(line) ?></th>
39+
<th class="base"><?cs var:#block.base.offset + name(line) + 1 ?></th>
4040
<td class="base"><span><?cs var:line ?></span></td>
41-
<th class="chg"><?cs var:#block.changed.offset + name(line) ?></th>
41+
<th class="chg"><?cs var:#block.changed.offset + name(line) + 1 ?></th>
4242
<td class="chg"><span><?cs var:line ?></span></td>
4343
</tr><?cs /each ?>
4444
</tbody><?cs
4545
elif:block.type == 'mod' ?><tbody class="mod"><?cs
4646
if:len(block.base.lines) >= len(block.changed.lines) ?><?cs
4747
each:line = block.base.lines ?><tr>
48-
<th class="base"><?cs var:#block.base.offset + name(line) ?></th>
49-
<td class="base"><del><?cs var:line ?></del></td><?cs
50-
if:len(block.changed.lines) >= name(line) ?><?cs
48+
<th class="base"><?cs var:#block.base.offset + name(line) + 1 ?></th>
49+
<td class="base"><?cs var:line ?></td><?cs
50+
if:len(block.changed.lines) >= name(line) + 1 ?><?cs
5151
each:changedline = block.changed.lines ?><?cs
5252
if:name(changedline) == name(line) ?>
53-
<th class="chg"><?cs var:#block.changed.offset + name(changedline) ?></th>
54-
<td class="chg"><ins><?cs var:changedline ?></ins></td><?cs
53+
<th class="chg"><?cs var:#block.changed.offset + name(changedline) + 1 ?></th>
54+
<td class="chg"><?cs var:changedline ?></td><?cs
5555
/if ?><?cs
5656
/each ?><?cs
5757
else ?>
@@ -61,32 +61,32 @@
6161
</tr><?cs /each ?><?cs
6262
else ?><?cs
6363
each:line = block.changed.lines ?><tr><?cs
64-
if:len(block.base.lines) >= name(line) ?><?cs
64+
if:len(block.base.lines) >= name(line) + 1 ?><?cs
6565
each:baseline = block.base.lines ?><?cs
6666
if:name(baseline) == name(line) ?>
67-
<th class="base"><?cs var:#block.base.offset + name(baseline) ?></th>
68-
<td class="base"><del><?cs var:baseline ?></del></td><?cs
67+
<th class="base"><?cs var:#block.base.offset + name(baseline) + 1 ?></th>
68+
<td class="base"><?cs var:baseline ?></td><?cs
6969
/if ?><?cs
7070
/each ?><?cs
7171
else ?>
7272
<th class="base">&nbsp;</th>
7373
<td class="base">&nbsp;</td><?cs
7474
/if ?>
75-
<th class="chg"><?cs var:#block.changed.offset + name(line) ?></th>
76-
<td class="chg"><ins><?cs var:line ?></ins></td>
75+
<th class="chg"><?cs var:#block.changed.offset + name(line) + 1 ?></th>
76+
<td class="chg"><?cs var:line ?></td>
7777
</tr><?cs /each ?><?cs
7878
/if ?>
7979
</tbody><?cs
8080
elif:block.type == 'add' ?><tbody class="add"><?cs
8181
each:line = block.changed.lines ?><tr>
8282
<th class="base">&nbsp;</th>
8383
<td class="base">&nbsp;</td>
84-
<th class="chg"><?cs var:#block.changed.offset + name(line) ?></th>
84+
<th class="chg"><?cs var:#block.changed.offset + name(line) + 1 ?></th>
8585
<td class="chg"><ins><?cs var:line ?></ins></td>
8686
</tr><?cs /each ?><?cs
8787
elif:block.type == 'rem' ?><tbody class="rem"><?cs
8888
each:line = block.base.lines ?><tr>
89-
<th class="base"><?cs var:#block.base.offset + name(line) ?></th>
89+
<th class="base"><?cs var:#block.base.offset + name(line) + 1 ?></th>
9090
<td class="base"><del><?cs var:line ?></del></td>
9191
<th class="chg">&nbsp;</th>
9292
<td class="chg">&nbsp;</td>
@@ -98,39 +98,39 @@
9898
each:block = change.blocks ?>
9999
<?cs if:block.type == 'unmod' ?><tbody class="unmod"><?cs
100100
each:line = block.base.lines ?><tr>
101-
<th class="base"><?cs var:#block.base.offset + name(line) ?></th>
102-
<th class="chg"><?cs var:#block.changed.offset + name(line) ?></th>
101+
<th class="base"><?cs var:#block.base.offset + name(line) + #1 ?></th>
102+
<th class="chg"><?cs var:#block.changed.offset + name(line) + #1 ?></th>
103103
<td class="base"><span><?cs var:line ?></span></td>
104104
</tr><?cs /each ?>
105105
</tbody>
106106
<?cs elif:block.type == 'mod' ?><tbody class="mod"><?cs
107107
each:line = block.base.lines ?><tr class="<?cs
108-
if:name(line) == 1 ?> first<?cs /if ?>">
109-
<th class="base"><?cs var:#block.base.offset + name(line) ?></th>
108+
if:name(line) == 0 ?>first<?cs /if ?>">
109+
<th class="base"><?cs var:#block.base.offset + name(line) + #1 ?></th>
110110
<th class="chg">&nbsp;</th>
111-
<td class="base"><del><?cs var:line ?></del></td>
111+
<td class="base"><?cs var:line ?></td>
112112
</tr><?cs /each ?><?cs
113113
each:line = block.changed.lines ?><tr class="<?cs
114-
if:name(line) == len(block.changed.lines) ?> last<?cs /if ?>">
114+
if:name(line) + 1 == len(block.changed.lines) ?> last<?cs /if ?>">
115115
<th class="base">&nbsp;</th>
116-
<th class="chg"><?cs var:#block.changed.offset + name(line) ?></th>
117-
<td class="chg"><ins><?cs var:line ?></ins></td>
116+
<th class="chg"><?cs var:#block.changed.offset + name(line) + #1 ?></th>
117+
<td class="chg"><?cs var:line ?></td>
118118
</tr><?cs /each ?>
119119
</tbody>
120120
<?cs elif:block.type == 'add' ?><tbody class="add"><?cs
121121
each:line = block.changed.lines ?><tr class="<?cs
122-
if:name(line) == 1 ?> first<?cs /if ?><?cs
123-
if:name(line) == len(block.changed.lines) ?> last ?><?cs /if ?>">
122+
if:name(line) == 0 ?>first<?cs /if ?><?cs
123+
if:name(line) + 1 == len(block.changed.lines) ?> last ?><?cs /if ?>">
124124
<th class="base">&nbsp;</th>
125-
<th class="chg"><?cs var:#block.changed.offset + name(line) ?></th>
125+
<th class="chg"><?cs var:#block.changed.offset + name(line) + #1 ?></th>
126126
<td class="chg"><ins><?cs var:line ?></ins></td>
127127
</tr><?cs /each ?>
128128
</tbody>
129129
<?cs elif:block.type == 'rem' ?><tbody class="rem"><?cs
130130
each:line = block.base.lines ?><tr class="<?cs
131-
if:name(line) == 1 ?> first<?cs /if ?><?cs
132-
if:name(line) == len(block.base.lines) ?> last ?><?cs /if ?>">
133-
<th class="base"><?cs var:#block.base.offset + name(line) ?></th>
131+
if:name(line) == 0 ?>first<?cs /if ?><?cs
132+
if:name(line) + 1 == len(block.base.lines) ?> last ?><?cs /if ?>">
133+
<th class="base"><?cs var:#block.base.offset + name(line) + 1 ?></th>
134134
<th class="chg">&nbsp;</th>
135135
<td class="base"><del><?cs var:line ?></del></td>
136136
</tr><?cs /each ?>

trac/Diff.py

+31-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
# Author: Jonas Borgström <[email protected]>
2121

2222
import re
23+
from difflib import SequenceMatcher
24+
from StringIO import StringIO
25+
from util import add_to_hdf
2326

2427
line_re = re.compile('@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@')
2528
header_re = re.compile('header ([^\|]+) ([^\|]+) \| ([^\|]+) ([^\|]+) redaeh')
@@ -38,24 +41,42 @@ def __init__(self, hdf, prefix, tabwidth=8):
3841
self.blockno = 0
3942
self.offset_base = 0
4043
self.offset_changed = 0
44+
self.matcher = SequenceMatcher()
45+
46+
def _write_line (self, prefix, oldline, newline):
47+
oldval = StringIO()
48+
newval = StringIO()
49+
self.matcher.set_seq1(oldline)
50+
self.matcher.set_seq2(newline)
51+
for tag, i1, i2, j1, j2 in self.matcher.get_opcodes():
52+
if tag == 'equal':
53+
oldval.write(oldline[i1:i2])
54+
newval.write(oldline[i1:i2])
55+
elif tag == 'delete':
56+
oldval.write('<del>%s</del>' % oldline[i1:i2])
57+
elif tag == 'insert':
58+
newval.write('<ins>%s</ins>' % newline[j1:j2])
59+
elif tag == 'replace':
60+
oldval.write('<del>%s</del>' % oldline[i1:i2])
61+
newval.write('<ins>%s</ins>' % newline[j1:j2])
62+
self.hdf.setValue(prefix + '.base.lines.0', oldval.getvalue())
63+
self.hdf.setValue(prefix + '.changed.lines.0', newval.getvalue())
4164

4265
def _write_block (self, prefix, dtype, old = None, new = None):
4366
self.hdf.setValue(prefix + '.type', dtype);
4467
self.hdf.setValue(prefix + '.base.offset', str(self.offset_base))
4568
self.hdf.setValue(prefix + '.changed.offset',
4669
str(self.offset_changed))
70+
if dtype == 'mod' and len(old) == len(new) == 1:
71+
self._write_line (prefix, old[0], new[0])
72+
return
73+
4774
if old:
48-
lineno = 1
49-
for line in old:
50-
self.hdf.setValue(prefix + '.base.lines.%d' % lineno, line)
51-
lineno += 1
52-
self.offset_base += lineno - 1
75+
add_to_hdf(old, self.hdf, prefix + '.base.lines')
76+
self.offset_base += len(old)
5377
if new:
54-
lineno = 1
55-
for line in new:
56-
self.hdf.setValue(prefix + '.changed.lines.%d' % lineno, line)
57-
lineno += 1
58-
self.offset_changed += lineno - 1
78+
add_to_hdf(new, self.hdf, prefix + '.changed.lines')
79+
self.offset_changed += len(new)
5980

6081
def print_block (self):
6182
prefix = '%s.changes.%d.blocks.%d' % (self.prefix, self.changeno,

0 commit comments

Comments
 (0)