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)
26 # Changed by Alan De Smet <adesmet@cs.wisc.edu> 2009-01-25
27 # - Remove headers and footers that are undesirable for for CVSTrac
29 # - Change style sheet to own preferences
30 # - Adjust LINESIZE code to reset counter and allow breaks whenever
31 # WORDBREAK characters are encountered.
32 # - Don't display offset info (it's redundant with the line numbers)
33 # instead show vertical ellipsis
34 # - If part of a "change" is actually blank, it's an addition or
35 # deletion, not a "change" where the entire line changed.
36 # - Don't display "\" for end of line; unnecessary noise.
39 # - The sane function currently mashes non-ASCII characters to "."
40 # Instead be clever and convert to something like "xF0"
41 # (the hex value), and mark with a <span>. Even more clever:
42 # Detect if the character is "printable" for whatever definition,
43 # and display those directly.
46 import sys, re, htmlentitydefs
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
69 # Characters we're willing to word wrap on
76 if i not in ['\t', '\n'] and ((j < 32) or (j >= 127)):
84 s=str(reduce(lambda x,y:x+y, [ sane(c) for c in s ]))
86 t=str(reduce(lambda x,y:x+y, [ sane(c) for c in t ]))
89 d=[[(0,0) for i in range(n+1)] for i in range(m+1)]
90 x=[[(0,0) for i in range(n+1)] for i in range(m+1)]
94 for i in range(m+1)[1:]:
96 for j in range(n+1)[1:]:
99 for i in range(m+1)[1:]:
100 for j in range(n+1)[1:]:
105 d[i][j] = min((d[i-1][j][0] + 1, (i-1,j)),
106 (d[i][j-1][0] + 1, (i,j-1)),
107 (d[i-1][j-1][0] + cost, (i-1,j-1)))
111 while coord != (0,0):
121 child_val = d[cx][cy][0]
123 father_coord = d[cx][cy][1]
125 father_val = d[fx][fy][0]
127 diff = (cx-fx, cy-fy)
131 l2.append(DIFFON + t[fy] + DIFFOFF)
133 l1.append(DIFFON + s[fx] + DIFFOFF)
135 elif child_val-father_val == 1:
136 l1.append(DIFFON + s[fx] + DIFFOFF)
137 l2.append(DIFFON + t[fy] + DIFFOFF)
142 r1,r2 = (reduce(lambda x,y:x+y, l1), reduce(lambda x,y:x+y, l2))
146 def convert(s, linesize=0, ponct=0):
153 t += '<span class="diffchanged2">'
158 elif htmlentitydefs.codepoint2name.has_key(ord(c)):
159 t += "&%s;"%(htmlentitydefs.codepoint2name[ord(c)])
162 # special highlighted chars
163 elif c=="\t" and ponct==1:
167 t += ('<span class="diffponct">»</span>'+' '*(n-1))
168 elif c==" " and ponct==1:
169 t += '<span class="diffponct">·</span>'
170 elif c=="\n" and ponct==1:
172 #t += '<span class="diffponct">\</span>'
177 if linesize and (WORDBREAK.count(c)==1):
180 if linesize and i>linesize:
188 sys.stdout.write('<tr class="diffmisc"><td colspan="4">%s</td></tr>\n'%convert(s))
190 def add_filename(f1, f2):
191 sys.stdout.write("<tr><th colspan='2'>%s</th>"%convert(f1, linesize=LINESIZE))
192 sys.stdout.write("<th colspan='2'>%s</th></tr>\n"%convert(f2, linesize=LINESIZE))
199 # Don't bother displaying, it's redundant with the line numbers.
200 #sys.stdout.write('<tr class="diffhunk"><td colspan="2">Offset %d, %d lines modified</td>'%(hunk_off1, hunk_size1))
201 #sys.stdout.write('<td colspan="2">Offset %d, %d lines modified</td></tr>\n'%(hunk_off2, hunk_size2))
202 # ⋮ - vertical ellipsis
203 sys.stdout.write('<tr class="diffhunk"><td colspan="2">⋮</td><td colspan="2">⋮</td></tr>');
206 def add_line(s1, s2):
210 if s1==None and s2==None:
212 elif s1==None or s1=="":
214 elif s2==None or s1=="":
220 s1,s2 = linediff(s1, s2)
222 sys.stdout.write('<tr class="diff%s">'%type)
223 if s1!=None and s1!="":
224 sys.stdout.write('<td class="diffline">%d </td>'%line1)
225 sys.stdout.write('<td class="diffpresent">')
226 sys.stdout.write(convert(s1, linesize=LINESIZE, ponct=1))
227 sys.stdout.write('</td>')
230 sys.stdout.write('<td colspan="2"> </td>')
232 if s2!=None and s2!="":
233 sys.stdout.write('<td class="diffline">%d </td>'%line2)
234 sys.stdout.write('<td class="diffpresent">')
235 sys.stdout.write(convert(s2, linesize=LINESIZE, ponct=1))
236 sys.stdout.write('</td>')
239 sys.stdout.write('<td colspan="2"></td>')
241 sys.stdout.write('</tr>\n')
254 if del_cpt == 0 or add_cpt == 0:
258 elif del_cpt != 0 and add_cpt != 0:
265 max = (len(l0) > len(l1)) and len(l0) or len(l1)
274 add_cpt, del_cpt = 0,0
279 sys.stdout.write(html_hdr)
282 l=sys.stdin.readline()
286 m=re.match('^--- ([^\s]*)', l)
290 l=sys.stdin.readline()
291 m=re.match('^\+\+\+ ([^\s]*)', l)
294 add_filename(file1, file2)
295 hunk_off1, hunk_size1, hunk_off2, hunk_size2 = 0,0,0,0
298 m=re.match("@@ -(\d+),?(\d*) \+(\d+),?(\d*)", l)
301 hunk_data = map(lambda x:x=="" and 1 or int(x), m.groups())
302 hunk_off1, hunk_size1, hunk_off2, hunk_size2 = hunk_data
303 line1, line2 = hunk_off1, hunk_off2
307 if hunk_size1 == 0 and hunk_size2 == 0:
312 if re.match("^\+", l):
315 buffer.append((None, l[1:]))
318 if re.match("^\-", l):
321 buffer.append((l[1:], None))
324 if re.match("^\ ", l) and hunk_size1 and hunk_size2:
328 buffer.append((l[1:], l[1:]))
336 sys.stdout.write(html_footer)