| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library services.src.correction.util; | 5 library services.src.correction.util; |
| 6 | 6 |
| 7 import 'dart:math'; | 7 import 'dart:math'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol.dart' show SourceChange, | 9 import 'package:analysis_server/src/protocol.dart' show SourceChange, |
| 10 SourceEdit; | 10 SourceEdit; |
| 11 import 'package:analysis_server/src/services/correction/source_range.dart'; | 11 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| 12 import 'package:analysis_server/src/services/correction/strings.dart'; | 12 import 'package:analysis_server/src/services/correction/strings.dart'; |
| 13 import 'package:analyzer/src/generated/ast.dart'; | 13 import 'package:analyzer/src/generated/ast.dart'; |
| 14 import 'package:analyzer/src/generated/element.dart'; | 14 import 'package:analyzer/src/generated/element.dart'; |
| 15 import 'package:analyzer/src/generated/engine.dart'; | 15 import 'package:analyzer/src/generated/engine.dart'; |
| 16 import 'package:analyzer/src/generated/resolver.dart'; | 16 import 'package:analyzer/src/generated/resolver.dart'; |
| 17 import 'package:analyzer/src/generated/scanner.dart'; | 17 import 'package:analyzer/src/generated/scanner.dart'; |
| 18 import 'package:analyzer/src/generated/source.dart'; | 18 import 'package:analyzer/src/generated/source.dart'; |
| 19 import 'package:path/path.dart'; |
| 19 | 20 |
| 20 | 21 |
| 21 /** | 22 /** |
| 22 * @return <code>true</code> if given [List]s are identical at given position. | 23 * @return <code>true</code> if given [List]s are identical at given position. |
| 23 */ | 24 */ |
| 24 bool allListsIdentical(List<List> lists, int position) { | 25 bool allListsIdentical(List<List> lists, int position) { |
| 25 Object element = lists[0][position]; | 26 Object element = lists[0][position]; |
| 26 for (List list in lists) { | 27 for (List list in lists) { |
| 27 if (list[position] != element) { | 28 if (list[position] != element) { |
| 28 return false; | 29 return false; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 45 if (parent is PropertyAccess && parent.propertyName == node) { | 46 if (parent is PropertyAccess && parent.propertyName == node) { |
| 46 node = parent; | 47 node = parent; |
| 47 continue; | 48 continue; |
| 48 } | 49 } |
| 49 return node; | 50 return node; |
| 50 } | 51 } |
| 51 } | 52 } |
| 52 | 53 |
| 53 | 54 |
| 54 /** | 55 /** |
| 56 * Attempts to convert the given absolute path into an absolute URI, such as |
| 57 * "dart" or "package" URI. |
| 58 * |
| 59 * [context] - the [AnalysisContext] to work in. |
| 60 * [path] - the absolute path, not `null`. |
| 61 * |
| 62 * Returns the absolute (non-file) URI or `null`. |
| 63 */ |
| 64 String findAbsoluteUri(AnalysisContext context, String path) { |
| 65 Source fileSource = new NonExistingSource(path, UriKind.FILE_URI); |
| 66 Uri uri = context.sourceFactory.restoreUri(fileSource); |
| 67 if (uri == null) { |
| 68 return null; |
| 69 } |
| 70 return uri.toString(); |
| 71 } |
| 72 |
| 73 |
| 74 /** |
| 55 * TODO(scheglov) replace with nodes once there will be [CompilationUnit#getComm
ents]. | 75 * TODO(scheglov) replace with nodes once there will be [CompilationUnit#getComm
ents]. |
| 56 * | 76 * |
| 57 * Returns [SourceRange]s of all comments in [unit]. | 77 * Returns [SourceRange]s of all comments in [unit]. |
| 58 */ | 78 */ |
| 59 List<SourceRange> getCommentRanges(CompilationUnit unit) { | 79 List<SourceRange> getCommentRanges(CompilationUnit unit) { |
| 60 List<SourceRange> ranges = <SourceRange>[]; | 80 List<SourceRange> ranges = <SourceRange>[]; |
| 61 Token token = unit.beginToken; | 81 Token token = unit.beginToken; |
| 62 while (token != null && token.type != TokenType.EOF) { | 82 while (token != null && token.type != TokenType.EOF) { |
| 63 Token commentToken = token.precedingComments; | 83 Token commentToken = token.precedingComments; |
| 64 while (commentToken != null) { | 84 while (commentToken != null) { |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 215 /** | 235 /** |
| 216 * Returns the namespace of the given [ImportElement]. | 236 * Returns the namespace of the given [ImportElement]. |
| 217 */ | 237 */ |
| 218 Map<String, Element> getImportNamespace(ImportElement imp) { | 238 Map<String, Element> getImportNamespace(ImportElement imp) { |
| 219 NamespaceBuilder builder = new NamespaceBuilder(); | 239 NamespaceBuilder builder = new NamespaceBuilder(); |
| 220 Namespace namespace = builder.createImportNamespaceForDirective(imp); | 240 Namespace namespace = builder.createImportNamespaceForDirective(imp); |
| 221 return namespace.definedNames; | 241 return namespace.definedNames; |
| 222 } | 242 } |
| 223 | 243 |
| 224 /** | 244 /** |
| 245 * Computes the best URI to import [what] into [from]. |
| 246 */ |
| 247 String getLibrarySourceUri(LibraryElement from, Source what) { |
| 248 String whatFile = what.fullName; |
| 249 // check if an absolute URI (such as 'dart:' or 'package:') |
| 250 Uri whatUri = what.uri; |
| 251 String whatUriScheme = whatUri.scheme; |
| 252 if (whatUriScheme != '' && whatUriScheme != 'file') { |
| 253 return whatUri.toString(); |
| 254 } |
| 255 // compute a relative URI |
| 256 String fromFolder = dirname(from.source.fullName); |
| 257 String relativeFile = relative(whatFile, from: fromFolder); |
| 258 return split(relativeFile).join('/'); |
| 259 } |
| 260 |
| 261 /** |
| 225 * Returns the line prefix from the given source, i.e. basically just a | 262 * Returns the line prefix from the given source, i.e. basically just a |
| 226 * whitespace prefix of the given [String]. | 263 * whitespace prefix of the given [String]. |
| 227 */ | 264 */ |
| 228 String getLinePrefix(String line) { | 265 String getLinePrefix(String line) { |
| 229 int index = 0; | 266 int index = 0; |
| 230 while (index < line.length) { | 267 while (index < line.length) { |
| 231 int c = line.codeUnitAt(index); | 268 int c = line.codeUnitAt(index); |
| 232 if (!isWhitespace(c)) { | 269 if (!isWhitespace(c)) { |
| 233 break; | 270 break; |
| 234 } | 271 } |
| 235 index++; | 272 index++; |
| 236 } | 273 } |
| 237 return line.substring(0, index); | 274 return line.substring(0, index); |
| 238 } | 275 } |
| 239 | 276 |
| 277 |
| 240 /** | 278 /** |
| 241 * @return the [LocalVariableElement] or [ParameterElement] if given | 279 * @return the [LocalVariableElement] or [ParameterElement] if given |
| 242 * [SimpleIdentifier] is the reference to local variable or parameter, o
r | 280 * [SimpleIdentifier] is the reference to local variable or parameter, o
r |
| 243 * <code>null</code> in the other case. | 281 * <code>null</code> in the other case. |
| 244 */ | 282 */ |
| 245 VariableElement getLocalOrParameterVariableElement(SimpleIdentifier node) { | 283 VariableElement getLocalOrParameterVariableElement(SimpleIdentifier node) { |
| 246 Element element = node.staticElement; | 284 Element element = node.staticElement; |
| 247 if (element is LocalVariableElement) { | 285 if (element is LocalVariableElement) { |
| 248 return element; | 286 return element; |
| 249 } | 287 } |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 } | 421 } |
| 384 if (parent is PropertyAccess) { | 422 if (parent is PropertyAccess) { |
| 385 PropertyAccess access = parent; | 423 PropertyAccess access = parent; |
| 386 if (access.propertyName == node) { | 424 if (access.propertyName == node) { |
| 387 return access.realTarget; | 425 return access.realTarget; |
| 388 } | 426 } |
| 389 } | 427 } |
| 390 return null; | 428 return null; |
| 391 } | 429 } |
| 392 | 430 |
| 393 | |
| 394 /** | 431 /** |
| 395 * Returns the given [Statement] if not a [Block], or the first child | 432 * Returns the given [Statement] if not a [Block], or the first child |
| 396 * [Statement] if a [Block], or `null` if more than one child. | 433 * [Statement] if a [Block], or `null` if more than one child. |
| 397 */ | 434 */ |
| 398 Statement getSingleStatement(Statement statement) { | 435 Statement getSingleStatement(Statement statement) { |
| 399 if (statement is Block) { | 436 if (statement is Block) { |
| 400 List<Statement> blockStatements = statement.statements; | 437 List<Statement> blockStatements = statement.statements; |
| 401 if (blockStatements.length != 1) { | 438 if (blockStatements.length != 1) { |
| 402 return null; | 439 return null; |
| 403 } | 440 } |
| 404 return blockStatements[0]; | 441 return blockStatements[0]; |
| 405 } | 442 } |
| 406 return statement; | 443 return statement; |
| 407 } | 444 } |
| 408 | 445 |
| 446 |
| 409 /** | 447 /** |
| 410 * Returns the [String] content of the given [Source]. | 448 * Returns the [String] content of the given [Source]. |
| 411 */ | 449 */ |
| 412 String getSourceContent(AnalysisContext context, Source source) { | 450 String getSourceContent(AnalysisContext context, Source source) { |
| 413 return context.getContents(source).data; | 451 return context.getContents(source).data; |
| 414 } | 452 } |
| 415 | 453 |
| 416 | 454 |
| 417 /** | 455 /** |
| 418 * Returns the given [Statement] if not a [Block], or all the children | 456 * Returns the given [Statement] if not a [Block], or all the children |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 /** | 572 /** |
| 535 * Returns the [AstNode] that encloses the given offset. | 573 * Returns the [AstNode] that encloses the given offset. |
| 536 */ | 574 */ |
| 537 AstNode findNode(int offset) => | 575 AstNode findNode(int offset) => |
| 538 new NodeLocator.con1(offset).searchWithin(unit); | 576 new NodeLocator.con1(offset).searchWithin(unit); |
| 539 | 577 |
| 540 /** | 578 /** |
| 541 * Returns the actual type source of the given [Expression], may be `null` | 579 * Returns the actual type source of the given [Expression], may be `null` |
| 542 * if can not be resolved, should be treated as the `dynamic` type. | 580 * if can not be resolved, should be treated as the `dynamic` type. |
| 543 */ | 581 */ |
| 544 String getExpressionTypeSource(Expression expression) { | 582 String getExpressionTypeSource(Expression expression, |
| 583 Set<LibraryElement> librariesToImport) { |
| 545 if (expression == null) { | 584 if (expression == null) { |
| 546 return null; | 585 return null; |
| 547 } | 586 } |
| 548 DartType type = expression.bestType; | 587 DartType type = expression.bestType; |
| 549 if (type.isDynamic) { | 588 if (type.isDynamic) { |
| 550 return null; | 589 return null; |
| 551 } | 590 } |
| 552 return getTypeSource(type); | 591 return getTypeSource(type, librariesToImport); |
| 553 } | 592 } |
| 554 | 593 |
| 555 /** | 594 /** |
| 556 * Returns the indentation with the given level. | 595 * Returns the indentation with the given level. |
| 557 */ | 596 */ |
| 558 String getIndent(int level) => repeat(' ', level); | 597 String getIndent(int level) => repeat(' ', level); |
| 559 | 598 |
| 560 /** | 599 /** |
| 561 * Returns a [InsertDesc] describing where to insert a new library-related | 600 * Returns a [InsertDesc] describing where to insert a new library-related |
| 562 * directive. | 601 * directive. |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 818 /** | 857 /** |
| 819 * Returns the text of the given [AstNode] in the unit. | 858 * Returns the text of the given [AstNode] in the unit. |
| 820 */ | 859 */ |
| 821 String getNodeText(AstNode node) { | 860 String getNodeText(AstNode node) { |
| 822 return getText(node.offset, node.length); | 861 return getText(node.offset, node.length); |
| 823 } | 862 } |
| 824 | 863 |
| 825 /** | 864 /** |
| 826 * @return the source for the parameter with the given type and name. | 865 * @return the source for the parameter with the given type and name. |
| 827 */ | 866 */ |
| 828 String getParameterSource(DartType type, String name) { | 867 String getParameterSource(DartType type, String name, |
| 868 Set<LibraryElement> librariesToImport) { |
| 829 // no type | 869 // no type |
| 830 if (type == null || type.isDynamic) { | 870 if (type == null || type.isDynamic) { |
| 831 return name; | 871 return name; |
| 832 } | 872 } |
| 833 // function type | 873 // function type |
| 834 if (type is FunctionType) { | 874 if (type is FunctionType) { |
| 835 FunctionType functionType = type; | 875 FunctionType functionType = type; |
| 836 StringBuffer sb = new StringBuffer(); | 876 StringBuffer sb = new StringBuffer(); |
| 837 // return type | 877 // return type |
| 838 DartType returnType = functionType.returnType; | 878 DartType returnType = functionType.returnType; |
| 839 if (returnType != null && !returnType.isDynamic) { | 879 if (returnType != null && !returnType.isDynamic) { |
| 840 sb.write(getTypeSource(returnType)); | 880 sb.write(getTypeSource(returnType, librariesToImport)); |
| 841 sb.write(' '); | 881 sb.write(' '); |
| 842 } | 882 } |
| 843 // parameter name | 883 // parameter name |
| 844 sb.write(name); | 884 sb.write(name); |
| 845 // parameters | 885 // parameters |
| 846 sb.write('('); | 886 sb.write('('); |
| 847 List<ParameterElement> fParameters = functionType.parameters; | 887 List<ParameterElement> fParameters = functionType.parameters; |
| 848 for (int i = 0; i < fParameters.length; i++) { | 888 for (int i = 0; i < fParameters.length; i++) { |
| 849 ParameterElement fParameter = fParameters[i]; | 889 ParameterElement fParameter = fParameters[i]; |
| 850 if (i != 0) { | 890 if (i != 0) { |
| 851 sb.write(", "); | 891 sb.write(", "); |
| 852 } | 892 } |
| 853 sb.write(getParameterSource(fParameter.type, fParameter.name)); | 893 sb.write( |
| 894 getParameterSource(fParameter.type, fParameter.name, librariesToImpo
rt)); |
| 854 } | 895 } |
| 855 sb.write(')'); | 896 sb.write(')'); |
| 856 // done | 897 // done |
| 857 return sb.toString(); | 898 return sb.toString(); |
| 858 } | 899 } |
| 859 // simple type | 900 // simple type |
| 860 return "${getTypeSource(type)} ${name}"; | 901 String typeSource = getTypeSource(type, librariesToImport); |
| 902 return '$typeSource $name'; |
| 861 } | 903 } |
| 862 | 904 |
| 863 /** | 905 /** |
| 864 * Returns the line prefix consisting of spaces and tabs on the left from the | 906 * Returns the line prefix consisting of spaces and tabs on the left from the |
| 865 * given offset. | 907 * given offset. |
| 866 */ | 908 */ |
| 867 String getPrefix(int endIndex) { | 909 String getPrefix(int endIndex) { |
| 868 int startIndex = getLineContentStart(endIndex); | 910 int startIndex = getLineContentStart(endIndex); |
| 869 return _buffer.substring(startIndex, endIndex); | 911 return _buffer.substring(startIndex, endIndex); |
| 870 } | 912 } |
| 871 | 913 |
| 872 /** | 914 /** |
| 873 * Returns the text of the given range in the unit. | 915 * Returns the text of the given range in the unit. |
| 874 */ | 916 */ |
| 875 String getRangeText(SourceRange range) { | 917 String getRangeText(SourceRange range) { |
| 876 return getText(range.offset, range.length); | 918 return getText(range.offset, range.length); |
| 877 } | 919 } |
| 878 | 920 |
| 879 /** | 921 /** |
| 880 * Returns the text of the given range in the unit. | 922 * Returns the text of the given range in the unit. |
| 881 */ | 923 */ |
| 882 String getText(int offset, int length) { | 924 String getText(int offset, int length) { |
| 883 return _buffer.substring(offset, offset + length); | 925 return _buffer.substring(offset, offset + length); |
| 884 } | 926 } |
| 885 | 927 |
| 886 /** | 928 /** |
| 887 * Returns the source to reference [type] in this [CompilationUnit]. | 929 * Returns the source to reference [type] in this [CompilationUnit]. |
| 930 * |
| 931 * Fills [librariesToImport] with [LibraryElement]s whose elements are |
| 932 * used by the generated source, but not imported. |
| 888 */ | 933 */ |
| 889 String getTypeSource(DartType type) { | 934 String getTypeSource(DartType type, Set<LibraryElement> librariesToImport) { |
| 890 StringBuffer sb = new StringBuffer(); | 935 StringBuffer sb = new StringBuffer(); |
| 891 // just a Function, not FunctionTypeAliasElement | 936 // just a Function, not FunctionTypeAliasElement |
| 892 if (type is FunctionType && type.element is! FunctionTypeAliasElement) { | 937 if (type is FunctionType && type.element is! FunctionTypeAliasElement) { |
| 893 return "Function"; | 938 return "Function"; |
| 894 } | 939 } |
| 895 // prepare element | 940 // prepare element |
| 896 Element element = type.element; | 941 Element element = type.element; |
| 897 if (element == null) { | 942 if (element == null) { |
| 898 String source = type.toString(); | 943 String source = type.toString(); |
| 899 source = source.replaceAll('<dynamic>', ''); | 944 source = source.replaceAll('<dynamic>', ''); |
| 900 source = source.replaceAll('<dynamic, dynamic>', ''); | 945 source = source.replaceAll('<dynamic, dynamic>', ''); |
| 901 return source; | 946 return source; |
| 902 } | 947 } |
| 903 // append prefix | 948 // check if imported |
| 904 { | 949 { |
| 905 ImportElement imp = _getImportElement(element); | 950 ImportElement importElement = _getImportElement(element); |
| 906 if (imp != null && imp.prefix != null) { | 951 if (importElement != null) { |
| 907 sb.write(imp.prefix.displayName); | 952 if (importElement.prefix != null) { |
| 908 sb.write("."); | 953 sb.write(importElement.prefix.displayName); |
| 954 sb.write("."); |
| 955 } |
| 956 } else { |
| 957 librariesToImport.add(element.library); |
| 909 } | 958 } |
| 910 } | 959 } |
| 911 // append simple name | 960 // append simple name |
| 912 String name = element.displayName; | 961 String name = element.displayName; |
| 913 sb.write(name); | 962 sb.write(name); |
| 914 // may be type arguments | 963 // may be type arguments |
| 915 if (type is ParameterizedType) { | 964 if (type is ParameterizedType) { |
| 916 List<DartType> arguments = type.typeArguments; | 965 List<DartType> arguments = type.typeArguments; |
| 917 // check if has arguments | 966 // check if has arguments |
| 918 bool hasArguments = false; | 967 bool hasArguments = false; |
| 919 for (DartType argument in arguments) { | 968 for (DartType argument in arguments) { |
| 920 if (!argument.isDynamic) { | 969 if (!argument.isDynamic) { |
| 921 hasArguments = true; | 970 hasArguments = true; |
| 922 break; | 971 break; |
| 923 } | 972 } |
| 924 } | 973 } |
| 925 // append type arguments | 974 // append type arguments |
| 926 if (hasArguments) { | 975 if (hasArguments) { |
| 927 sb.write("<"); | 976 sb.write("<"); |
| 928 for (int i = 0; i < arguments.length; i++) { | 977 for (int i = 0; i < arguments.length; i++) { |
| 929 DartType argument = arguments[i]; | 978 DartType argument = arguments[i]; |
| 930 if (i != 0) { | 979 if (i != 0) { |
| 931 sb.write(", "); | 980 sb.write(", "); |
| 932 } | 981 } |
| 933 sb.write(getTypeSource(argument)); | 982 String argumentSrc = getTypeSource(argument, librariesToImport); |
| 983 sb.write(argumentSrc); |
| 934 } | 984 } |
| 935 sb.write(">"); | 985 sb.write(">"); |
| 936 } | 986 } |
| 937 } | 987 } |
| 938 // done | 988 // done |
| 939 return sb.toString(); | 989 return sb.toString(); |
| 940 } | 990 } |
| 941 | 991 |
| 942 /** | 992 /** |
| 943 * Indents given source left or right. | 993 * Indents given source left or right. |
| (...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1400 | 1450 |
| 1401 @override | 1451 @override |
| 1402 Object visitExpression(Expression node) { | 1452 Object visitExpression(Expression node) { |
| 1403 if (node is BinaryExpression && node.operator.type == groupOperatorType) { | 1453 if (node is BinaryExpression && node.operator.type == groupOperatorType) { |
| 1404 return super.visitNode(node); | 1454 return super.visitNode(node); |
| 1405 } | 1455 } |
| 1406 operands.add(node); | 1456 operands.add(node); |
| 1407 return null; | 1457 return null; |
| 1408 } | 1458 } |
| 1409 } | 1459 } |
| OLD | NEW |