##// END OF EJS Templates
- Close #131.
fperez -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,267 +1,281 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Class and program to colorize python source code for ANSI terminals.
3 Class and program to colorize python source code for ANSI terminals.
4
4
5 Based on an HTML code highlighter by Jurgen Hermann found at:
5 Based on an HTML code highlighter by Jurgen Hermann found at:
6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
7
7
8 Modifications by Fernando Perez (fperez@colorado.edu).
8 Modifications by Fernando Perez (fperez@colorado.edu).
9
9
10 Information on the original HTML highlighter follows:
10 Information on the original HTML highlighter follows:
11
11
12 MoinMoin - Python Source Parser
12 MoinMoin - Python Source Parser
13
13
14 Title:olorize Python source using the built-in tokenizer
14 Title:olorize Python source using the built-in tokenizer
15
15
16 Submitter: Jurgen Hermann
16 Submitter: Jurgen Hermann
17 Last Updated:2001/04/06
17 Last Updated:2001/04/06
18
18
19 Version no:1.2
19 Version no:1.2
20
20
21 Description:
21 Description:
22
22
23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
24 Python source code to HTML markup, rendering comments, keywords,
24 Python source code to HTML markup, rendering comments, keywords,
25 operators, numeric and string literals in different colors.
25 operators, numeric and string literals in different colors.
26
26
27 It shows how to use the built-in keyword, token and tokenize modules to
27 It shows how to use the built-in keyword, token and tokenize modules to
28 scan Python source code and re-emit it with no changes to its original
28 scan Python source code and re-emit it with no changes to its original
29 formatting (which is the hard part).
29 formatting (which is the hard part).
30
30
31 $Id: PyColorize.py 2155 2007-03-19 00:45:51Z fperez $"""
31 $Id: PyColorize.py 2205 2007-04-04 06:04:01Z fperez $"""
32
32
33 __all__ = ['ANSICodeColors','Parser']
33 __all__ = ['ANSICodeColors','Parser']
34
34
35 _scheme_default = 'Linux'
35 _scheme_default = 'Linux'
36
36
37 # Imports
37 # Imports
38 import cStringIO
38 import cStringIO
39 import keyword
39 import keyword
40 import os
40 import os
41 import string
41 import string
42 import sys
42 import sys
43 import token
43 import token
44 import tokenize
44 import tokenize
45
45
46 from IPython.ColorANSI import *
46 from IPython.ColorANSI import *
47
47
48 #############################################################################
48 #############################################################################
49 ### Python Source Parser (does Hilighting)
49 ### Python Source Parser (does Hilighting)
50 #############################################################################
50 #############################################################################
51
51
52 _KEYWORD = token.NT_OFFSET + 1
52 _KEYWORD = token.NT_OFFSET + 1
53 _TEXT = token.NT_OFFSET + 2
53 _TEXT = token.NT_OFFSET + 2
54
54
55 #****************************************************************************
55 #****************************************************************************
56 # Builtin color schemes
56 # Builtin color schemes
57
57
58 Colors = TermColors # just a shorthand
58 Colors = TermColors # just a shorthand
59
59
60 # Build a few color schemes
60 # Build a few color schemes
61 NoColor = ColorScheme(
61 NoColor = ColorScheme(
62 'NoColor',{
62 'NoColor',{
63 token.NUMBER : Colors.NoColor,
63 token.NUMBER : Colors.NoColor,
64 token.OP : Colors.NoColor,
64 token.OP : Colors.NoColor,
65 token.STRING : Colors.NoColor,
65 token.STRING : Colors.NoColor,
66 tokenize.COMMENT : Colors.NoColor,
66 tokenize.COMMENT : Colors.NoColor,
67 token.NAME : Colors.NoColor,
67 token.NAME : Colors.NoColor,
68 token.ERRORTOKEN : Colors.NoColor,
68 token.ERRORTOKEN : Colors.NoColor,
69
69
70 _KEYWORD : Colors.NoColor,
70 _KEYWORD : Colors.NoColor,
71 _TEXT : Colors.NoColor,
71 _TEXT : Colors.NoColor,
72
72
73 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
73 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
74 } )
74 } )
75
75
76 LinuxColors = ColorScheme(
76 LinuxColors = ColorScheme(
77 'Linux',{
77 'Linux',{
78 token.NUMBER : Colors.LightCyan,
78 token.NUMBER : Colors.LightCyan,
79 token.OP : Colors.Yellow,
79 token.OP : Colors.Yellow,
80 token.STRING : Colors.LightBlue,
80 token.STRING : Colors.LightBlue,
81 tokenize.COMMENT : Colors.LightRed,
81 tokenize.COMMENT : Colors.LightRed,
82 token.NAME : Colors.White,
82 token.NAME : Colors.White,
83 token.ERRORTOKEN : Colors.Red,
83 token.ERRORTOKEN : Colors.Red,
84
84
85 _KEYWORD : Colors.LightGreen,
85 _KEYWORD : Colors.LightGreen,
86 _TEXT : Colors.Yellow,
86 _TEXT : Colors.Yellow,
87
87
88 'normal' : Colors.Normal # color off (usu. Colors.Normal)
88 'normal' : Colors.Normal # color off (usu. Colors.Normal)
89 } )
89 } )
90
90
91 LightBGColors = ColorScheme(
91 LightBGColors = ColorScheme(
92 'LightBG',{
92 'LightBG',{
93 token.NUMBER : Colors.Cyan,
93 token.NUMBER : Colors.Cyan,
94 token.OP : Colors.Blue,
94 token.OP : Colors.Blue,
95 token.STRING : Colors.Blue,
95 token.STRING : Colors.Blue,
96 tokenize.COMMENT : Colors.Red,
96 tokenize.COMMENT : Colors.Red,
97 token.NAME : Colors.Black,
97 token.NAME : Colors.Black,
98 token.ERRORTOKEN : Colors.Red,
98 token.ERRORTOKEN : Colors.Red,
99
99
100 _KEYWORD : Colors.Green,
100 _KEYWORD : Colors.Green,
101 _TEXT : Colors.Blue,
101 _TEXT : Colors.Blue,
102
102
103 'normal' : Colors.Normal # color off (usu. Colors.Normal)
103 'normal' : Colors.Normal # color off (usu. Colors.Normal)
104 } )
104 } )
105
105
106 # Build table of color schemes (needed by the parser)
106 # Build table of color schemes (needed by the parser)
107 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
107 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
108 _scheme_default)
108 _scheme_default)
109
109
110 class Parser:
110 class Parser:
111 """ Format colored Python source.
111 """ Format colored Python source.
112 """
112 """
113
113
114 def __init__(self, color_table=None,out = sys.stdout):
114 def __init__(self, color_table=None,out = sys.stdout):
115 """ Create a parser with a specified color table and output channel.
115 """ Create a parser with a specified color table and output channel.
116
116
117 Call format() to process code.
117 Call format() to process code.
118 """
118 """
119 self.color_table = color_table and color_table or ANSICodeColors
119 self.color_table = color_table and color_table or ANSICodeColors
120 self.out = out
120 self.out = out
121
121
122 def format(self, raw, out = None, scheme = ''):
122 def format(self, raw, out = None, scheme = ''):
123 return self.format2(raw, out, scheme)[0]
123 return self.format2(raw, out, scheme)[0]
124
124
125 def format2(self, raw, out = None, scheme = ''):
125 def format2(self, raw, out = None, scheme = ''):
126 """ Parse and send the colored source.
126 """ Parse and send the colored source.
127
127
128 If out and scheme are not specified, the defaults (given to
128 If out and scheme are not specified, the defaults (given to
129 constructor) are used.
129 constructor) are used.
130
130
131 out should be a file-type object. Optionally, out can be given as the
131 out should be a file-type object. Optionally, out can be given as the
132 string 'str' and the parser will automatically return the output in a
132 string 'str' and the parser will automatically return the output in a
133 string."""
133 string."""
134
134
135 self.raw = string.strip(string.expandtabs(raw))
136 string_output = 0
135 string_output = 0
137 if out == 'str' or self.out == 'str':
136 if out == 'str' or self.out == 'str':
138 out_old = self.out
137 out_old = self.out
139 self.out = cStringIO.StringIO()
138 self.out = cStringIO.StringIO()
140 string_output = 1
139 string_output = 1
141 elif out is not None:
140 elif out is not None:
142 self.out = out
141 self.out = out
143 # local shorthand
142
143 # Fast return of the unmodified input for NoColor scheme
144 if scheme == 'NoColor':
145 error = False
146 self.out.write(raw)
147 if string_output:
148 return raw,error
149 else:
150 return None,error
151
152 # local shorthands
144 colors = self.color_table[scheme].colors
153 colors = self.color_table[scheme].colors
145 self.colors = colors # put in object so __call__ sees it
154 self.colors = colors # put in object so __call__ sees it
155
156 # Remove trailing whitespace and normalize tabs
157 self.raw = raw.expandtabs().rstrip()
146 # store line offsets in self.lines
158 # store line offsets in self.lines
147 self.lines = [0, 0]
159 self.lines = [0, 0]
148 pos = 0
160 pos = 0
161 raw_find = raw.find
162 lines_append = self.lines.append
149 while 1:
163 while 1:
150 pos = string.find(self.raw, '\n', pos) + 1
164 pos = raw_find('\n', pos) + 1
151 if not pos: break
165 if not pos: break
152 self.lines.append(pos)
166 lines_append(pos)
153 self.lines.append(len(self.raw))
167 lines_append(len(self.raw))
154
168
155 # parse the source and write it
169 # parse the source and write it
156 self.pos = 0
170 self.pos = 0
157 text = cStringIO.StringIO(self.raw)
171 text = cStringIO.StringIO(self.raw)
158 #self.out.write('<pre><font face="Courier New">')
159
172
160 error = False
173 error = False
161 try:
174 try:
162 tokenize.tokenize(text.readline, self)
175 tokenize.tokenize(text.readline, self)
163 except tokenize.TokenError, ex:
176 except tokenize.TokenError, ex:
164 msg = ex[0]
177 msg = ex[0]
165 line = ex[1][0]
178 line = ex[1][0]
166 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
179 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
167 (colors[token.ERRORTOKEN],
180 (colors[token.ERRORTOKEN],
168 msg, self.raw[self.lines[line]:],
181 msg, self.raw[self.lines[line]:],
169 colors.normal)
182 colors.normal)
170 )
183 )
171 error = True
184 error = True
172 self.out.write(colors.normal+'\n')
185 self.out.write(colors.normal+'\n')
173 if string_output:
186 if string_output:
174 output = self.out.getvalue()
187 output = self.out.getvalue()
175 self.out = out_old
188 self.out = out_old
176 return (output, error)
189 return (output, error)
177 return (None, error)
190 return (None, error)
178
191
179 def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
192 def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
180 """ Token handler, with syntax highlighting."""
193 """ Token handler, with syntax highlighting."""
181
194
182 # local shorthand
195 # local shorthands
183 colors = self.colors
196 colors = self.colors
197 owrite = self.out.write
184
198
185 # line separator, so this works across platforms
199 # line separator, so this works across platforms
186 linesep = os.linesep
200 linesep = os.linesep
187
201
188 # calculate new positions
202 # calculate new positions
189 oldpos = self.pos
203 oldpos = self.pos
190 newpos = self.lines[srow] + scol
204 newpos = self.lines[srow] + scol
191 self.pos = newpos + len(toktext)
205 self.pos = newpos + len(toktext)
192
206
193 # handle newlines
207 # handle newlines
194 if toktype in [token.NEWLINE, tokenize.NL]:
208 if toktype in [token.NEWLINE, tokenize.NL]:
195 self.out.write(linesep)
209 owrite(linesep)
196 return
210 return
197
211
198 # send the original whitespace, if needed
212 # send the original whitespace, if needed
199 if newpos > oldpos:
213 if newpos > oldpos:
200 self.out.write(self.raw[oldpos:newpos])
214 owrite(self.raw[oldpos:newpos])
201
215
202 # skip indenting tokens
216 # skip indenting tokens
203 if toktype in [token.INDENT, token.DEDENT]:
217 if toktype in [token.INDENT, token.DEDENT]:
204 self.pos = newpos
218 self.pos = newpos
205 return
219 return
206
220
207 # map token type to a color group
221 # map token type to a color group
208 if token.LPAR <= toktype and toktype <= token.OP:
222 if token.LPAR <= toktype and toktype <= token.OP:
209 toktype = token.OP
223 toktype = token.OP
210 elif toktype == token.NAME and keyword.iskeyword(toktext):
224 elif toktype == token.NAME and keyword.iskeyword(toktext):
211 toktype = _KEYWORD
225 toktype = _KEYWORD
212 color = colors.get(toktype, colors[_TEXT])
226 color = colors.get(toktype, colors[_TEXT])
213
227
214 #print '<%s>' % toktext, # dbg
228 #print '<%s>' % toktext, # dbg
215
229
216 # Triple quoted strings must be handled carefully so that backtracking
230 # Triple quoted strings must be handled carefully so that backtracking
217 # in pagers works correctly. We need color terminators on _each_ line.
231 # in pagers works correctly. We need color terminators on _each_ line.
218 if linesep in toktext:
232 if linesep in toktext:
219 toktext = toktext.replace(linesep, '%s%s%s' %
233 toktext = toktext.replace(linesep, '%s%s%s' %
220 (colors.normal,linesep,color))
234 (colors.normal,linesep,color))
221
235
222 # send text
236 # send text
223 self.out.write('%s%s%s' % (color,toktext,colors.normal))
237 owrite('%s%s%s' % (color,toktext,colors.normal))
224
238
225 def main():
239 def main():
226 """Colorize a python file using ANSI color escapes and print to stdout.
240 """Colorize a python file using ANSI color escapes and print to stdout.
227
241
228 Usage:
242 Usage:
229 %s [-s scheme] filename
243 %s [-s scheme] filename
230
244
231 Options:
245 Options:
232
246
233 -s scheme: give the color scheme to use. Currently only 'Linux'
247 -s scheme: give the color scheme to use. Currently only 'Linux'
234 (default) and 'LightBG' and 'NoColor' are implemented (give without
248 (default) and 'LightBG' and 'NoColor' are implemented (give without
235 quotes). """
249 quotes). """
236
250
237 def usage():
251 def usage():
238 print >> sys.stderr, main.__doc__ % sys.argv[0]
252 print >> sys.stderr, main.__doc__ % sys.argv[0]
239 sys.exit(1)
253 sys.exit(1)
240
254
241 # FIXME: rewrite this to at least use getopt
255 # FIXME: rewrite this to at least use getopt
242 try:
256 try:
243 if sys.argv[1] == '-s':
257 if sys.argv[1] == '-s':
244 scheme_name = sys.argv[2]
258 scheme_name = sys.argv[2]
245 del sys.argv[1:3]
259 del sys.argv[1:3]
246 else:
260 else:
247 scheme_name = _scheme_default
261 scheme_name = _scheme_default
248
262
249 except:
263 except:
250 usage()
264 usage()
251
265
252 try:
266 try:
253 fname = sys.argv[1]
267 fname = sys.argv[1]
254 except:
268 except:
255 usage()
269 usage()
256
270
257 # write colorized version to stdout
271 # write colorized version to stdout
258 parser = Parser()
272 parser = Parser()
259 try:
273 try:
260 parser.format(file(fname).read(),scheme = scheme_name)
274 parser.format(file(fname).read(),scheme = scheme_name)
261 except IOError,msg:
275 except IOError,msg:
262 # if user reads through a pager and quits, don't print traceback
276 # if user reads through a pager and quits, don't print traceback
263 if msg.args != (32,'Broken pipe'):
277 if msg.args != (32,'Broken pipe'):
264 raise
278 raise
265
279
266 if __name__ == "__main__":
280 if __name__ == "__main__":
267 main()
281 main()
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now