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