diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html index 504713e66858a..0c7d34b8efc91 100644 --- a/pkg/analysis_server/doc/api.html +++ b/pkg/analysis_server/doc/api.html @@ -2658,6 +2658,7 @@

Types

+
AddContentOverlay: object
@@ -4379,9 +4380,9 @@

Types

RuntimeCompletionExpression: object

An expression for which we want to know its runtime type. - In expressions like `a.b.c.where((e) => e.^)` we want to know the - runtime type of `a.b.c` to enforce it statically at the time when we - compute completion suggestions, and get better type for `e`. + In expressions like 'a.b.c.where((e) => e.^)' we want to know the + runtime type of 'a.b.c' to enforce it statically at the time when we + compute completion suggestions, and get better type for 'e'.

offset: int
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart index 9d85d08bdd2eb..1280460f470c7 100644 --- a/pkg/analysis_server/lib/protocol/protocol_constants.dart +++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart @@ -173,11 +173,10 @@ const String EDIT_REQUEST_ORGANIZE_DIRECTIVES = 'edit.organizeDirectives'; const String EDIT_REQUEST_ORGANIZE_DIRECTIVES_FILE = 'file'; const String EDIT_REQUEST_SORT_MEMBERS = 'edit.sortMembers'; const String EDIT_REQUEST_SORT_MEMBERS_FILE = 'file'; -const String EDIT_RESPONSE_DARTFIX_DESCRIPTION_OF_FIXES = 'descriptionOfFixes'; -const String EDIT_RESPONSE_DARTFIX_FIXES = 'fixes'; +const String EDIT_RESPONSE_DARTFIX_EDITS = 'edits'; const String EDIT_RESPONSE_DARTFIX_HAS_ERRORS = 'hasErrors'; -const String EDIT_RESPONSE_DARTFIX_OTHER_RECOMMENDATIONS = - 'otherRecommendations'; +const String EDIT_RESPONSE_DARTFIX_OTHER_SUGGESTIONS = 'otherSuggestions'; +const String EDIT_RESPONSE_DARTFIX_SUGGESTIONS = 'suggestions'; const String EDIT_RESPONSE_FORMAT_EDITS = 'edits'; const String EDIT_RESPONSE_FORMAT_SELECTION_LENGTH = 'selectionLength'; const String EDIT_RESPONSE_FORMAT_SELECTION_OFFSET = 'selectionOffset'; diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart index cb7d9469bd36d..3a0a480f94d5e 100644 --- a/pkg/analysis_server/lib/protocol/protocol_generated.dart +++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart @@ -5931,6 +5931,105 @@ class ConvertMethodToGetterOptions extends RefactoringOptions } } +/** + * DartFixSuggestion + * + * { + * "description": String + * "location": optional Location + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class DartFixSuggestion implements HasToJson { + String _description; + + Location _location; + + /** + * A human readable description of the suggested change. + */ + String get description => _description; + + /** + * A human readable description of the suggested change. + */ + void set description(String value) { + assert(value != null); + this._description = value; + } + + /** + * The location of the suggested change. + */ + Location get location => _location; + + /** + * The location of the suggested change. + */ + void set location(Location value) { + this._location = value; + } + + DartFixSuggestion(String description, {Location location}) { + this.description = description; + this.location = location; + } + + factory DartFixSuggestion.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String description; + if (json.containsKey("description")) { + description = jsonDecoder.decodeString( + jsonPath + ".description", json["description"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "description"); + } + Location location; + if (json.containsKey("location")) { + location = new Location.fromJson( + jsonDecoder, jsonPath + ".location", json["location"]); + } + return new DartFixSuggestion(description, location: location); + } else { + throw jsonDecoder.mismatch(jsonPath, "DartFixSuggestion", json); + } + } + + @override + Map toJson() { + Map result = {}; + result["description"] = description; + if (location != null) { + result["location"] = location.toJson(); + } + return result; + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is DartFixSuggestion) { + return description == other.description && location == other.location; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, description.hashCode); + hash = JenkinsSmiHash.combine(hash, location.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + /** * diagnostic.getDiagnostics params * @@ -6268,60 +6367,60 @@ class EditDartfixParams implements RequestParams { * edit.dartfix result * * { - * "descriptionOfFixes": List - * "otherRecommendations": List + * "suggestions": List + * "otherSuggestions": List * "hasErrors": bool - * "fixes": List + * "edits": List * } * * Clients may not extend, implement or mix-in this class. */ class EditDartfixResult implements ResponseResult { - List _descriptionOfFixes; + List _suggestions; - List _otherRecommendations; + List _otherSuggestions; bool _hasErrors; - List _fixes; + List _edits; /** - * A list of human readable changes made by applying the fixes. + * A list of recommended changes that can be automatically made by applying + * the 'edits' included in this response. */ - List get descriptionOfFixes => _descriptionOfFixes; + List get suggestions => _suggestions; /** - * A list of human readable changes made by applying the fixes. + * A list of recommended changes that can be automatically made by applying + * the 'edits' included in this response. */ - void set descriptionOfFixes(List value) { + void set suggestions(List value) { assert(value != null); - this._descriptionOfFixes = value; + this._suggestions = value; } /** - * A list of human readable recommended changes that cannot be made - * automatically. + * A list of recommended changes that could not be automatically made. */ - List get otherRecommendations => _otherRecommendations; + List get otherSuggestions => _otherSuggestions; /** - * A list of human readable recommended changes that cannot be made - * automatically. + * A list of recommended changes that could not be automatically made. */ - void set otherRecommendations(List value) { + void set otherSuggestions(List value) { assert(value != null); - this._otherRecommendations = value; + this._otherSuggestions = value; } /** * True if the analyzed source contains errors that might impact the - * correctness of the recommended fixes that can be automatically applied. + * correctness of the recommended changes that can be automatically applied. */ bool get hasErrors => _hasErrors; /** * True if the analyzed source contains errors that might impact the - * correctness of the recommended fixes that can be automatically applied. + * correctness of the recommended changes that can be automatically applied. */ void set hasErrors(bool value) { assert(value != null); @@ -6329,27 +6428,27 @@ class EditDartfixResult implements ResponseResult { } /** - * The suggested fixes. + * A list of source edits to apply the recommended changes. */ - List get fixes => _fixes; + List get edits => _edits; /** - * The suggested fixes. + * A list of source edits to apply the recommended changes. */ - void set fixes(List value) { + void set edits(List value) { assert(value != null); - this._fixes = value; + this._edits = value; } EditDartfixResult( - List descriptionOfFixes, - List otherRecommendations, + List suggestions, + List otherSuggestions, bool hasErrors, - List fixes) { - this.descriptionOfFixes = descriptionOfFixes; - this.otherRecommendations = otherRecommendations; + List edits) { + this.suggestions = suggestions; + this.otherSuggestions = otherSuggestions; this.hasErrors = hasErrors; - this.fixes = fixes; + this.edits = edits; } factory EditDartfixResult.fromJson( @@ -6358,23 +6457,25 @@ class EditDartfixResult implements ResponseResult { json = {}; } if (json is Map) { - List descriptionOfFixes; - if (json.containsKey("descriptionOfFixes")) { - descriptionOfFixes = jsonDecoder.decodeList( - jsonPath + ".descriptionOfFixes", - json["descriptionOfFixes"], - jsonDecoder.decodeString); + List suggestions; + if (json.containsKey("suggestions")) { + suggestions = jsonDecoder.decodeList( + jsonPath + ".suggestions", + json["suggestions"], + (String jsonPath, Object json) => + new DartFixSuggestion.fromJson(jsonDecoder, jsonPath, json)); } else { - throw jsonDecoder.mismatch(jsonPath, "descriptionOfFixes"); + throw jsonDecoder.mismatch(jsonPath, "suggestions"); } - List otherRecommendations; - if (json.containsKey("otherRecommendations")) { - otherRecommendations = jsonDecoder.decodeList( - jsonPath + ".otherRecommendations", - json["otherRecommendations"], - jsonDecoder.decodeString); + List otherSuggestions; + if (json.containsKey("otherSuggestions")) { + otherSuggestions = jsonDecoder.decodeList( + jsonPath + ".otherSuggestions", + json["otherSuggestions"], + (String jsonPath, Object json) => + new DartFixSuggestion.fromJson(jsonDecoder, jsonPath, json)); } else { - throw jsonDecoder.mismatch(jsonPath, "otherRecommendations"); + throw jsonDecoder.mismatch(jsonPath, "otherSuggestions"); } bool hasErrors; if (json.containsKey("hasErrors")) { @@ -6383,18 +6484,18 @@ class EditDartfixResult implements ResponseResult { } else { throw jsonDecoder.mismatch(jsonPath, "hasErrors"); } - List fixes; - if (json.containsKey("fixes")) { - fixes = jsonDecoder.decodeList( - jsonPath + ".fixes", - json["fixes"], + List edits; + if (json.containsKey("edits")) { + edits = jsonDecoder.decodeList( + jsonPath + ".edits", + json["edits"], (String jsonPath, Object json) => new SourceFileEdit.fromJson(jsonDecoder, jsonPath, json)); } else { - throw jsonDecoder.mismatch(jsonPath, "fixes"); + throw jsonDecoder.mismatch(jsonPath, "edits"); } return new EditDartfixResult( - descriptionOfFixes, otherRecommendations, hasErrors, fixes); + suggestions, otherSuggestions, hasErrors, edits); } else { throw jsonDecoder.mismatch(jsonPath, "edit.dartfix result", json); } @@ -6410,11 +6511,14 @@ class EditDartfixResult implements ResponseResult { @override Map toJson() { Map result = {}; - result["descriptionOfFixes"] = descriptionOfFixes; - result["otherRecommendations"] = otherRecommendations; + result["suggestions"] = + suggestions.map((DartFixSuggestion value) => value.toJson()).toList(); + result["otherSuggestions"] = otherSuggestions + .map((DartFixSuggestion value) => value.toJson()) + .toList(); result["hasErrors"] = hasErrors; - result["fixes"] = - fixes.map((SourceFileEdit value) => value.toJson()).toList(); + result["edits"] = + edits.map((SourceFileEdit value) => value.toJson()).toList(); return result; } @@ -6429,12 +6533,12 @@ class EditDartfixResult implements ResponseResult { @override bool operator ==(other) { if (other is EditDartfixResult) { - return listEqual(descriptionOfFixes, other.descriptionOfFixes, - (String a, String b) => a == b) && - listEqual(otherRecommendations, other.otherRecommendations, - (String a, String b) => a == b) && + return listEqual(suggestions, other.suggestions, + (DartFixSuggestion a, DartFixSuggestion b) => a == b) && + listEqual(otherSuggestions, other.otherSuggestions, + (DartFixSuggestion a, DartFixSuggestion b) => a == b) && hasErrors == other.hasErrors && - listEqual(fixes, other.fixes, + listEqual(edits, other.edits, (SourceFileEdit a, SourceFileEdit b) => a == b); } return false; @@ -6443,10 +6547,10 @@ class EditDartfixResult implements ResponseResult { @override int get hashCode { int hash = 0; - hash = JenkinsSmiHash.combine(hash, descriptionOfFixes.hashCode); - hash = JenkinsSmiHash.combine(hash, otherRecommendations.hashCode); + hash = JenkinsSmiHash.combine(hash, suggestions.hashCode); + hash = JenkinsSmiHash.combine(hash, otherSuggestions.hashCode); hash = JenkinsSmiHash.combine(hash, hasErrors.hashCode); - hash = JenkinsSmiHash.combine(hash, fixes.hashCode); + hash = JenkinsSmiHash.combine(hash, edits.hashCode); return JenkinsSmiHash.finish(hash); } } diff --git a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart index 3a8e3449d0abb..6673fb475f341 100644 --- a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart +++ b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart @@ -23,7 +23,7 @@ import 'package:analyzer/src/lint/linter_visitor.dart'; import 'package:analyzer/src/lint/registry.dart'; import 'package:analyzer/src/services/lint.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart' - show SourceChange, SourceEdit, SourceFileEdit; + show Location, SourceChange, SourceEdit, SourceFileEdit; import 'package:front_end/src/fasta/fasta_codes.dart'; import 'package:front_end/src/scanner/token.dart'; import 'package:source_span/src/span.dart'; @@ -34,14 +34,14 @@ class EditDartFix { final fixFolders = []; final fixFiles = []; - List descriptionOfFixes; - List otherRecommendations; + List suggestions; + List otherSuggestions; SourceChange sourceChange; EditDartFix(this.server, this.request); - void addFix(String description, SourceChange change) { - descriptionOfFixes.add(description); + void addFix(String description, Location location, SourceChange change) { + suggestions.add(new DartFixSuggestion(description, location: location)); for (SourceFileEdit fileEdit in change.edits) { for (SourceEdit sourceEdit in fileEdit.edits) { sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit); @@ -49,8 +49,9 @@ class EditDartFix { } } - void addRecommendation(String recommendation) { - otherRecommendations.add(recommendation); + void addRecommendation(String description, [Location location]) { + otherSuggestions + .add(new DartFixSuggestion(description, location: location)); } Future compute() async { @@ -125,8 +126,8 @@ class EditDartFix { for (String rootPath in contextManager.includedPaths) { resources.add(resourceProvider.getResource(rootPath)); } - descriptionOfFixes = []; - otherRecommendations = []; + suggestions = []; + otherSuggestions = []; sourceChange = new SourceChange('dartfix'); bool hasErrors = false; while (resources.isNotEmpty) { @@ -186,8 +187,8 @@ class EditDartFix { await fix.applyRemainingFixes(); } - return new EditDartfixResult(descriptionOfFixes, otherRecommendations, - hasErrors, sourceChange.edits) + return new EditDartfixResult( + suggestions, otherSuggestions, hasErrors, sourceChange.edits) .toResponse(request.id); } @@ -204,7 +205,6 @@ class EditDartFix { return false; } - final location = '${locationDescription(result, error.offset)}'; final dartContext = new DartFixContextImpl( new FixContextImpl( server.resourceProvider, result.driver, error, result.errors), @@ -212,12 +212,13 @@ class EditDartFix { result.unit); final processor = new FixProcessor(dartContext); Fix fix = await processor.computeFix(); + final location = locationFor(result, error.offset, error.length); if (fix != null) { - addFix('${fix.change.message} in $location', fix.change); + addFix(fix.change.message, location, fix.change); } else { // TODO(danrubel): Determine why the fix could not be applied // and report that in the description. - addRecommendation('Could not fix "${error.message}" in $location'); + addRecommendation('Could not fix "${error.message}"', location); } return true; } @@ -240,29 +241,11 @@ class EditDartFix { return false; } - /// Return a human readable description of the specified offset and file. - String locationDescription(AnalysisResult result, int offset) { - // TODO(danrubel): Pass the location back to the client along with the - // message indicating what was or was not automatically fixed - // rather than interpreting and integrating the location into the message. - final description = new StringBuffer(); - // Determine the relative path - for (Folder folder in fixFolders) { - if (folder.contains(result.path)) { - description.write(server.resourceProvider.pathContext - .relative(result.path, from: folder.path)); - break; - } - } - if (description.isEmpty) { - description.write(result.path); - } - // Determine the line and column number - if (offset >= 0) { - final loc = result.unit.lineInfo.getLocation(offset); - description.write(':${loc.lineNumber}'); - } - return description.toString(); + Location locationFor(AnalysisResult result, int offset, int length) { + final locInfo = result.unit.lineInfo.getLocation(offset); + final location = new Location( + result.path, offset, length, locInfo.lineNumber, locInfo.columnNumber); + return location; } } diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart index 81758f9d24318..52dda1f517f0a 100644 --- a/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart +++ b/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart @@ -23,18 +23,18 @@ class PreferIntLiteralsFix extends LinterFix { new EditDartFixAssistContext(dartFix, source, result.unit, literal)); List assists = await processor.computeAssist(DartAssistKind.CONVERT_TO_INT_LITERAL); - final location = dartFix.locationDescription(result, literal.offset); + final location = + dartFix.locationFor(result, literal.offset, literal.length); if (assists.isNotEmpty) { for (Assist assist in assists) { - dartFix.addFix( - 'Replace a double literal with an int literal in $location', - assist.change); + dartFix.addFix('Replace a double literal with an int literal', + location, assist.change); } } else { // TODO(danrubel): If assists is empty, then determine why // assist could not be performed and report that in the description. - dartFix.addRecommendation('Could not replace' - ' a double literal with an int literal in $location'); + dartFix.addRecommendation( + 'Could not replace a double literal with an int literal', location); } } } diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart index 5e2aad1c85ec7..52f924a374404 100644 --- a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart +++ b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart @@ -41,11 +41,11 @@ class PreferMixinFix extends LinterFix { dartFix, elem.source, result.unit, declaration.name)); List assists = await processor .computeAssist(DartAssistKind.CONVERT_CLASS_TO_MIXIN); - final location = dartFix.locationDescription(result, elem.nameOffset); + final location = + dartFix.locationFor(result, elem.nameOffset, elem.nameLength); if (assists.isNotEmpty) { for (Assist assist in assists) { - dartFix.addFix( - 'Convert ${elem.displayName} to a mixin in $location', + dartFix.addFix('Convert ${elem.displayName} to a mixin', location, assist.change); } } else { @@ -53,7 +53,8 @@ class PreferMixinFix extends LinterFix { // assist could not be performed and report that in the description. dartFix.addRecommendation( 'Could not convert ${elem.displayName} to a mixin' - ' because the class contains a constructor in $location'); + ' because the class contains a constructor', + location); } } } diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart index 3976d6c68e000..401723d8c7ed0 100644 --- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart +++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart @@ -1517,23 +1517,24 @@ abstract class IntegrationTestMixin { * * Returns * - * descriptionOfFixes: List + * suggestions: List * - * A list of human readable changes made by applying the fixes. + * A list of recommended changes that can be automatically made by applying + * the 'edits' included in this response. * - * otherRecommendations: List + * otherSuggestions: List * - * A list of human readable recommended changes that cannot be made - * automatically. + * A list of recommended changes that could not be automatically made. * * hasErrors: bool * * True if the analyzed source contains errors that might impact the - * correctness of the recommended fixes that can be automatically applied. + * correctness of the recommended changes that can be automatically + * applied. * - * fixes: List + * edits: List * - * The suggested fixes. + * A list of source edits to apply the recommended changes. */ Future sendEditDartfix(List included) async { var params = new EditDartfixParams(included).toJson(); diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart index db9505f263256..13ba9213204c1 100644 --- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart +++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart @@ -297,6 +297,18 @@ final Matcher isContextData = "cacheEntryExceptions": isListOf(isString) })); +/** + * DartFixSuggestion + * + * { + * "description": String + * "location": optional Location + * } + */ +final Matcher isDartFixSuggestion = new LazyMatcher(() => new MatchesJsonObject( + "DartFixSuggestion", {"description": isString}, + optionalFields: {"location": isLocation})); + /** * Element * @@ -2131,18 +2143,18 @@ final Matcher isEditDartfixParams = new LazyMatcher(() => new MatchesJsonObject( * edit.dartfix result * * { - * "descriptionOfFixes": List - * "otherRecommendations": List + * "suggestions": List + * "otherSuggestions": List * "hasErrors": bool - * "fixes": List + * "edits": List * } */ final Matcher isEditDartfixResult = new LazyMatcher(() => new MatchesJsonObject("edit.dartfix result", { - "descriptionOfFixes": isListOf(isString), - "otherRecommendations": isListOf(isString), + "suggestions": isListOf(isDartFixSuggestion), + "otherSuggestions": isListOf(isDartFixSuggestion), "hasErrors": isBool, - "fixes": isListOf(isSourceFileEdit) + "edits": isListOf(isSourceFileEdit) })); /** diff --git a/pkg/analysis_server/tool/spec/generated/java/types/DartFixSuggestion.java b/pkg/analysis_server/tool/spec/generated/java/types/DartFixSuggestion.java new file mode 100644 index 0000000000000..7a106f82f0133 --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/DartFixSuggestion.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + * + * This file has been automatically generated. Please do not edit it manually. + * To regenerate the file, use the script "pkg/analysis_server/tool/spec/generate_files". + */ +package org.dartlang.analysis.server.protocol; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import com.google.common.collect.Lists; +import com.google.dart.server.utilities.general.JsonUtilities; +import com.google.dart.server.utilities.general.ObjectUtilities; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import java.util.ArrayList; +import java.util.Iterator; +import org.apache.commons.lang3.StringUtils; + +/** + * A suggestion from an edit.dartfix request. + * + * @coverage dart.server.generated.types + */ +@SuppressWarnings("unused") +public class DartFixSuggestion { + + public static final DartFixSuggestion[] EMPTY_ARRAY = new DartFixSuggestion[0]; + + public static final List EMPTY_LIST = Lists.newArrayList(); + + /** + * A human readable description of the suggested change. + */ + private final String description; + + /** + * The location of the suggested change. + */ + private final Location location; + + /** + * Constructor for {@link DartFixSuggestion}. + */ + public DartFixSuggestion(String description, Location location) { + this.description = description; + this.location = location; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DartFixSuggestion) { + DartFixSuggestion other = (DartFixSuggestion) obj; + return + ObjectUtilities.equals(other.description, description) && + ObjectUtilities.equals(other.location, location); + } + return false; + } + + public static DartFixSuggestion fromJson(JsonObject jsonObject) { + String description = jsonObject.get("description").getAsString(); + Location location = jsonObject.get("location") == null ? null : Location.fromJson(jsonObject.get("location").getAsJsonObject()); + return new DartFixSuggestion(description, location); + } + + public static List fromJsonArray(JsonArray jsonArray) { + if (jsonArray == null) { + return EMPTY_LIST; + } + ArrayList list = new ArrayList(jsonArray.size()); + Iterator iterator = jsonArray.iterator(); + while (iterator.hasNext()) { + list.add(fromJson(iterator.next().getAsJsonObject())); + } + return list; + } + + /** + * A human readable description of the suggested change. + */ + public String getDescription() { + return description; + } + + /** + * The location of the suggested change. + */ + public Location getLocation() { + return location; + } + + @Override + public int hashCode() { + HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(description); + builder.append(location); + return builder.toHashCode(); + } + + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("description", description); + if (location != null) { + jsonObject.add("location", location.toJson()); + } + return jsonObject; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append("description="); + builder.append(description + ", "); + builder.append("location="); + builder.append(location); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java index eb6b0f0400ce8..b2d6b705f596b 100644 --- a/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java +++ b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java @@ -24,9 +24,9 @@ import org.apache.commons.lang3.StringUtils; /** - * An expression for which we want to know its runtime type. In expressions like `a.b.c.where((e) - * => e.^)` we want to know the runtime type of `a.b.c` to enforce it statically at the time - * when we compute completion suggestions, and get better type for `e`. + * An expression for which we want to know its runtime type. In expressions like 'a.b.c.where((e) + * => e.^)' we want to know the runtime type of 'a.b.c' to enforce it statically at the time + * when we compute completion suggestions, and get better type for 'e'. * * @coverage dart.server.generated.types */ diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html index c8f98641c6c02..bb90c8a424fa3 100644 --- a/pkg/analysis_server/tool/spec/spec_input.html +++ b/pkg/analysis_server/tool/spec/spec_input.html @@ -1958,35 +1958,36 @@

Options

- + - String + DartFixSuggestion

- A list of human readable changes made by applying the fixes. + A list of recommended changes that can be automatically made + by applying the 'edits' included in this response.

- + - String + DartFixSuggestion

- A list of human readable recommended changes that cannot be made automatically. + A list of recommended changes that could not be automatically made.

bool

True if the analyzed source contains errors that might impact the correctness - of the recommended fixes that can be automatically applied. + of the recommended changes that can be automatically applied.

- + SourceFileEdit

- The suggested fixes. + A list of source edits to apply the recommended changes.

@@ -3313,9 +3314,9 @@

Types

An expression for which we want to know its runtime type. - In expressions like `a.b.c.where((e) => e.^)` we want to know the - runtime type of `a.b.c` to enforce it statically at the time when we - compute completion suggestions, and get better type for `e`. + In expressions like 'a.b.c.where((e) => e.^)' we want to know the + runtime type of 'a.b.c' to enforce it statically at the time when we + compute completion suggestions, and get better type for 'e'.

@@ -4232,6 +4233,25 @@

Types

request.

+ +

+ A suggestion from an edit.dartfix request. +

+ + + String +

+ A human readable description of the suggested change. +

+
+ + Location +

+ The location of the suggested change. +

+
+
+

A single result from a search request. diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart index 9d85d08bdd2eb..1280460f470c7 100644 --- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart +++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart @@ -173,11 +173,10 @@ const String EDIT_REQUEST_ORGANIZE_DIRECTIVES = 'edit.organizeDirectives'; const String EDIT_REQUEST_ORGANIZE_DIRECTIVES_FILE = 'file'; const String EDIT_REQUEST_SORT_MEMBERS = 'edit.sortMembers'; const String EDIT_REQUEST_SORT_MEMBERS_FILE = 'file'; -const String EDIT_RESPONSE_DARTFIX_DESCRIPTION_OF_FIXES = 'descriptionOfFixes'; -const String EDIT_RESPONSE_DARTFIX_FIXES = 'fixes'; +const String EDIT_RESPONSE_DARTFIX_EDITS = 'edits'; const String EDIT_RESPONSE_DARTFIX_HAS_ERRORS = 'hasErrors'; -const String EDIT_RESPONSE_DARTFIX_OTHER_RECOMMENDATIONS = - 'otherRecommendations'; +const String EDIT_RESPONSE_DARTFIX_OTHER_SUGGESTIONS = 'otherSuggestions'; +const String EDIT_RESPONSE_DARTFIX_SUGGESTIONS = 'suggestions'; const String EDIT_RESPONSE_FORMAT_EDITS = 'edits'; const String EDIT_RESPONSE_FORMAT_SELECTION_LENGTH = 'selectionLength'; const String EDIT_RESPONSE_FORMAT_SELECTION_OFFSET = 'selectionOffset'; diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart index 2bbf169d20865..9fabb4c83ad2b 100644 --- a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart +++ b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart @@ -5931,6 +5931,105 @@ class ConvertMethodToGetterOptions extends RefactoringOptions } } +/** + * DartFixSuggestion + * + * { + * "description": String + * "location": optional Location + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class DartFixSuggestion implements HasToJson { + String _description; + + Location _location; + + /** + * A human readable description of the suggested change. + */ + String get description => _description; + + /** + * A human readable description of the suggested change. + */ + void set description(String value) { + assert(value != null); + this._description = value; + } + + /** + * The location of the suggested change. + */ + Location get location => _location; + + /** + * The location of the suggested change. + */ + void set location(Location value) { + this._location = value; + } + + DartFixSuggestion(String description, {Location location}) { + this.description = description; + this.location = location; + } + + factory DartFixSuggestion.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String description; + if (json.containsKey("description")) { + description = jsonDecoder.decodeString( + jsonPath + ".description", json["description"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "description"); + } + Location location; + if (json.containsKey("location")) { + location = new Location.fromJson( + jsonDecoder, jsonPath + ".location", json["location"]); + } + return new DartFixSuggestion(description, location: location); + } else { + throw jsonDecoder.mismatch(jsonPath, "DartFixSuggestion", json); + } + } + + @override + Map toJson() { + Map result = {}; + result["description"] = description; + if (location != null) { + result["location"] = location.toJson(); + } + return result; + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is DartFixSuggestion) { + return description == other.description && location == other.location; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, description.hashCode); + hash = JenkinsSmiHash.combine(hash, location.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + /** * diagnostic.getDiagnostics params * @@ -6268,60 +6367,60 @@ class EditDartfixParams implements RequestParams { * edit.dartfix result * * { - * "descriptionOfFixes": List - * "otherRecommendations": List + * "suggestions": List + * "otherSuggestions": List * "hasErrors": bool - * "fixes": List + * "edits": List * } * * Clients may not extend, implement or mix-in this class. */ class EditDartfixResult implements ResponseResult { - List _descriptionOfFixes; + List _suggestions; - List _otherRecommendations; + List _otherSuggestions; bool _hasErrors; - List _fixes; + List _edits; /** - * A list of human readable changes made by applying the fixes. + * A list of recommended changes that can be automatically made by applying + * the 'edits' included in this response. */ - List get descriptionOfFixes => _descriptionOfFixes; + List get suggestions => _suggestions; /** - * A list of human readable changes made by applying the fixes. + * A list of recommended changes that can be automatically made by applying + * the 'edits' included in this response. */ - void set descriptionOfFixes(List value) { + void set suggestions(List value) { assert(value != null); - this._descriptionOfFixes = value; + this._suggestions = value; } /** - * A list of human readable recommended changes that cannot be made - * automatically. + * A list of recommended changes that could not be automatically made. */ - List get otherRecommendations => _otherRecommendations; + List get otherSuggestions => _otherSuggestions; /** - * A list of human readable recommended changes that cannot be made - * automatically. + * A list of recommended changes that could not be automatically made. */ - void set otherRecommendations(List value) { + void set otherSuggestions(List value) { assert(value != null); - this._otherRecommendations = value; + this._otherSuggestions = value; } /** * True if the analyzed source contains errors that might impact the - * correctness of the recommended fixes that can be automatically applied. + * correctness of the recommended changes that can be automatically applied. */ bool get hasErrors => _hasErrors; /** * True if the analyzed source contains errors that might impact the - * correctness of the recommended fixes that can be automatically applied. + * correctness of the recommended changes that can be automatically applied. */ void set hasErrors(bool value) { assert(value != null); @@ -6329,27 +6428,27 @@ class EditDartfixResult implements ResponseResult { } /** - * The suggested fixes. + * A list of source edits to apply the recommended changes. */ - List get fixes => _fixes; + List get edits => _edits; /** - * The suggested fixes. + * A list of source edits to apply the recommended changes. */ - void set fixes(List value) { + void set edits(List value) { assert(value != null); - this._fixes = value; + this._edits = value; } EditDartfixResult( - List descriptionOfFixes, - List otherRecommendations, + List suggestions, + List otherSuggestions, bool hasErrors, - List fixes) { - this.descriptionOfFixes = descriptionOfFixes; - this.otherRecommendations = otherRecommendations; + List edits) { + this.suggestions = suggestions; + this.otherSuggestions = otherSuggestions; this.hasErrors = hasErrors; - this.fixes = fixes; + this.edits = edits; } factory EditDartfixResult.fromJson( @@ -6358,23 +6457,25 @@ class EditDartfixResult implements ResponseResult { json = {}; } if (json is Map) { - List descriptionOfFixes; - if (json.containsKey("descriptionOfFixes")) { - descriptionOfFixes = jsonDecoder.decodeList( - jsonPath + ".descriptionOfFixes", - json["descriptionOfFixes"], - jsonDecoder.decodeString); + List suggestions; + if (json.containsKey("suggestions")) { + suggestions = jsonDecoder.decodeList( + jsonPath + ".suggestions", + json["suggestions"], + (String jsonPath, Object json) => + new DartFixSuggestion.fromJson(jsonDecoder, jsonPath, json)); } else { - throw jsonDecoder.mismatch(jsonPath, "descriptionOfFixes"); + throw jsonDecoder.mismatch(jsonPath, "suggestions"); } - List otherRecommendations; - if (json.containsKey("otherRecommendations")) { - otherRecommendations = jsonDecoder.decodeList( - jsonPath + ".otherRecommendations", - json["otherRecommendations"], - jsonDecoder.decodeString); + List otherSuggestions; + if (json.containsKey("otherSuggestions")) { + otherSuggestions = jsonDecoder.decodeList( + jsonPath + ".otherSuggestions", + json["otherSuggestions"], + (String jsonPath, Object json) => + new DartFixSuggestion.fromJson(jsonDecoder, jsonPath, json)); } else { - throw jsonDecoder.mismatch(jsonPath, "otherRecommendations"); + throw jsonDecoder.mismatch(jsonPath, "otherSuggestions"); } bool hasErrors; if (json.containsKey("hasErrors")) { @@ -6383,18 +6484,18 @@ class EditDartfixResult implements ResponseResult { } else { throw jsonDecoder.mismatch(jsonPath, "hasErrors"); } - List fixes; - if (json.containsKey("fixes")) { - fixes = jsonDecoder.decodeList( - jsonPath + ".fixes", - json["fixes"], + List edits; + if (json.containsKey("edits")) { + edits = jsonDecoder.decodeList( + jsonPath + ".edits", + json["edits"], (String jsonPath, Object json) => new SourceFileEdit.fromJson(jsonDecoder, jsonPath, json)); } else { - throw jsonDecoder.mismatch(jsonPath, "fixes"); + throw jsonDecoder.mismatch(jsonPath, "edits"); } return new EditDartfixResult( - descriptionOfFixes, otherRecommendations, hasErrors, fixes); + suggestions, otherSuggestions, hasErrors, edits); } else { throw jsonDecoder.mismatch(jsonPath, "edit.dartfix result", json); } @@ -6410,11 +6511,14 @@ class EditDartfixResult implements ResponseResult { @override Map toJson() { Map result = {}; - result["descriptionOfFixes"] = descriptionOfFixes; - result["otherRecommendations"] = otherRecommendations; + result["suggestions"] = + suggestions.map((DartFixSuggestion value) => value.toJson()).toList(); + result["otherSuggestions"] = otherSuggestions + .map((DartFixSuggestion value) => value.toJson()) + .toList(); result["hasErrors"] = hasErrors; - result["fixes"] = - fixes.map((SourceFileEdit value) => value.toJson()).toList(); + result["edits"] = + edits.map((SourceFileEdit value) => value.toJson()).toList(); return result; } @@ -6429,12 +6533,12 @@ class EditDartfixResult implements ResponseResult { @override bool operator ==(other) { if (other is EditDartfixResult) { - return listEqual(descriptionOfFixes, other.descriptionOfFixes, - (String a, String b) => a == b) && - listEqual(otherRecommendations, other.otherRecommendations, - (String a, String b) => a == b) && + return listEqual(suggestions, other.suggestions, + (DartFixSuggestion a, DartFixSuggestion b) => a == b) && + listEqual(otherSuggestions, other.otherSuggestions, + (DartFixSuggestion a, DartFixSuggestion b) => a == b) && hasErrors == other.hasErrors && - listEqual(fixes, other.fixes, + listEqual(edits, other.edits, (SourceFileEdit a, SourceFileEdit b) => a == b); } return false; @@ -6443,10 +6547,10 @@ class EditDartfixResult implements ResponseResult { @override int get hashCode { int hash = 0; - hash = JenkinsSmiHash.combine(hash, descriptionOfFixes.hashCode); - hash = JenkinsSmiHash.combine(hash, otherRecommendations.hashCode); + hash = JenkinsSmiHash.combine(hash, suggestions.hashCode); + hash = JenkinsSmiHash.combine(hash, otherSuggestions.hashCode); hash = JenkinsSmiHash.combine(hash, hasErrors.hashCode); - hash = JenkinsSmiHash.combine(hash, fixes.hashCode); + hash = JenkinsSmiHash.combine(hash, edits.hashCode); return JenkinsSmiHash.finish(hash); } } diff --git a/pkg/dartfix/lib/src/driver.dart b/pkg/dartfix/lib/src/driver.dart index 80b6c11d10599..80e7a05ac497f 100644 --- a/pkg/dartfix/lib/src/driver.dart +++ b/pkg/dartfix/lib/src/driver.dart @@ -122,25 +122,23 @@ class Driver { } Future applyFixes(EditDartfixResult result) async { - showDescriptions('Recommended changes', result.descriptionOfFixes); - showDescriptions( - 'Recommended changes that cannot not be automatically applied', - result.otherRecommendations, - ); - if (result.descriptionOfFixes.isEmpty) { + showDescriptions('Recommended changes', result.suggestions); + showDescriptions('Recommended changes that cannot be automatically applied', + result.otherSuggestions); + if (result.suggestions.isEmpty) { logger.stdout(''); - logger.stdout(result.otherRecommendations.isNotEmpty - ? 'No recommended changes that cannot be automatically applied.' + logger.stdout(result.otherSuggestions.isNotEmpty + ? 'None of the recommended changes can be automatically applied.' : 'No recommended changes.'); return; } logger.stdout(''); logger.stdout(ansi.emphasized('Files to be changed:')); - for (SourceFileEdit fileEdit in result.fixes) { + for (SourceFileEdit fileEdit in result.edits) { logger.stdout(' ${_relativePath(fileEdit.file)}'); } if (shouldApplyChanges(result)) { - for (SourceFileEdit fileEdit in result.fixes) { + for (SourceFileEdit fileEdit in result.edits) { final file = new File(fileEdit.file); String code = await file.readAsString(); for (SourceEdit edit in fileEdit.edits) { @@ -152,13 +150,16 @@ class Driver { } } - void showDescriptions(String title, List descriptions) { - if (descriptions.isNotEmpty) { + void showDescriptions(String title, List suggestions) { + if (suggestions.isNotEmpty) { logger.stdout(''); logger.stdout(ansi.emphasized('$title:')); - List sorted = new List.from(descriptions)..sort(); - for (String line in sorted) { - logger.stdout(' $line'); + List sorted = new List.from(suggestions) + ..sort(compareSuggestions); + for (DartFixSuggestion suggestion in sorted) { + Location loc = suggestion.location; + logger.stdout(' ${_toSentenceFragment(suggestion.description)}' + '${loc == null ? "" : " • ${loc.startLine}:${loc.startColumn}"}'); } } } @@ -327,6 +328,14 @@ class Driver { } } + int compareSuggestions(DartFixSuggestion s1, DartFixSuggestion s2) { + int result = s1.description.compareTo(s2.description); + if (result != 0) { + return result; + } + return (s2.location?.offset ?? 0) - (s1.location?.offset ?? 0); + } + bool shouldFilterError(AnalysisError error) { // Do not show TODOs or errors that will be automatically fixed.