...
[cython.git] / Cython / Compiler / Annotate.py
1 # Note: Work in progress
2
3 import os
4 import re
5 import time
6 from StringIO import StringIO
7
8 import Version
9 from Code import CCodeWriter
10
11 # need one-characters subsitutions (for now) so offsets aren't off
12 special_chars = [('<', '\xF0', '&lt;'),
13                  ('>', '\xF1', '&gt;'), 
14                  ('&', '\xF2', '&amp;')]
15
16 class AnnotationCCodeWriter(CCodeWriter):
17
18     def __init__(self, f):
19         CCodeWriter.__init__(self, self)
20         self.buffer = StringIO()
21         self.real_f = f
22         self.annotations = []
23         self.last_pos = None
24         self.code = {}
25         
26     def getvalue(self):
27         return self.real_f.getvalue()
28         
29     def write(self, s):
30         self.real_f.write(s)
31         self.buffer.write(s)
32         
33     def mark_pos(self, pos):
34 #        if pos is not None:
35 #            CCodeWriter.mark_pos(self, pos)
36 #        return
37         if self.last_pos:
38             try:
39                 code = self.code[self.last_pos[1]]
40             except KeyError:
41                 code = ""
42             self.code[self.last_pos[1]] = code + self.buffer.getvalue()
43         self.buffer = StringIO()
44         self.last_pos = pos
45
46     def annotate(self, pos, item):
47         self.annotations.append((pos, item))
48         
49     def save_annotation(self, filename):
50         self.mark_pos(None)
51         f = open(filename)
52         lines = f.readlines()
53         for k in range(len(lines)):
54             line = lines[k]
55             for c, cc, html in special_chars:
56                 line = line.replace(c, cc)
57             lines[k] = line
58         f.close()
59         all = []
60         for pos, item in self.annotations:
61             if pos[0] == filename:
62                 start = item.start()
63                 size, end = item.end()
64                 if size:
65                     all.append((pos, start))
66                     all.append(((filename, pos[1], pos[2]+size), end))
67                 else:
68                     all.append((pos, start+end))
69                 
70         all.sort()
71         all.reverse()
72         for pos, item in all:
73             _, line_no, col = pos
74             line_no -= 1
75             col += 1
76             line = lines[line_no]
77             lines[line_no] = line[:col] + item + line[col:]
78         
79         f = open("%s.html" % filename, "w")
80         f.write('<html>\n')
81         f.write("""
82 <head>
83 <style type="text/css">
84
85 body { font-family: courier; font-size: 12; }
86
87 .code  { font-size: 9; color: #444444; display: none; margin-left: 20px; }
88 .py_api  { color: red; }
89 .pyx_api  { color: #FF3000; }
90 .py_macro_api  { color: #FF8000; }
91 .error_goto  { color: #FF8000; }
92
93 .tag  {  }
94
95 .coerce  { color: #008000; border: 1px dotted #008000 }
96
97 .py_attr { color: #FF0000; font-weight: bold; }
98 .c_attr  { color: #0000FF; }
99
100 .py_call { color: #FF0000; font-weight: bold; }
101 .c_call  { color: #0000FF; }
102
103 .line { margin: 0em }
104
105 </style>
106 <script>
107 function toggleDiv(id) {
108     theDiv = document.getElementById(id);
109     if (theDiv.style.display == 'none') theDiv.style.display = 'block';
110     else theDiv.style.display = 'none';
111 }
112 </script>
113 </head>
114         """)
115         f.write('<body>\n')
116         f.write('<p>Generated by Cython %s on %s\n' % (Version.version, time.asctime()))
117         c_file = os.path.basename(filename)[:-3] + 'c'
118         f.write('<p>Raw output: <a href="%s">%s</a>\n' % (c_file, c_file))
119         k = 0
120         
121         py_c_api = re.compile('(Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)')
122         pyx_api = re.compile('(__Pyx[A-Za-z_]+)\(')
123         py_marco_api = re.compile('(Py[A-Za-z]*_[A-Z][A-Z_]+)')
124         error_goto = re.compile(r'((; *if .*)? \{__pyx_filename = .*goto __pyx_L\w+;\})')
125         
126         for line in lines:
127
128             k += 1
129             try:
130                 code = self.code[k]
131             except KeyError:
132                 code = ''
133                 
134             code, c_api_calls = py_c_api.subn(r"<span class='py_api'>\1</span>", code)
135             code, pyx_api_calls = pyx_api.subn(r"<span class='pyx_api'>\1</span>(", code)
136             code, macro_api_calls = py_marco_api.subn(r"<span class='py_macro_api'>\1</span>", code)
137             code, error_goto_calls = error_goto.subn(r"<span class='error_goto'>\1</span>", code)
138             
139             code = code.replace("<span class='error_goto'>;", ";<span class='error_goto'>")
140             
141             color = "FFFF%02x" % int(255/(1+(5*c_api_calls+2*pyx_api_calls+macro_api_calls)/10.0))
142             f.write("<pre class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color, k))
143
144             f.write(" %d: " % k)
145             for c, cc, html in special_chars:
146                 line = line.replace(cc, html)
147             f.write(line.rstrip())
148                 
149             f.write('</pre>\n')
150             f.write("<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k, color, code))
151         f.write('</body></html>\n')
152         f.close()
153         
154
155 # TODO: make this cleaner
156 def escape(raw_string):
157     raw_string = raw_string.replace("\'", r"&#146;")
158     raw_string = raw_string.replace('\"', r'&quot;')
159     raw_string = raw_string.replace('\n', r'<br>\n')
160     raw_string = raw_string.replace('\t', r'\t')
161     return raw_string
162
163
164 class AnnotationItem:
165     
166     def __init__(self, style, text, tag="", size=0):
167         self.style = style
168         self.text = text
169         self.tag = tag
170         self.size = size
171         
172     def start(self):
173         return "<span class='tag %s' title='%s'>%s" % (self.style, self.text, self.tag)
174     
175     def end(self):
176         return self.size, "</span>"