3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # Transform a unified diff from stdin to a colored
19 # side-by-side HTML page on stdout.
21 # Author: Olivier MATZ <zer0@droids-corp.org>
23 # Inspired by diff2html.rb from Dave Burt <dave (at) burt.id.au>
24 # (mainly for html theme)
27 import sys, re, htmlentitydefs
29 html_hdr="""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
31 <meta name="generator" content="diff2html.rb" />
32 <title>HTML Diff</title>
34 table { border:0px; border-collapse:collapse; width: 100%; font-size:0.75em; font-family: Lucida Console, monospace }
35 td.line { color:#8080a0 }
36 th { background: black; color: white }
37 tr.unmodified td { background: #D0D0E0 }
38 tr.hunk td { background: #A0A0A0 }
39 tr.added td { background: #CCFFCC }
40 tr.deleted td { background: #FFCCCC }
41 tr.changed td { background: #FFFFA0 }
42 span.changed2 { background: #E0C880 }
43 span.ponct { color: #B08080 }
53 </table></body></html>
60 add_cpt, del_cpt = 0,0
62 hunk_off1, hunk_size1, hunk_off2, hunk_size2 = 0,0,0,0
64 # minimum line size, we add a zero-sized breakable space every
73 if i not in ['\t', '\n'] and ((j < 32) or (j >= 127)):
81 s=str(reduce(lambda x,y:x+y, [ sane(c) for c in s ]))
83 t=str(reduce(lambda x,y:x+y, [ sane(c) for c in t ]))
86 d=[[(0,0) for i in range(n+1)] for i in range(m+1)]
87 x=[[(0,0) for i in range(n+1)] for i in range(m+1)]
91 for i in range(m+1)[1:]:
93 for j in range(n+1)[1:]:
96 for i in range(m+1)[1:]:
97 for j in range(n+1)[1:]:
102 d[i][j] = min((d[i-1][j][0] + 1, (i-1,j)),
103 (d[i][j-1][0] + 1, (i,j-1)),
104 (d[i-1][j-1][0] + cost, (i-1,j-1)))
108 while coord != (0,0):
118 child_val = d[cx][cy][0]
120 father_coord = d[cx][cy][1]
122 father_val = d[fx][fy][0]
124 diff = (cx-fx, cy-fy)
128 l2.append(DIFFON + t[fy] + DIFFOFF)
130 l1.append(DIFFON + s[fx] + DIFFOFF)
132 elif child_val-father_val == 1:
133 l1.append(DIFFON + s[fx] + DIFFOFF)
134 l2.append(DIFFON + t[fy] + DIFFOFF)
139 r1,r2 = (reduce(lambda x,y:x+y, l1), reduce(lambda x,y:x+y, l2))
143 def convert(s, linesize=0, ponct=0):
150 t += '<spanclass="changed2">'
155 elif htmlentitydefs.codepoint2name.has_key(ord(c)):
156 t += "&%s;"%(htmlentitydefs.codepoint2name[ord(c)])
159 # special highlighted chars
160 elif c=="\t" and ponct==1:
164 t += ('<spanclass="ponct">»</span>'+' '*(n-1))
166 elif c=="\n" and ponct==1:
167 t += '<spanclass="ponct">\</span>'
171 if linesize and i>linesize:
176 t = t.replace(' ', '<spanclass="ponct">·</span>')
177 t = t.replace("spanclass", "span class")
183 sys.stdout.write('<tr class="misc"><td colspan="4">%s</td></tr>\n'%convert(s))
185 def add_filename(f1, f2):
186 sys.stdout.write("<tr><th colspan='2'>%s</th>"%convert(f1, linesize=LINESIZE))
187 sys.stdout.write("<th colspan='2'>%s</th></tr>\n"%convert(f2, linesize=LINESIZE))
194 sys.stdout.write('<tr class="hunk"><td colspan="2">Offset %d, %d lines modified</td>'%(hunk_off1, hunk_size1))
195 sys.stdout.write('<td colspan="2">Offset %d, %d lines modified</td></tr>\n'%(hunk_off2, hunk_size2))
197 def add_line(s1, s2):
201 if s1==None and s2==None:
211 s1,s2 = linediff(s1, s2)
213 sys.stdout.write('<tr class="%s">'%type)
214 if s1!=None and s1!="":
215 sys.stdout.write('<td class="line">%d </td>'%line1)
216 sys.stdout.write('<td>')
217 sys.stdout.write(convert(s1, linesize=LINESIZE, ponct=1))
218 sys.stdout.write('</td>')
221 sys.stdout.write('<td colspan="2"> </td>')
223 if s2!=None and s2!="":
224 sys.stdout.write('<td class="line">%d </td>'%line2)
225 sys.stdout.write('<td>')
226 sys.stdout.write(convert(s2, linesize=LINESIZE, ponct=1))
227 sys.stdout.write('</td>')
230 sys.stdout.write('<td colspan="2"></td>')
232 sys.stdout.write('</tr>\n')
245 if del_cpt == 0 or add_cpt == 0:
249 elif del_cpt != 0 and add_cpt != 0:
256 max = (len(l0) > len(l1)) and len(l0) or len(l1)
265 add_cpt, del_cpt = 0,0
270 sys.stdout.write(html_hdr)
273 l=sys.stdin.readline()
277 m=re.match('^--- ([^\s]*)', l)
281 l=sys.stdin.readline()
282 m=re.match('^\+\+\+ ([^\s]*)', l)
285 add_filename(file1, file2)
286 hunk_off1, hunk_size1, hunk_off2, hunk_size2 = 0,0,0,0
289 m=re.match("@@ -(\d+),?(\d*) \+(\d+),?(\d*)", l)
292 hunk_data = map(lambda x:x=="" and 1 or int(x), m.groups())
293 hunk_off1, hunk_size1, hunk_off2, hunk_size2 = hunk_data
294 line1, line2 = hunk_off1, hunk_off2
298 if hunk_size1 == 0 and hunk_size2 == 0:
303 if re.match("^\+", l):
306 buffer.append((None, l[1:]))
309 if re.match("^\-", l):
312 buffer.append((l[1:], None))
315 if re.match("^\ ", l) and hunk_size1 and hunk_size2:
319 buffer.append((l[1:], l[1:]))
327 sys.stdout.write(html_footer)