| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 | 6 |
| 7 import 'package:analysis_server/protocol/protocol_generated.dart' | 7 import 'package:analysis_server/protocol/protocol_generated.dart' |
| 8 hide Element, ElementKind; | 8 hide Element, ElementKind; |
| 9 import 'package:analysis_server/src/provisional/edit/utilities/change_builder_co
re.dart'; | 9 import 'package:analysis_server/src/provisional/edit/utilities/change_builder_co
re.dart'; |
| 10 import 'package:analysis_server/src/provisional/edit/utilities/change_builder_da
rt.dart'; | 10 import 'package:analysis_server/src/provisional/edit/utilities/change_builder_da
rt.dart'; |
| 11 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; | 11 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; |
| 12 import 'package:analysis_server/src/services/correction/source_range.dart'; | 12 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| 13 import 'package:analysis_server/src/services/correction/util.dart'; | 13 import 'package:analysis_server/src/services/correction/util.dart'; |
| 14 import 'package:analysis_server/src/utilities/change_builder_core.dart'; | 14 import 'package:analysis_server/src/utilities/change_builder_core.dart'; |
| 15 import 'package:analyzer/dart/ast/ast.dart'; | 15 import 'package:analyzer/dart/ast/ast.dart'; |
| 16 import 'package:analyzer/dart/ast/token.dart'; | 16 import 'package:analyzer/dart/ast/token.dart'; |
| 17 import 'package:analyzer/dart/element/element.dart'; | 17 import 'package:analyzer/dart/element/element.dart'; |
| 18 import 'package:analyzer/dart/element/type.dart'; | 18 import 'package:analyzer/dart/element/type.dart'; |
| 19 import 'package:analyzer/src/dart/analysis/driver.dart'; | 19 import 'package:analyzer/src/dart/analysis/driver.dart'; |
| 20 import 'package:analyzer/src/dart/ast/utilities.dart'; |
| 21 import 'package:analyzer/src/generated/engine.dart' hide AnalysisResult; |
| 20 import 'package:analyzer/src/generated/resolver.dart'; | 22 import 'package:analyzer/src/generated/resolver.dart'; |
| 21 import 'package:analyzer/src/generated/source.dart'; | 23 import 'package:analyzer/src/generated/source.dart'; |
| 22 import 'package:analyzer/src/generated/utilities_dart.dart'; | 24 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 23 | 25 |
| 24 /** | 26 /** |
| 25 * A [ChangeBuilder] used to build changes in Dart files. | 27 * A [ChangeBuilder] used to build changes in Dart files. |
| 26 */ | 28 */ |
| 27 class DartChangeBuilderImpl extends ChangeBuilderImpl | 29 class DartChangeBuilderImpl extends ChangeBuilderImpl |
| 28 implements DartChangeBuilder { | 30 implements DartChangeBuilder { |
| 29 /** | 31 /** |
| (...skipping 12 matching lines...) Expand all Loading... |
| 42 AnalysisResult result = await driver.getResult(path); | 44 AnalysisResult result = await driver.getResult(path); |
| 43 return new DartFileEditBuilderImpl(this, path, fileStamp, result.unit); | 45 return new DartFileEditBuilderImpl(this, path, fileStamp, result.unit); |
| 44 } | 46 } |
| 45 } | 47 } |
| 46 | 48 |
| 47 /** | 49 /** |
| 48 * An [EditBuilder] used to build edits in Dart files. | 50 * An [EditBuilder] used to build edits in Dart files. |
| 49 */ | 51 */ |
| 50 class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { | 52 class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { |
| 51 /** | 53 /** |
| 52 * A utility class used to help build the source code. | |
| 53 */ | |
| 54 final CorrectionUtils utils; | |
| 55 | |
| 56 /** | |
| 57 * Initialize a newly created builder to build a source edit. | 54 * Initialize a newly created builder to build a source edit. |
| 58 */ | 55 */ |
| 59 DartEditBuilderImpl( | 56 DartEditBuilderImpl( |
| 60 DartFileEditBuilderImpl sourceFileEditBuilder, int offset, int length) | 57 DartFileEditBuilderImpl sourceFileEditBuilder, int offset, int length) |
| 61 : utils = sourceFileEditBuilder.utils, | 58 : super(sourceFileEditBuilder, offset, length); |
| 62 super(sourceFileEditBuilder, offset, length); | |
| 63 | 59 |
| 64 DartFileEditBuilderImpl get dartFileEditBuilder => fileEditBuilder; | 60 DartFileEditBuilderImpl get dartFileEditBuilder => fileEditBuilder; |
| 65 | 61 |
| 66 @override | 62 @override |
| 67 void set targetClassElement(ClassElement element) { | |
| 68 utils.targetClassElement = element; | |
| 69 } | |
| 70 | |
| 71 @override | |
| 72 LinkedEditBuilderImpl createLinkedEditBuilder() { | 63 LinkedEditBuilderImpl createLinkedEditBuilder() { |
| 73 return new DartLinkedEditBuilderImpl(this); | 64 return new DartLinkedEditBuilderImpl(this); |
| 74 } | 65 } |
| 75 | 66 |
| 76 /** | 67 /** |
| 77 * Returns the indentation with the given [level]. | 68 * Returns the indentation with the given [level]. |
| 78 */ | 69 */ |
| 79 String getIndent(int level) => ' ' * level; | 70 String getIndent(int level) => ' ' * level; |
| 80 | 71 |
| 81 // /** | |
| 82 // * Arrange to have imports added for each of the given [libraries]. | |
| 83 // */ | |
| 84 // void importLibraries(Iterable<Source> libraries) { | |
| 85 // dartFileEditBuilder.importLibraries(libraries); | |
| 86 // } | |
| 87 | |
| 88 /** | 72 /** |
| 89 * Arrange to have an import added for the given [library]. | 73 * Arrange to have an import added for the given [library]. |
| 90 */ | 74 */ |
| 91 void importLibrary(Source library) { | 75 void importLibrary(Source library) { |
| 92 dartFileEditBuilder.librariesToImport.add(library); | 76 dartFileEditBuilder.librariesToImport.add(library); |
| 93 } | 77 } |
| 94 | 78 |
| 95 @override | 79 @override |
| 96 void writeClassDeclaration(String name, | 80 void writeClassDeclaration(String name, |
| 97 {Iterable<DartType> interfaces, | 81 {Iterable<DartType> interfaces, |
| (...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 } | 451 } |
| 468 writeParameterMatchingArgument(argument, i, usedNames); | 452 writeParameterMatchingArgument(argument, i, usedNames); |
| 469 } | 453 } |
| 470 if (hasNamedParameters) { | 454 if (hasNamedParameters) { |
| 471 write('}'); | 455 write('}'); |
| 472 } | 456 } |
| 473 } | 457 } |
| 474 | 458 |
| 475 @override | 459 @override |
| 476 void writeParameterSource(DartType type, String name) { | 460 void writeParameterSource(DartType type, String name) { |
| 477 String parameterSource = utils.getParameterSource( | 461 _EnclosingElementFinder finder = new _EnclosingElementFinder(); |
| 478 type, name, dartFileEditBuilder.librariesToImport); | 462 finder.find(dartFileEditBuilder.unit, offset); |
| 463 String parameterSource = _getParameterSource( |
| 464 type, name, finder.enclosingClass, finder.enclosingExecutable); |
| 479 write(parameterSource); | 465 write(parameterSource); |
| 480 } | 466 } |
| 481 | 467 |
| 482 @override | 468 @override |
| 483 bool writeType(DartType type, | 469 bool writeType(DartType type, |
| 484 {bool addSupertypeProposals: false, | 470 {bool addSupertypeProposals: false, |
| 485 String groupName, | 471 String groupName, |
| 486 bool required: false}) { | 472 bool required: false}) { |
| 487 if (type != null && !type.isDynamic) { | 473 if (type != null && !type.isDynamic) { |
| 488 String typeSource = | 474 _EnclosingElementFinder finder = new _EnclosingElementFinder(); |
| 489 utils.getTypeSource(type, dartFileEditBuilder.librariesToImport); | 475 finder.find(dartFileEditBuilder.unit, offset); |
| 476 String typeSource = _getTypeSource( |
| 477 type, finder.enclosingClass, finder.enclosingExecutable); |
| 490 if (typeSource != 'dynamic') { | 478 if (typeSource != 'dynamic') { |
| 491 if (groupName != null) { | 479 if (groupName != null) { |
| 492 addLinkedEdit(groupName, (LinkedEditBuilder builder) { | 480 addLinkedEdit(groupName, (LinkedEditBuilder builder) { |
| 493 write(typeSource); | 481 write(typeSource); |
| 494 if (addSupertypeProposals) { | 482 if (addSupertypeProposals) { |
| 495 _addSuperTypeProposals(builder, type, new Set<DartType>()); | 483 _addSuperTypeProposals(builder, type, new Set<DartType>()); |
| 496 } | 484 } |
| 497 }); | 485 }); |
| 498 } else { | 486 } else { |
| 499 write(typeSource); | 487 write(typeSource); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 ClassElement element = type.element as ClassElement; | 552 ClassElement element = type.element as ClassElement; |
| 565 builder.addSuggestion(LinkedEditSuggestionKind.TYPE, element.name); | 553 builder.addSuggestion(LinkedEditSuggestionKind.TYPE, element.name); |
| 566 _addSuperTypeProposals(builder, element.supertype, alreadyAdded); | 554 _addSuperTypeProposals(builder, element.supertype, alreadyAdded); |
| 567 for (InterfaceType interfaceType in element.interfaces) { | 555 for (InterfaceType interfaceType in element.interfaces) { |
| 568 _addSuperTypeProposals(builder, interfaceType, alreadyAdded); | 556 _addSuperTypeProposals(builder, interfaceType, alreadyAdded); |
| 569 } | 557 } |
| 570 } | 558 } |
| 571 } | 559 } |
| 572 | 560 |
| 573 /** | 561 /** |
| 562 * Return the import element used to import the given [element] into the given |
| 563 * [library], or `null` if the element was not imported, such as when the |
| 564 * element is declared in the same library. |
| 565 */ |
| 566 ImportElement _getImportElement(Element element, LibraryElement library) { |
| 567 for (ImportElement imp in library.imports) { |
| 568 Map<String, Element> definedNames = getImportNamespace(imp); |
| 569 if (definedNames.containsValue(element)) { |
| 570 return imp; |
| 571 } |
| 572 } |
| 573 return null; |
| 574 } |
| 575 |
| 576 /** |
| 574 * Return a list containing the suggested names for a parameter with the given | 577 * Return a list containing the suggested names for a parameter with the given |
| 575 * [type] whose value in one location is computed by the given [expression]. | 578 * [type] whose value in one location is computed by the given [expression]. |
| 576 * The list will not contain any names in the set of [excluded] names. The | 579 * The list will not contain any names in the set of [excluded] names. The |
| 577 * [index] is the index of the argument, used to create a name if no better | 580 * [index] is the index of the argument, used to create a name if no better |
| 578 * name could be created. The first name in the list will be the best name. | 581 * name could be created. The first name in the list will be the best name. |
| 579 */ | 582 */ |
| 580 List<String> _getParameterNameSuggestions( | 583 List<String> _getParameterNameSuggestions( |
| 581 Set<String> usedNames, DartType type, Expression expression, int index) { | 584 Set<String> usedNames, DartType type, Expression expression, int index) { |
| 582 List<String> suggestions = | 585 List<String> suggestions = |
| 583 getVariableNameSuggestionsForExpression(type, expression, usedNames); | 586 getVariableNameSuggestionsForExpression(type, expression, usedNames); |
| 584 if (suggestions.length != 0) { | 587 if (suggestions.length != 0) { |
| 585 return suggestions; | 588 return suggestions; |
| 586 } | 589 } |
| 587 // TODO(brianwilkerson) Verify that the name below is not in the set of used
names. | 590 // TODO(brianwilkerson) Verify that the name below is not in the set of used
names. |
| 588 return <String>['param$index']; | 591 return <String>['param$index']; |
| 589 } | 592 } |
| 593 |
| 594 /** |
| 595 * Return the source for the parameter with the given [type] and [name]. |
| 596 */ |
| 597 String _getParameterSource(DartType type, String name, |
| 598 ClassElement enclosingClass, ExecutableElement enclosingExecutable) { |
| 599 // no type |
| 600 if (type == null || type.isDynamic) { |
| 601 return name; |
| 602 } |
| 603 // function type |
| 604 if (type is FunctionType && type.element.isSynthetic) { |
| 605 FunctionType functionType = type; |
| 606 StringBuffer sb = new StringBuffer(); |
| 607 // return type |
| 608 DartType returnType = functionType.returnType; |
| 609 if (returnType != null && !returnType.isDynamic) { |
| 610 String returnTypeSource = |
| 611 _getTypeSource(returnType, enclosingClass, enclosingExecutable); |
| 612 sb.write(returnTypeSource); |
| 613 sb.write(' '); |
| 614 } |
| 615 // parameter name |
| 616 sb.write(name); |
| 617 // parameters |
| 618 sb.write('('); |
| 619 List<ParameterElement> fParameters = functionType.parameters; |
| 620 for (int i = 0; i < fParameters.length; i++) { |
| 621 ParameterElement fParameter = fParameters[i]; |
| 622 if (i != 0) { |
| 623 sb.write(", "); |
| 624 } |
| 625 sb.write(_getParameterSource(fParameter.type, fParameter.name, |
| 626 enclosingClass, enclosingExecutable)); |
| 627 } |
| 628 sb.write(')'); |
| 629 // done |
| 630 return sb.toString(); |
| 631 } |
| 632 // simple type |
| 633 String typeSource = |
| 634 _getTypeSource(type, enclosingClass, enclosingExecutable); |
| 635 return '$typeSource $name'; |
| 636 } |
| 637 |
| 638 /** |
| 639 * Returns the source to reference [type] in this [CompilationUnit]. |
| 640 * |
| 641 * Fills [librariesToImport] with [LibraryElement]s whose elements are |
| 642 * used by the generated source, but not imported. |
| 643 */ |
| 644 String _getTypeSource(DartType type, ClassElement enclosingClass, |
| 645 ExecutableElement enclosingExecutable, |
| 646 {StringBuffer parametersBuffer}) { |
| 647 StringBuffer sb = new StringBuffer(); |
| 648 // type parameter |
| 649 if (!_isTypeVisible(type, enclosingClass, enclosingExecutable)) { |
| 650 return 'dynamic'; |
| 651 } |
| 652 // just a Function, not FunctionTypeAliasElement |
| 653 if (type is FunctionType && type.element is! FunctionTypeAliasElement) { |
| 654 if (parametersBuffer == null) { |
| 655 return "Function"; |
| 656 } |
| 657 parametersBuffer.write('('); |
| 658 for (ParameterElement parameter in type.parameters) { |
| 659 String parameterType = |
| 660 _getTypeSource(parameter.type, enclosingClass, enclosingExecutable); |
| 661 if (parametersBuffer.length != 1) { |
| 662 parametersBuffer.write(', '); |
| 663 } |
| 664 parametersBuffer.write(parameterType); |
| 665 parametersBuffer.write(' '); |
| 666 parametersBuffer.write(parameter.name); |
| 667 } |
| 668 parametersBuffer.write(')'); |
| 669 return _getTypeSource( |
| 670 type.returnType, enclosingClass, enclosingExecutable); |
| 671 } |
| 672 // <Bottom>, Null |
| 673 if (type.isBottom || type.isDartCoreNull) { |
| 674 return 'dynamic'; |
| 675 } |
| 676 // prepare element |
| 677 Element element = type.element; |
| 678 if (element == null) { |
| 679 String source = type.toString(); |
| 680 source = source.replaceAll('<dynamic>', ''); |
| 681 source = source.replaceAll('<dynamic, dynamic>', ''); |
| 682 return source; |
| 683 } |
| 684 // check if imported |
| 685 LibraryElement definingLibrary = element.library; |
| 686 LibraryElement importingLibrary = dartFileEditBuilder.unit.element.library; |
| 687 if (definingLibrary != null && definingLibrary != importingLibrary) { |
| 688 // no source, if private |
| 689 if (element.isPrivate) { |
| 690 return null; |
| 691 } |
| 692 // ensure import |
| 693 ImportElement importElement = |
| 694 _getImportElement(element, importingLibrary); |
| 695 if (importElement != null) { |
| 696 if (importElement.prefix != null) { |
| 697 sb.write(importElement.prefix.displayName); |
| 698 sb.write("."); |
| 699 } |
| 700 } else { |
| 701 importLibrary(definingLibrary.source); |
| 702 } |
| 703 } |
| 704 // append simple name |
| 705 String name = element.displayName; |
| 706 sb.write(name); |
| 707 // may be type arguments |
| 708 if (type is ParameterizedType) { |
| 709 List<DartType> arguments = type.typeArguments; |
| 710 // check if has arguments |
| 711 bool hasArguments = false; |
| 712 bool allArgumentsVisible = true; |
| 713 for (DartType argument in arguments) { |
| 714 hasArguments = hasArguments || !argument.isDynamic; |
| 715 allArgumentsVisible = allArgumentsVisible && |
| 716 _isTypeVisible(argument, enclosingClass, enclosingExecutable); |
| 717 } |
| 718 // append type arguments |
| 719 if (hasArguments && allArgumentsVisible) { |
| 720 sb.write("<"); |
| 721 for (int i = 0; i < arguments.length; i++) { |
| 722 DartType argument = arguments[i]; |
| 723 if (i != 0) { |
| 724 sb.write(", "); |
| 725 } |
| 726 String argumentSrc = |
| 727 _getTypeSource(argument, enclosingClass, enclosingExecutable); |
| 728 if (argumentSrc != null) { |
| 729 sb.write(argumentSrc); |
| 730 } else { |
| 731 return null; |
| 732 } |
| 733 } |
| 734 sb.write(">"); |
| 735 } |
| 736 } |
| 737 // done |
| 738 return sb.toString(); |
| 739 } |
| 740 |
| 741 /** |
| 742 * Checks if [type] is visible in either the [enclosingExecutable] or |
| 743 * [enclosingClass]. |
| 744 */ |
| 745 bool _isTypeVisible(DartType type, ClassElement enclosingClass, |
| 746 ExecutableElement enclosingExecutable) { |
| 747 if (type is TypeParameterType) { |
| 748 TypeParameterElement parameterElement = type.element; |
| 749 Element parameterParent = parameterElement.enclosingElement; |
| 750 // TODO(brianwilkerson) This needs to compare the parameterParent with |
| 751 // each of the parents of the enclosingElement. (That means that we only |
| 752 // need the most closely enclosing element.) |
| 753 return identical(parameterParent, enclosingExecutable) || |
| 754 identical(parameterParent, enclosingClass); |
| 755 } |
| 756 return true; |
| 757 } |
| 590 } | 758 } |
| 591 | 759 |
| 592 /** | 760 /** |
| 593 * A [FileEditBuilder] used to build edits for Dart files. | 761 * A [FileEditBuilder] used to build edits for Dart files. |
| 594 */ | 762 */ |
| 595 class DartFileEditBuilderImpl extends FileEditBuilderImpl | 763 class DartFileEditBuilderImpl extends FileEditBuilderImpl |
| 596 implements DartFileEditBuilder { | 764 implements DartFileEditBuilder { |
| 597 /** | 765 /** |
| 598 * The compilation unit to which the code will be added. | 766 * The compilation unit to which the code will be added. |
| 599 */ | 767 */ |
| 600 CompilationUnit unit; | 768 CompilationUnit unit; |
| 601 | 769 |
| 602 /** | 770 /** |
| 603 * A utility class used to help build the source code. | |
| 604 */ | |
| 605 CorrectionUtils utils; | |
| 606 | |
| 607 /** | |
| 608 * A set containing the sources of the libraries that need to be imported in | 771 * A set containing the sources of the libraries that need to be imported in |
| 609 * order to make visible the names used in generated code. | 772 * order to make visible the names used in generated code. |
| 610 */ | 773 */ |
| 611 Set<Source> librariesToImport = new Set<Source>(); | 774 Set<Source> librariesToImport = new Set<Source>(); |
| 612 | 775 |
| 613 /** | 776 /** |
| 777 * The content of the file being edited. |
| 778 */ |
| 779 String _content; |
| 780 |
| 781 /** |
| 614 * Initialize a newly created builder to build a source file edit within the | 782 * Initialize a newly created builder to build a source file edit within the |
| 615 * change being built by the given [changeBuilder]. The file being edited has | 783 * change being built by the given [changeBuilder]. The file being edited has |
| 616 * the given [source] and [timeStamp], and the given fully resolved [unit]. | 784 * the given [source] and [timeStamp], and the given fully resolved [unit]. |
| 617 */ | 785 */ |
| 618 DartFileEditBuilderImpl(DartChangeBuilderImpl changeBuilder, String path, | 786 DartFileEditBuilderImpl(DartChangeBuilderImpl changeBuilder, String path, |
| 619 int timeStamp, this.unit) | 787 int timeStamp, this.unit) |
| 620 : super(changeBuilder, path, timeStamp) { | 788 : super(changeBuilder, path, timeStamp); |
| 621 utils = new CorrectionUtils(unit); | |
| 622 } | |
| 623 | 789 |
| 624 @override | 790 @override |
| 625 void convertFunctionFromSyncToAsync( | 791 void convertFunctionFromSyncToAsync( |
| 626 FunctionBody body, TypeProvider typeProvider) { | 792 FunctionBody body, TypeProvider typeProvider) { |
| 627 if (body == null && body.keyword != null) { | 793 if (body == null && body.keyword != null) { |
| 628 throw new ArgumentError( | 794 throw new ArgumentError( |
| 629 'The function must have a synchronous, non-generator body.'); | 795 'The function must have a synchronous, non-generator body.'); |
| 630 } | 796 } |
| 631 addInsertion(body.offset, (EditBuilder builder) { | 797 addInsertion(body.offset, (EditBuilder builder) { |
| 632 builder.write('async '); | 798 builder.write('async '); |
| 633 }); | 799 }); |
| 634 _replaceReturnTypeWithFuture(body, typeProvider); | 800 _replaceReturnTypeWithFuture(body, typeProvider); |
| 635 } | 801 } |
| 636 | 802 |
| 637 @override | 803 @override |
| 638 DartEditBuilderImpl createEditBuilder(int offset, int length) { | 804 DartEditBuilderImpl createEditBuilder(int offset, int length) { |
| 639 return new DartEditBuilderImpl(this, offset, length); | 805 return new DartEditBuilderImpl(this, offset, length); |
| 640 } | 806 } |
| 641 | 807 |
| 642 @override | 808 @override |
| 643 void finalize() { | 809 void finalize() { |
| 644 addLibraryImports( | 810 addLibraryImports( |
| 645 changeBuilder.sourceChange, unit.element.library, librariesToImport); | 811 changeBuilder.sourceChange, unit.element.library, librariesToImport); |
| 646 } | 812 } |
| 647 | 813 |
| 814 /** |
| 815 * Return the content of the file being edited. |
| 816 */ |
| 817 String getContent() { |
| 818 if (_content == null) { |
| 819 CompilationUnitElement unitElement = unit.element; |
| 820 AnalysisContext context = unitElement.context; |
| 821 if (context == null) { |
| 822 throw new CancelCorrectionException(); |
| 823 } |
| 824 _content = context.getContents(unitElement.source).data; |
| 825 } |
| 826 return _content; |
| 827 } |
| 828 |
| 648 @override | 829 @override |
| 649 void importLibraries(Iterable<Source> libraries) { | 830 void importLibraries(Iterable<Source> libraries) { |
| 650 librariesToImport.addAll(libraries); | 831 librariesToImport.addAll(libraries); |
| 651 } | 832 } |
| 652 | 833 |
| 653 @override | 834 @override |
| 654 void replaceTypeWithFuture( | 835 void replaceTypeWithFuture( |
| 655 TypeAnnotation typeAnnotation, TypeProvider typeProvider) { | 836 TypeAnnotation typeAnnotation, TypeProvider typeProvider) { |
| 656 InterfaceType futureType = typeProvider.futureType; | 837 InterfaceType futureType = typeProvider.futureType; |
| 657 // | 838 // |
| 658 // Check whether the type needs to be replaced. | 839 // Check whether the type needs to be replaced. |
| 659 // | 840 // |
| 660 DartType type = typeAnnotation?.type; | 841 DartType type = typeAnnotation?.type; |
| 661 if (type == null || | 842 if (type == null || |
| 662 type.isDynamic || | 843 type.isDynamic || |
| 663 type is InterfaceType && type.element == futureType.element) { | 844 type is InterfaceType && type.element == futureType.element) { |
| 664 return; | 845 return; |
| 665 } | 846 } |
| 847 futureType = futureType.instantiate(<DartType>[type]); |
| 666 // prepare code for the types | 848 // prepare code for the types |
| 667 String futureTypeCode = utils.getTypeSource(futureType, librariesToImport); | |
| 668 String nodeCode = utils.getNodeText(typeAnnotation); | |
| 669 // wrap the existing type with Future | |
| 670 String returnTypeCode = | |
| 671 nodeCode == 'void' ? futureTypeCode : '$futureTypeCode<$nodeCode>'; | |
| 672 addReplacement(rangeNode(typeAnnotation), (EditBuilder builder) { | 849 addReplacement(rangeNode(typeAnnotation), (EditBuilder builder) { |
| 673 builder.write(returnTypeCode); | 850 if (!(builder as DartEditBuilder).writeType(futureType)) { |
| 851 builder.write('void'); |
| 852 } |
| 674 }); | 853 }); |
| 675 } | 854 } |
| 676 | 855 |
| 677 /** | 856 /** |
| 857 * Returns the text of the given [AstNode] in the unit. |
| 858 */ |
| 859 String _getNodeText(AstNode node) { |
| 860 return _getText(node.offset, node.length); |
| 861 } |
| 862 |
| 863 /** |
| 864 * Returns the text of the given range in the unit. |
| 865 */ |
| 866 String _getText(int offset, int length) { |
| 867 return getContent().substring(offset, offset + length); |
| 868 } |
| 869 |
| 870 /** |
| 678 * Create an edit to replace the return type of the innermost function | 871 * Create an edit to replace the return type of the innermost function |
| 679 * containing the given [node] with the type `Future`. The [typeProvider] is | 872 * containing the given [node] with the type `Future`. The [typeProvider] is |
| 680 * used to check the current return type, because if it is already `Future` no | 873 * used to check the current return type, because if it is already `Future` no |
| 681 * edit will be added. | 874 * edit will be added. |
| 682 */ | 875 */ |
| 683 void _replaceReturnTypeWithFuture(AstNode node, TypeProvider typeProvider) { | 876 void _replaceReturnTypeWithFuture(AstNode node, TypeProvider typeProvider) { |
| 684 while (node != null) { | 877 while (node != null) { |
| 685 node = node.parent; | 878 node = node.parent; |
| 686 if (node is FunctionDeclaration) { | 879 if (node is FunctionDeclaration) { |
| 687 replaceTypeWithFuture(node.returnType, typeProvider); | 880 replaceTypeWithFuture(node.returnType, typeProvider); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 718 void _addSuperTypesAsSuggestions(DartType type, Set<DartType> alreadyAdded) { | 911 void _addSuperTypesAsSuggestions(DartType type, Set<DartType> alreadyAdded) { |
| 719 if (type is InterfaceType && alreadyAdded.add(type)) { | 912 if (type is InterfaceType && alreadyAdded.add(type)) { |
| 720 addSuggestion(LinkedEditSuggestionKind.TYPE, type.displayName); | 913 addSuggestion(LinkedEditSuggestionKind.TYPE, type.displayName); |
| 721 _addSuperTypesAsSuggestions(type.superclass, alreadyAdded); | 914 _addSuperTypesAsSuggestions(type.superclass, alreadyAdded); |
| 722 for (InterfaceType interfaceType in type.interfaces) { | 915 for (InterfaceType interfaceType in type.interfaces) { |
| 723 _addSuperTypesAsSuggestions(interfaceType, alreadyAdded); | 916 _addSuperTypesAsSuggestions(interfaceType, alreadyAdded); |
| 724 } | 917 } |
| 725 } | 918 } |
| 726 } | 919 } |
| 727 } | 920 } |
| 921 |
| 922 class _EnclosingElementFinder { |
| 923 ClassElement enclosingClass; |
| 924 ExecutableElement enclosingExecutable; |
| 925 |
| 926 _EnclosingElementFinder(); |
| 927 |
| 928 void find(AstNode target, int offset) { |
| 929 AstNode node = new NodeLocator2(offset).searchWithin(target); |
| 930 while (node != null) { |
| 931 if (node is ClassDeclaration) { |
| 932 enclosingClass = node.element; |
| 933 } else if (node is ConstructorDeclaration) { |
| 934 enclosingExecutable = node.element; |
| 935 } else if (node is MethodDeclaration) { |
| 936 enclosingExecutable = node.element; |
| 937 } else if (node is FunctionDeclaration) { |
| 938 enclosingExecutable = node.element; |
| 939 } |
| 940 node = node.parent; |
| 941 } |
| 942 } |
| 943 } |
| OLD | NEW |