1#
2# Copyright 2009, Alexandre Deckner, alex@zappotek.com
3# Distributed under the terms of the MIT License.
4#
5from cgi import escape
6
7
8# prints match to stdout
9def printMatch(name, match, source):
10    start = match.start()
11    end = match.end()
12    startLine = source.count('\n', 0, start)
13    startColumn = start - source.rfind('\n', 0, start)
14    print name + " (line " + str(startLine + 1) + ", " + str(startColumn) \
15        + "): '" + match.group().replace('\n','\\n') + "'"
16
17
18def openHtml(fileList, outputFileName):
19    file = open(outputFileName, 'w')
20    file.write("""
21    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
22        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
23    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
24    <head>
25        <title>Style violations</title>
26        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
27        <style type="text/css">""" + cssStyle() + """</style>
28    </head>
29    <body>
30        <p><b>File list:</b><br>""")
31    for fileName in fileList:
32        file.write(fileName + "<br>")
33    file.write("</p>")
34    file.close()
35
36
37def closeHtml(outputFileName):
38    file = open(outputFileName, 'a')
39    file.write("""
40        </pre>
41    </body>
42    </html>""")
43
44    file.close()
45
46
47# render in html
48def renderHtml(text, highlights, sourceFileName, outputFileName):
49    splittedText = highlightSplit(text, highlights)
50
51    file = open(outputFileName, 'a')
52    file.write("<hr/><p><b>" + sourceFileName + "</b></p>")
53
54    # insert highlight tags in a temp buffer
55    temp = ""
56    count = 0
57    for slice in splittedText:
58        if count % 2 == 0:
59            temp += escape(slice) + '<span class="highlight tooltip">'
60        else:
61            temp += escape(slice) + "<em>" + highlights[(count - 1) / 2][2] \
62                + "</em></span>"
63        count += 1
64
65    temp += "</span>" # close the superfluous last highlight
66
67    file.write('<table><tr><td><pre class="code"><span class="linenumber">')
68    count = 1
69    for line in temp.split('\n'):
70        file.write(str(count).rjust(4)+"<br>")
71        count += 1
72
73    file.write('</span></pre></td><td><pre class="code">')
74
75    for line in temp.split('\n'):
76        file.write('<span class="linehead"> </span>' + line.replace('\r', ' ') \
77             + '<br>')
78
79    file.write("</pre></td></tr></table>")
80
81    file.close()
82
83
84# highlight overlap check
85def highlightOverlaps(highlight1, highlight2):
86    #print "hl1", highlight1, "hl2", highlight2
87    return not(highlight2[0] > highlight1[1] or highlight1[0] > highlight2[1])
88
89
90# splits the string in three parts before, between and after the highlight
91def splitByHighlight(string, highlight):
92    return (string[:highlight[0]], string[highlight[0]:highlight[1]], \
93        string[highlight[1]:])
94
95
96# splits the source text on highlights boundaries so that we can escape to html
97# without the need to recalculate the highlights positions
98def highlightSplit(string, highlights):
99    splittedString = []
100    text = string
101    offset = 0
102    lastEnd = 0
103    for (start, end, name) in highlights:
104         if start >= lastEnd:
105            (before, between, after) = splitByHighlight( \
106                text, (start - offset, end - offset))
107            splittedString.append(before)
108            splittedString.append(between)
109            text = after
110            lastEnd = end
111            offset += len(before + between)
112         else:
113            print "overlap ", (start, end, name)
114    splittedString.append(text)
115    return splittedString
116
117
118# checkHighlights() checks for highlights overlaps
119def checkHighlights(highlights):
120    highlights.sort()
121
122    index = 0
123    lastHighlight = (-2, -1, '')
124
125    # merge overlapping highlights
126    for highlight in highlights:
127        if highlightOverlaps(highlight, lastHighlight):
128
129            newStart = min(lastHighlight[0], highlight[0])
130            newEnd = max(lastHighlight[1], highlight[1])
131            newComment = lastHighlight[2]
132
133            if (newComment.find(highlight[2]) == -1):
134                newComment += " + " + highlight[2]
135
136            highlight = (newStart, newEnd, newComment)
137            highlights[index] = highlight
138
139            # mark highlight to be deleted
140            highlights[index - 1] = (0, 0, "")
141
142        lastHighlight = highlight
143        index += 1
144
145    # remove "to be deleted" highlights
146    return [ (start, end, comment) for (start, end, comment) in highlights \
147        if (start, end, comment) != (0, 0, "") ]
148
149
150def cssStyle():
151    return """
152    .highlight {
153        background: #ffff00;
154        color: #000000;
155    }
156
157    .linehead {
158        background: #ddd;
159        font-size: 1px;
160    }
161
162    .highlight .linehead {
163        background: #ffff00;;
164        color: #999;
165        text-align: right;
166        font-size: 8px;
167    }
168
169    .linenumber {
170        background: #eee;
171        color: #999;
172        text-align: right;
173    }
174
175    td {
176        border-spacing: 0px;
177        border-width: 0px;
178        padding: 0px
179    }
180
181    div.code pre {
182        font-family: monospace;
183    }
184
185    .tooltip em {
186        display:none;
187    }
188
189    .tooltip:hover {
190        border: 0;
191        position: relative;
192        z-index: 500;
193        text-decoration:none;
194    }
195
196    .tooltip:hover em {
197        font-style: normal;
198        display: block;
199        position: absolute;
200        top: 20px;
201        left: -10px;
202        padding: 5px;
203        color: #000;
204        border: 1px solid #bbb;
205        background: #ffc;
206        width: auto;
207    }"""
208