OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 #import('../lang.dart'); | |
5 | |
6 /** | |
7 * Kinds of tokens that we care to highlight differently. The values of the | |
8 * fields here will be used as CSS class names for the generated spans. | |
9 */ | |
10 class Classification { | |
11 static final NONE = null; | |
12 static final ERROR = "e"; | |
13 static final COMMENT = "c"; | |
14 static final IDENTIFIER = "i"; | |
15 static final KEYWORD = "k"; | |
16 static final OPERATOR = "o"; | |
17 static final STRING = "s"; | |
18 static final NUMBER = "n"; | |
19 static final PUNCTUATION = "p"; | |
20 | |
21 // A few things that are nice to make different: | |
22 static final TYPE_IDENTIFIER = "t"; | |
23 | |
24 // Between a keyword and an identifier | |
25 static final SPECIAL_IDENTIFIER = "r"; | |
26 | |
27 static final ARROW_OPERATOR = "a"; | |
28 | |
29 static final STRING_INTERPOLATION = 'si'; | |
30 } | |
31 | |
32 String classifySource(SourceFile src) { | |
33 var html = new StringBuffer(); | |
34 var tokenizer = new Tokenizer(src, /*skipWhitespace:*/false); | |
35 | |
36 var token; | |
37 var inString = false; | |
38 while ((token = tokenizer.next()).kind != TokenKind.END_OF_FILE) { | |
39 | |
40 // Track whether or not we're in a string. | |
41 switch (token.kind) { | |
42 case TokenKind.STRING: | |
43 case TokenKind.STRING_PART: | |
44 case TokenKind.INCOMPLETE_STRING: | |
45 case TokenKind.INCOMPLETE_MULTILINE_STRING_DQ: | |
46 case TokenKind.INCOMPLETE_MULTILINE_STRING_SQ: | |
47 inString = true; | |
48 break; | |
49 } | |
50 | |
51 final kind = classify(token); | |
52 final text = htmlEscape(token.text); | |
53 if (kind != null) { | |
54 // Add a secondary class to tokens appearing within a string so that | |
55 // we can highlight tokens in an interpolation specially. | |
56 var stringClass = inString ? Classification.STRING_INTERPOLATION : ''; | |
57 html.add('<span class="$kind $stringClass">$text</span>'); | |
58 } else { | |
59 html.add('<span>$text</span>'); | |
60 } | |
61 | |
62 // Track whether or not we're in a string. | |
63 if (token.kind == TokenKind.STRING) { | |
64 inString = false; | |
65 } | |
66 } | |
67 return html.toString(); | |
68 } | |
69 | |
70 // TODO(rnystrom): should exist in standard lib somewhere | |
71 String htmlEscape(String text) { | |
72 return text.replaceAll('&', '&').replaceAll( | |
73 '>', '>').replaceAll('<', '<'); | |
74 } | |
75 | |
76 bool _looksLikeType(String name) { | |
77 // If the name looks like an UppercaseName, assume it's a type. | |
78 return _looksLikePublicType(name) || _looksLikePrivateType(name); | |
79 } | |
80 | |
81 bool _looksLikePublicType(String name) { | |
82 // If the name looks like an UppercaseName, assume it's a type. | |
83 return name.length >= 2 && isUpper(name[0]) && isLower(name[1]); | |
84 } | |
85 | |
86 bool _looksLikePrivateType(String name) { | |
87 // If the name looks like an _UppercaseName, assume it's a type. | |
88 return (name.length >= 3 && name[0] == '_' && isUpper(name[1]) | |
89 && isLower(name[2])); | |
90 } | |
91 | |
92 // These ensure that they don't return "true" if the string only has symbols. | |
93 bool isUpper(String s) => s.toLowerCase() != s; | |
94 bool isLower(String s) => s.toUpperCase() != s; | |
95 | |
96 String classify(Token token) { | |
97 switch (token.kind) { | |
98 case TokenKind.ERROR: | |
99 return Classification.ERROR; | |
100 | |
101 case TokenKind.IDENTIFIER: | |
102 // Special case for names that look like types. | |
103 if (_looksLikeType(token.text) | |
104 || token.text == 'num' | |
105 || token.text == 'bool' | |
106 || token.text == 'int' | |
107 || token.text == 'double') { | |
108 return Classification.TYPE_IDENTIFIER; | |
109 } | |
110 return Classification.IDENTIFIER; | |
111 | |
112 // Even though it's a reserved word, let's try coloring it like a type. | |
113 case TokenKind.VOID: | |
114 return Classification.TYPE_IDENTIFIER; | |
115 | |
116 case TokenKind.THIS: | |
117 case TokenKind.SUPER: | |
118 return Classification.SPECIAL_IDENTIFIER; | |
119 | |
120 case TokenKind.STRING: | |
121 case TokenKind.STRING_PART: | |
122 case TokenKind.INCOMPLETE_STRING: | |
123 case TokenKind.INCOMPLETE_MULTILINE_STRING_DQ: | |
124 case TokenKind.INCOMPLETE_MULTILINE_STRING_SQ: | |
125 return Classification.STRING; | |
126 | |
127 case TokenKind.INTEGER: | |
128 case TokenKind.HEX_INTEGER: | |
129 case TokenKind.DOUBLE: | |
130 return Classification.NUMBER; | |
131 | |
132 case TokenKind.COMMENT: | |
133 case TokenKind.INCOMPLETE_COMMENT: | |
134 return Classification.COMMENT; | |
135 | |
136 // => is so awesome it is in a class of its own. | |
137 case TokenKind.ARROW: | |
138 return Classification.ARROW_OPERATOR; | |
139 | |
140 case TokenKind.HASHBANG: | |
141 case TokenKind.LPAREN: | |
142 case TokenKind.RPAREN: | |
143 case TokenKind.LBRACK: | |
144 case TokenKind.RBRACK: | |
145 case TokenKind.LBRACE: | |
146 case TokenKind.RBRACE: | |
147 case TokenKind.COLON: | |
148 case TokenKind.SEMICOLON: | |
149 case TokenKind.COMMA: | |
150 case TokenKind.DOT: | |
151 case TokenKind.ELLIPSIS: | |
152 return Classification.PUNCTUATION; | |
153 | |
154 case TokenKind.INCR: | |
155 case TokenKind.DECR: | |
156 case TokenKind.BIT_NOT: | |
157 case TokenKind.NOT: | |
158 case TokenKind.ASSIGN: | |
159 case TokenKind.ASSIGN_OR: | |
160 case TokenKind.ASSIGN_XOR: | |
161 case TokenKind.ASSIGN_AND: | |
162 case TokenKind.ASSIGN_SHL: | |
163 case TokenKind.ASSIGN_SAR: | |
164 case TokenKind.ASSIGN_SHR: | |
165 case TokenKind.ASSIGN_ADD: | |
166 case TokenKind.ASSIGN_SUB: | |
167 case TokenKind.ASSIGN_MUL: | |
168 case TokenKind.ASSIGN_DIV: | |
169 case TokenKind.ASSIGN_TRUNCDIV: | |
170 case TokenKind.ASSIGN_MOD: | |
171 case TokenKind.CONDITIONAL: | |
172 case TokenKind.OR: | |
173 case TokenKind.AND: | |
174 case TokenKind.BIT_OR: | |
175 case TokenKind.BIT_XOR: | |
176 case TokenKind.BIT_AND: | |
177 case TokenKind.SHL: | |
178 case TokenKind.SAR: | |
179 case TokenKind.SHR: | |
180 case TokenKind.ADD: | |
181 case TokenKind.SUB: | |
182 case TokenKind.MUL: | |
183 case TokenKind.DIV: | |
184 case TokenKind.TRUNCDIV: | |
185 case TokenKind.MOD: | |
186 case TokenKind.EQ: | |
187 case TokenKind.NE: | |
188 case TokenKind.EQ_STRICT: | |
189 case TokenKind.NE_STRICT: | |
190 case TokenKind.LT: | |
191 case TokenKind.GT: | |
192 case TokenKind.LTE: | |
193 case TokenKind.GTE: | |
194 case TokenKind.INDEX: | |
195 case TokenKind.SETINDEX: | |
196 return Classification.OPERATOR; | |
197 | |
198 // Color this like a keyword | |
199 case TokenKind.HASH: | |
200 | |
201 case TokenKind.ABSTRACT: | |
202 case TokenKind.ASSERT: | |
203 case TokenKind.CLASS: | |
204 case TokenKind.EXTENDS: | |
205 case TokenKind.FACTORY: | |
206 case TokenKind.GET: | |
207 case TokenKind.IMPLEMENTS: | |
208 case TokenKind.IMPORT: | |
209 case TokenKind.INTERFACE: | |
210 case TokenKind.LIBRARY: | |
211 case TokenKind.NATIVE: | |
212 case TokenKind.NEGATE: | |
213 case TokenKind.OPERATOR: | |
214 case TokenKind.SET: | |
215 case TokenKind.SOURCE: | |
216 case TokenKind.STATIC: | |
217 case TokenKind.TYPEDEF: | |
218 case TokenKind.BREAK: | |
219 case TokenKind.CASE: | |
220 case TokenKind.CATCH: | |
221 case TokenKind.CONST: | |
222 case TokenKind.CONTINUE: | |
223 case TokenKind.DEFAULT: | |
224 case TokenKind.DO: | |
225 case TokenKind.ELSE: | |
226 case TokenKind.FALSE: | |
227 case TokenKind.FINALLY: | |
228 case TokenKind.FOR: | |
229 case TokenKind.IF: | |
230 case TokenKind.IN: | |
231 case TokenKind.IS: | |
232 case TokenKind.NEW: | |
233 case TokenKind.NULL: | |
234 case TokenKind.RETURN: | |
235 case TokenKind.SWITCH: | |
236 case TokenKind.THROW: | |
237 case TokenKind.TRUE: | |
238 case TokenKind.TRY: | |
239 case TokenKind.WHILE: | |
240 case TokenKind.VAR: | |
241 case TokenKind.FINAL: | |
242 return Classification.KEYWORD; | |
243 | |
244 case TokenKind.WHITESPACE: | |
245 case TokenKind.END_OF_FILE: | |
246 return Classification.NONE; | |
247 | |
248 default: | |
249 return Classification.NONE; | |
250 } | |
251 } | |
OLD | NEW |