| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 services.src.correction.name_suggestion; | |
| 6 | |
| 7 import 'package:analysis_services/src/correction/strings.dart'; | |
| 8 import 'package:analyzer/src/generated/ast.dart'; | |
| 9 import 'package:analyzer/src/generated/element.dart'; | |
| 10 | |
| 11 | |
| 12 List<String> _KNOWN_METHOD_NAME_PREFIXES = ['get', 'is', 'to']; | |
| 13 | |
| 14 /** | |
| 15 * Returns a list of words for the given camel case string. | |
| 16 * | |
| 17 * 'getCamelWords' => ['get', 'Camel', 'Words'] | |
| 18 * 'getHTMLText' => ['get', 'HTML', 'Text'] | |
| 19 */ | |
| 20 List<String> getCamelWords(String str) { | |
| 21 if (str == null || str.isEmpty) { | |
| 22 return <String>[]; | |
| 23 } | |
| 24 List<String> parts = <String>[]; | |
| 25 bool wasLowerCase = false; | |
| 26 bool wasUpperCase = false; | |
| 27 int wordStart = 0; | |
| 28 for (int i = 0; i < str.length; i++) { | |
| 29 int c = str.codeUnitAt(i); | |
| 30 var newLowerCase = isLowerCase(c); | |
| 31 var newUpperCase = isUpperCase(c); | |
| 32 // myWord | |
| 33 // | ^ | |
| 34 if (wasLowerCase && newUpperCase) { | |
| 35 parts.add(str.substring(wordStart, i)); | |
| 36 wordStart = i; | |
| 37 } | |
| 38 // myHTMLText | |
| 39 // | ^ | |
| 40 if (wasUpperCase && | |
| 41 newUpperCase && | |
| 42 i + 1 < str.length && | |
| 43 isLowerCase(str.codeUnitAt(i + 1))) { | |
| 44 parts.add(str.substring(wordStart, i)); | |
| 45 wordStart = i; | |
| 46 } | |
| 47 wasLowerCase = newLowerCase; | |
| 48 wasUpperCase = newUpperCase; | |
| 49 } | |
| 50 parts.add(str.substring(wordStart)); | |
| 51 return parts; | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * Returns possible names for a [String] variable with [text] value. | |
| 56 */ | |
| 57 List<String> getVariableNameSuggestionsForText(String text, | |
| 58 Set<String> excluded) { | |
| 59 // filter out everything except of letters and white spaces | |
| 60 { | |
| 61 StringBuffer sb = new StringBuffer(); | |
| 62 for (int i = 0; i < text.length; i++) { | |
| 63 int c = text.codeUnitAt(i); | |
| 64 if (isLetter(c) || isWhitespace(c)) { | |
| 65 sb.writeCharCode(c); | |
| 66 } | |
| 67 } | |
| 68 text = sb.toString(); | |
| 69 } | |
| 70 // make single camel-case text | |
| 71 { | |
| 72 List<String> words = text.split(' '); | |
| 73 StringBuffer sb = new StringBuffer(); | |
| 74 for (int i = 0; i < words.length; i++) { | |
| 75 String word = words[i]; | |
| 76 if (i > 0) { | |
| 77 word = capitalize(word); | |
| 78 } | |
| 79 sb.write(word); | |
| 80 } | |
| 81 text = sb.toString(); | |
| 82 } | |
| 83 // split camel-case into separate suggested names | |
| 84 Set<String> res = new Set(); | |
| 85 _addAll(excluded, res, _getCamelWordCombinations(text)); | |
| 86 return new List.from(res); | |
| 87 } | |
| 88 | |
| 89 /** | |
| 90 * Returns possible names for a variable with the given expected type and | |
| 91 * expression assigned. | |
| 92 */ | |
| 93 List<String> getVariableNameSuggestionsForExpression(DartType expectedType, | |
| 94 Expression assignedExpression, Set<String> excluded) { | |
| 95 Set<String> res = new Set(); | |
| 96 // use expression | |
| 97 if (assignedExpression != null) { | |
| 98 String nameFromExpression = _getBaseNameFromExpression(assignedExpression); | |
| 99 if (nameFromExpression != null) { | |
| 100 nameFromExpression = removeStart(nameFromExpression, '_'); | |
| 101 _addAll(excluded, res, _getCamelWordCombinations(nameFromExpression)); | |
| 102 } | |
| 103 String nameFromParent = | |
| 104 _getBaseNameFromLocationInParent(assignedExpression); | |
| 105 if (nameFromParent != null) { | |
| 106 _addAll(excluded, res, _getCamelWordCombinations(nameFromParent)); | |
| 107 } | |
| 108 } | |
| 109 // use type | |
| 110 if (expectedType != null && !expectedType.isDynamic) { | |
| 111 String typeName = expectedType.name; | |
| 112 if ('int' == typeName) { | |
| 113 _addSingleCharacterName(excluded, res, 0x69); | |
| 114 } else if ('double' == typeName) { | |
| 115 _addSingleCharacterName(excluded, res, 0x64); | |
| 116 } else if ('String' == typeName) { | |
| 117 _addSingleCharacterName(excluded, res, 0x73); | |
| 118 } else { | |
| 119 _addAll(excluded, res, _getCamelWordCombinations(typeName)); | |
| 120 } | |
| 121 res.remove(typeName); | |
| 122 } | |
| 123 // done | |
| 124 return new List.from(res); | |
| 125 } | |
| 126 | |
| 127 /** | |
| 128 * Adds [toAdd] items which are not excluded. | |
| 129 */ | |
| 130 void _addAll(Set<String> excluded, Set<String> result, Iterable<String> toAdd) { | |
| 131 for (String item in toAdd) { | |
| 132 // add name based on "item", but not "excluded" | |
| 133 for (int suffix = 1; ; suffix++) { | |
| 134 // prepare name, just "item" or "item2", "item3", etc | |
| 135 String name = item; | |
| 136 if (suffix > 1) { | |
| 137 name += suffix.toString(); | |
| 138 } | |
| 139 // add once found not excluded | |
| 140 if (!excluded.contains(name)) { | |
| 141 result.add(name); | |
| 142 break; | |
| 143 } | |
| 144 } | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 /** | |
| 149 * Adds to [result] either [c] or the first ASCII character after it. | |
| 150 */ | |
| 151 void _addSingleCharacterName(Set<String> excluded, Set<String> result, int c) { | |
| 152 while (c < 0x7A) { | |
| 153 String name = new String.fromCharCode(c); | |
| 154 // may be done | |
| 155 if (!excluded.contains(name)) { | |
| 156 result.add(name); | |
| 157 break; | |
| 158 } | |
| 159 // next character | |
| 160 c = c + 1; | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 String _getBaseNameFromExpression(Expression expression) { | |
| 165 String name = null; | |
| 166 // e as Type | |
| 167 if (expression is AsExpression) { | |
| 168 AsExpression asExpression = expression as AsExpression; | |
| 169 expression = asExpression.expression; | |
| 170 } | |
| 171 // analyze expressions | |
| 172 if (expression is SimpleIdentifier) { | |
| 173 SimpleIdentifier node = expression; | |
| 174 return node.name; | |
| 175 } else if (expression is PrefixedIdentifier) { | |
| 176 PrefixedIdentifier node = expression; | |
| 177 return node.identifier.name; | |
| 178 } else if (expression is MethodInvocation) { | |
| 179 name = expression.methodName.name; | |
| 180 } else if (expression is InstanceCreationExpression) { | |
| 181 InstanceCreationExpression creation = expression; | |
| 182 ConstructorName constructorName = creation.constructorName; | |
| 183 TypeName typeName = constructorName.type; | |
| 184 if (typeName != null) { | |
| 185 Identifier typeNameIdentifier = typeName.name; | |
| 186 // new ClassName() | |
| 187 if (typeNameIdentifier is SimpleIdentifier) { | |
| 188 return typeNameIdentifier.name; | |
| 189 } | |
| 190 // new prefix.name(); | |
| 191 if (typeNameIdentifier is PrefixedIdentifier) { | |
| 192 PrefixedIdentifier prefixed = typeNameIdentifier; | |
| 193 // new prefix.ClassName() | |
| 194 if (prefixed.prefix.staticElement is PrefixElement) { | |
| 195 return prefixed.identifier.name; | |
| 196 } | |
| 197 // new ClassName.constructorName() | |
| 198 return prefixed.prefix.name; | |
| 199 } | |
| 200 } | |
| 201 } | |
| 202 // strip known prefixes | |
| 203 if (name != null) { | |
| 204 for (int i = 0; i < _KNOWN_METHOD_NAME_PREFIXES.length; i++) { | |
| 205 String curr = _KNOWN_METHOD_NAME_PREFIXES[i]; | |
| 206 if (name.startsWith(curr)) { | |
| 207 if (name == curr) { | |
| 208 return null; | |
| 209 } else if (isUpperCase(name.codeUnitAt(curr.length))) { | |
| 210 return name.substring(curr.length); | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 } | |
| 215 // done | |
| 216 return name; | |
| 217 } | |
| 218 | |
| 219 | |
| 220 String _getBaseNameFromLocationInParent(Expression expression) { | |
| 221 // value in named expression | |
| 222 if (expression.parent is NamedExpression) { | |
| 223 NamedExpression namedExpression = expression.parent as NamedExpression; | |
| 224 if (namedExpression.expression == expression) { | |
| 225 return namedExpression.name.label.name; | |
| 226 } | |
| 227 } | |
| 228 // positional argument | |
| 229 { | |
| 230 ParameterElement parameter = expression.propagatedParameterElement; | |
| 231 if (parameter == null) { | |
| 232 parameter = expression.staticParameterElement; | |
| 233 } | |
| 234 if (parameter != null) { | |
| 235 return parameter.displayName; | |
| 236 } | |
| 237 } | |
| 238 // unknown | |
| 239 return null; | |
| 240 } | |
| 241 | |
| 242 /** | |
| 243 * Returns all variants of names by removing leading words one by one. | |
| 244 */ | |
| 245 List<String> _getCamelWordCombinations(String name) { | |
| 246 List<String> result = []; | |
| 247 List<String> parts = getCamelWords(name); | |
| 248 for (int i = 0; i < parts.length; i++) { | |
| 249 var s1 = parts[i].toLowerCase(); | |
| 250 var s2 = parts.skip(i + 1).join(); | |
| 251 String suggestion = '$s1$s2'; | |
| 252 result.add(suggestion); | |
| 253 } | |
| 254 return result; | |
| 255 } | |
| OLD | NEW |