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    </body>
41    </html>""")
42
43    file.close()
44
45
46# render in html
47def renderHtml(text, highlights, sourceFileName, outputFileName):
48    splittedText = highlightSplit(text, highlights)
49
50    file = open(outputFileName, 'a')
51    file.write("<hr/><p><b>" + sourceFileName + "</b></p>")
52
53    # insert highlight tags in a temp buffer
54    temp = ""
55    count = 0
56    for slice in splittedText:
57        if count % 2 == 0:
58            temp += escape(slice) + '<span class="highlight tooltip">'
59        else:
60            temp += escape(slice) + "<em>" + highlights[(count - 1) / 2][2] \
61                + "</em></span>"
62        count += 1
63
64    temp += "</span>" # close the superfluous last highlight
65
66    file.write('<table><tr><td><pre class="code"><span class="linenumber">')
67    count = 1
68    for line in temp.split('\n'):
69        file.write(str(count).rjust(4)+"<br/>")
70        count += 1
71
72    file.write('</span></pre></td><td><pre class="code">')
73
74    for line in temp.split('\n'):
75        file.write('<span class="linehead"> </span>' + line.replace('\r', ' ') \
76             + '<br/>')
77
78    file.write("</pre></td></tr></table>")
79
80    file.close()
81
82
83# highlight overlap check
84def highlightOverlaps(highlight1, highlight2):
85    #print "hl1", highlight1, "hl2", highlight2
86    return not(highlight2[0] > highlight1[1] or highlight1[0] > highlight2[1])
87
88
89# splits the string in three parts before, between and after the highlight
90def splitByHighlight(string, highlight):
91    return (string[:highlight[0]], string[highlight[0]:highlight[1]], \
92        string[highlight[1]:])
93
94
95# splits the source text on highlights boundaries so that we can escape to html
96# without the need to recalculate the highlights positions
97def highlightSplit(string, highlights):
98    splittedString = []
99    text = string
100    offset = 0
101    lastEnd = 0
102    for (start, end, name) in highlights:
103         if start >= lastEnd:
104            (before, between, after) = splitByHighlight( \
105                text, (start - offset, end - offset))
106            splittedString.append(before)
107            splittedString.append(between)
108            text = after
109            lastEnd = end
110            offset += len(before + between)
111         else:
112            print "overlap ", (start, end, name)
113    splittedString.append(text)
114    return splittedString
115
116
117# checkHighlights() checks for highlights overlaps
118def checkHighlights(highlights):
119    highlights.sort()
120
121    index = 0
122    lastHighlight = (-2, -1, '')
123
124    # merge overlapping highlights
125    for highlight in highlights:
126        if highlightOverlaps(highlight, lastHighlight):
127
128            newStart = min(lastHighlight[0], highlight[0])
129            newEnd = max(lastHighlight[1], highlight[1])
130            newComment = lastHighlight[2]
131
132            if (newComment.find(highlight[2]) == -1):
133                newComment += " + " + highlight[2]
134
135            highlight = (newStart, newEnd, newComment)
136            highlights[index] = highlight
137
138            # mark highlight to be deleted
139            highlights[index - 1] = (0, 0, "")
140
141        lastHighlight = highlight
142        index += 1
143
144    # remove "to be deleted" highlights
145    return [ (start, end, comment) for (start, end, comment) in highlights \
146        if (start, end, comment) != (0, 0, "") ]
147
148
149def cssStyle():
150    return """
151    .highlight {
152        background: #ffff00;
153        color: #000000;
154    }
155
156    .linehead {
157        background: #ddd;
158        font-size: 1px;
159    }
160
161    .highlight .linehead {
162        background: #ffff00;;
163        color: #999;
164        text-align: right;
165        font-size: 8px;
166    }
167
168    .linenumber {
169        background: #eee;
170        color: #999;
171        text-align: right;
172    }
173
174    td {
175        border-spacing: 0px;
176        border-width: 0px;
177        padding: 0px
178    }
179
180    div.code pre {
181        font-family: monospace;
182    }
183
184    .tooltip em {
185        display:none;
186    }
187
188    .tooltip:hover {
189        border: 0;
190        position: relative;
191        z-index: 500;
192        text-decoration:none;
193    }
194
195    .tooltip:hover em {
196        font-style: normal;
197        display: block;
198        position: absolute;
199        top: 20px;
200        left: -10px;
201        padding: 5px;
202        color: #000;
203        border: 1px solid #bbb;
204        background: #ffc;
205        width: auto;
206    }"""
207