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