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 9d85d08bdd2..1280460f470 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 cb7d9469bd3..3a0a480f94d 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 3a8e3449d0a..6673fb475f3 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 81758f9d243..52dda1f517f 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 5e2aad1c85e..52f924a3744 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 3976d6c68e0..401723d8c7e 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 db9505f2632..13ba9213204 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 00000000000..7a106f82f01
--- /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 eb6b0f0400c..b2d6b705f59 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 java.util.Iterator;
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 c8f98641c6c..bb90c8a424f 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -1958,35 +1958,36 @@
-
+
- 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 @@
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'.
+
+
+ A suggestion from an edit.dartfix request.
+
+
+
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 9d85d08bdd2..1280460f470 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 2bbf169d208..9fabb4c83ad 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 80b6c11d105..80e7a05ac49 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.