| 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 |