OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 | |
5 #library('classify'); | |
6 | |
7 #import('../../lib/compiler/implementation/scanner/scannerlib.dart'); | |
8 #import('markdown.dart', prefix: 'md'); | |
9 | |
10 /** | |
11 * Kinds of tokens that we care to highlight differently. The values of the | |
12 * fields here will be used as CSS class names for the generated spans. | |
13 */ | |
14 class Classification { | |
15 static const NONE = null; | |
16 static const ERROR = "e"; | |
17 static const COMMENT = "c"; | |
18 static const IDENTIFIER = "i"; | |
19 static const KEYWORD = "k"; | |
20 static const OPERATOR = "o"; | |
21 static const STRING = "s"; | |
22 static const NUMBER = "n"; | |
23 static const PUNCTUATION = "p"; | |
24 | |
25 // A few things that are nice to make different: | |
26 static const TYPE_IDENTIFIER = "t"; | |
27 | |
28 // Between a keyword and an identifier | |
29 static const SPECIAL_IDENTIFIER = "r"; | |
30 | |
31 static const ARROW_OPERATOR = "a"; | |
32 | |
33 static const STRING_INTERPOLATION = 'si'; | |
34 } | |
35 | |
36 String classifySource(String text) { | |
37 var html = new StringBuffer(); | |
38 var tokenizer = new StringScanner(text, includeComments: true); | |
39 | |
40 var whitespaceOffset = 0; | |
41 var token = tokenizer.tokenize(); | |
42 var inString = false; | |
43 while (token.kind != EOF_TOKEN) { | |
44 html.add(text.substring(whitespaceOffset, token.charOffset)); | |
45 whitespaceOffset = token.charOffset + token.slowCharCount; | |
46 | |
47 // Track whether or not we're in a string. | |
48 switch (token.kind) { | |
49 case STRING_TOKEN: | |
50 case STRING_INTERPOLATION_TOKEN: | |
51 inString = true; | |
52 break; | |
53 } | |
54 | |
55 final kind = classify(token); | |
56 final escapedText = md.escapeHtml(token.slowToString()); | |
57 if (kind != null) { | |
58 // Add a secondary class to tokens appearing within a string so that | |
59 // we can highlight tokens in an interpolation specially. | |
60 var stringClass = inString ? Classification.STRING_INTERPOLATION : ''; | |
61 html.add('<span class="$kind $stringClass">$escapedText</span>'); | |
62 } else { | |
63 html.add(escapedText); | |
64 } | |
65 | |
66 // Track whether or not we're in a string. | |
67 if (token.kind == STRING_TOKEN) { | |
68 inString = false; | |
69 } | |
70 token = token.next; | |
71 } | |
72 return html.toString(); | |
73 } | |
74 | |
75 bool _looksLikeType(String name) { | |
76 // If the name looks like an UppercaseName, assume it's a type. | |
77 return _looksLikePublicType(name) || _looksLikePrivateType(name); | |
78 } | |
79 | |
80 bool _looksLikePublicType(String name) { | |
81 // If the name looks like an UppercaseName, assume it's a type. | |
82 return name.length >= 2 && isUpper(name[0]) && isLower(name[1]); | |
83 } | |
84 | |
85 bool _looksLikePrivateType(String name) { | |
86 // If the name looks like an _UppercaseName, assume it's a type. | |
87 return (name.length >= 3 && name[0] == '_' && isUpper(name[1]) | |
88 && isLower(name[2])); | |
89 } | |
90 | |
91 // These ensure that they don't return "true" if the string only has symbols. | |
92 bool isUpper(String s) => s.toLowerCase() != s; | |
93 bool isLower(String s) => s.toUpperCase() != s; | |
94 | |
95 String classify(Token token) { | |
96 switch (token.kind) { | |
97 case UNKNOWN_TOKEN: | |
98 return Classification.ERROR; | |
99 | |
100 case IDENTIFIER_TOKEN: | |
101 // Special case for names that look like types. | |
102 final text = token.slowToString(); | |
103 if (_looksLikeType(text) | |
104 || text == 'num' | |
105 || text == 'bool' | |
106 || text == 'int' | |
107 || text == 'double') { | |
108 return Classification.TYPE_IDENTIFIER; | |
109 } | |
110 return Classification.IDENTIFIER; | |
111 | |
112 case STRING_TOKEN: | |
113 case STRING_INTERPOLATION_TOKEN: | |
114 return Classification.STRING; | |
115 | |
116 case INT_TOKEN: | |
117 case HEXADECIMAL_TOKEN: | |
118 case DOUBLE_TOKEN: | |
119 return Classification.NUMBER; | |
120 | |
121 case COMMENT_TOKEN: | |
122 return Classification.COMMENT; | |
123 | |
124 // => is so awesome it is in a class of its own. | |
125 case FUNCTION_TOKEN: | |
126 return Classification.ARROW_OPERATOR; | |
127 | |
128 case OPEN_PAREN_TOKEN: | |
129 case CLOSE_PAREN_TOKEN: | |
130 case OPEN_SQUARE_BRACKET_TOKEN: | |
131 case CLOSE_SQUARE_BRACKET_TOKEN: | |
132 case OPEN_CURLY_BRACKET_TOKEN: | |
133 case CLOSE_CURLY_BRACKET_TOKEN: | |
134 case COLON_TOKEN: | |
135 case SEMICOLON_TOKEN: | |
136 case COMMA_TOKEN: | |
137 case PERIOD_TOKEN: | |
138 case PERIOD_PERIOD_TOKEN: | |
139 return Classification.PUNCTUATION; | |
140 | |
141 case PLUS_PLUS_TOKEN: | |
142 case MINUS_MINUS_TOKEN: | |
143 case TILDE_TOKEN: | |
144 case BANG_TOKEN: | |
145 case EQ_TOKEN: | |
146 case BAR_EQ_TOKEN: | |
147 case CARET_EQ_TOKEN: | |
148 case AMPERSAND_EQ_TOKEN: | |
149 case LT_LT_EQ_TOKEN: | |
150 case GT_GT_GT_EQ_TOKEN: | |
151 case GT_GT_EQ_TOKEN: | |
152 case PLUS_EQ_TOKEN: | |
153 case MINUS_EQ_TOKEN: | |
154 case STAR_EQ_TOKEN: | |
155 case SLASH_EQ_TOKEN: | |
156 case TILDE_SLASH_EQ_TOKEN: | |
157 case PERCENT_EQ_TOKEN: | |
158 case QUESTION_TOKEN: | |
159 case BAR_BAR_TOKEN: | |
160 case AMPERSAND_AMPERSAND_TOKEN: | |
161 case BAR_TOKEN: | |
162 case CARET_TOKEN: | |
163 case AMPERSAND_TOKEN: | |
164 case LT_LT_TOKEN: | |
165 case GT_GT_GT_TOKEN: | |
166 case GT_GT_TOKEN: | |
167 case PLUS_TOKEN: | |
168 case MINUS_TOKEN: | |
169 case STAR_TOKEN: | |
170 case SLASH_TOKEN: | |
171 case TILDE_SLASH_TOKEN: | |
172 case PERCENT_TOKEN: | |
173 case EQ_EQ_TOKEN: | |
174 case BANG_EQ_TOKEN: | |
175 case EQ_EQ_EQ_TOKEN: | |
176 case BANG_EQ_EQ_TOKEN: | |
177 case LT_TOKEN: | |
178 case GT_TOKEN: | |
179 case LT_EQ_TOKEN: | |
180 case GT_EQ_TOKEN: | |
181 case INDEX_TOKEN: | |
182 case INDEX_EQ_TOKEN: | |
183 return Classification.OPERATOR; | |
184 | |
185 // Color keyword token. Most are colored as keywords. | |
186 case HASH_TOKEN: | |
187 case KEYWORD_TOKEN: | |
188 if (token.stringValue === 'void') { | |
189 // Color "void" as a type. | |
190 return Classification.TYPE_IDENTIFIER; | |
191 } | |
192 if (token.stringValue === 'this' || token.stringValue === 'super') { | |
193 // Color "this" and "super" as identifiers. | |
194 return Classification.SPECIAL_IDENTIFIER; | |
195 } | |
196 return Classification.KEYWORD; | |
197 | |
198 case EOF_TOKEN: | |
199 return Classification.NONE; | |
200 | |
201 default: | |
202 return Classification.NONE; | |
203 } | |
204 } | |
OLD | NEW |