Revise the analysis server edit.dartfix protocol

This updates the edit.dartfix protocol to separate location from the text
describing the changes so that the dartfix client can choose
what location information to display and how it should be displayed.

Change-Id: Ic56a4cb187538571d60136d6de9265d879fccb53
Reviewed-on: https://dart-review.googlesource.com/c/81780
Commit-Queue: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
danrubel
2018-10-29 01:50:27 +00:00
committed by commit-bot@chromium.org
parent ae2441337a
commit 0fe448a996
14 changed files with 591 additions and 230 deletions
+4 -3
View File
@@ -2658,6 +2658,7 @@ a:focus, a:hover {
<dl><dt class="typeDefinition"><a name="type_AddContentOverlay">AddContentOverlay: object</a></dt><dd>
@@ -4379,9 +4380,9 @@ a:focus, a:hover {
</dd></dl></dd><dt class="typeDefinition"><a name="type_RuntimeCompletionExpression">RuntimeCompletionExpression: object</a></dt><dd>
<p>
An expression for which we want to know its runtime type.
In expressions like `a.b.c.where((e) =&gt; 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) =&gt; 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'.
</p>
<dl><dt class="field"><b>offset: int</b></dt><dd>
@@ -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';
@@ -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<String, dynamic> toJson() {
Map<String, dynamic> 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<String>
* "otherRecommendations": List<String>
* "suggestions": List<DartFixSuggestion>
* "otherSuggestions": List<DartFixSuggestion>
* "hasErrors": bool
* "fixes": List<SourceFileEdit>
* "edits": List<SourceFileEdit>
* }
*
* Clients may not extend, implement or mix-in this class.
*/
class EditDartfixResult implements ResponseResult {
List<String> _descriptionOfFixes;
List<DartFixSuggestion> _suggestions;
List<String> _otherRecommendations;
List<DartFixSuggestion> _otherSuggestions;
bool _hasErrors;
List<SourceFileEdit> _fixes;
List<SourceFileEdit> _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<String> get descriptionOfFixes => _descriptionOfFixes;
List<DartFixSuggestion> 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<String> value) {
void set suggestions(List<DartFixSuggestion> 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<String> get otherRecommendations => _otherRecommendations;
List<DartFixSuggestion> 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<String> value) {
void set otherSuggestions(List<DartFixSuggestion> 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<SourceFileEdit> get fixes => _fixes;
List<SourceFileEdit> get edits => _edits;
/**
* The suggested fixes.
* A list of source edits to apply the recommended changes.
*/
void set fixes(List<SourceFileEdit> value) {
void set edits(List<SourceFileEdit> value) {
assert(value != null);
this._fixes = value;
this._edits = value;
}
EditDartfixResult(
List<String> descriptionOfFixes,
List<String> otherRecommendations,
List<DartFixSuggestion> suggestions,
List<DartFixSuggestion> otherSuggestions,
bool hasErrors,
List<SourceFileEdit> fixes) {
this.descriptionOfFixes = descriptionOfFixes;
this.otherRecommendations = otherRecommendations;
List<SourceFileEdit> 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<String> descriptionOfFixes;
if (json.containsKey("descriptionOfFixes")) {
descriptionOfFixes = jsonDecoder.decodeList(
jsonPath + ".descriptionOfFixes",
json["descriptionOfFixes"],
jsonDecoder.decodeString);
List<DartFixSuggestion> 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<String> otherRecommendations;
if (json.containsKey("otherRecommendations")) {
otherRecommendations = jsonDecoder.decodeList(
jsonPath + ".otherRecommendations",
json["otherRecommendations"],
jsonDecoder.decodeString);
List<DartFixSuggestion> 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<SourceFileEdit> fixes;
if (json.containsKey("fixes")) {
fixes = jsonDecoder.decodeList(
jsonPath + ".fixes",
json["fixes"],
List<SourceFileEdit> 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<String, dynamic> toJson() {
Map<String, dynamic> 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);
}
}
@@ -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 = <Folder>[];
final fixFiles = <File>[];
List<String> descriptionOfFixes;
List<String> otherRecommendations;
List<DartFixSuggestion> suggestions;
List<DartFixSuggestion> 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<Response> compute() async {
@@ -125,8 +126,8 @@ class EditDartFix {
for (String rootPath in contextManager.includedPaths) {
resources.add(resourceProvider.getResource(rootPath));
}
descriptionOfFixes = <String>[];
otherRecommendations = <String>[];
suggestions = <DartFixSuggestion>[];
otherSuggestions = <DartFixSuggestion>[];
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;
}
}
@@ -23,18 +23,18 @@ class PreferIntLiteralsFix extends LinterFix {
new EditDartFixAssistContext(dartFix, source, result.unit, literal));
List<Assist> 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);
}
}
}
@@ -41,11 +41,11 @@ class PreferMixinFix extends LinterFix {
dartFix, elem.source, result.unit, declaration.name));
List<Assist> 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);
}
}
}
@@ -1517,23 +1517,24 @@ abstract class IntegrationTestMixin {
*
* Returns
*
* descriptionOfFixes: List<String>
* suggestions: List<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.
*
* otherRecommendations: List<String>
* otherSuggestions: List<DartFixSuggestion>
*
* 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<SourceFileEdit>
* edits: List<SourceFileEdit>
*
* The suggested fixes.
* A list of source edits to apply the recommended changes.
*/
Future<EditDartfixResult> sendEditDartfix(List<String> included) async {
var params = new EditDartfixParams(included).toJson();
@@ -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<String>
* "otherRecommendations": List<String>
* "suggestions": List<DartFixSuggestion>
* "otherSuggestions": List<DartFixSuggestion>
* "hasErrors": bool
* "fixes": List<SourceFileEdit>
* "edits": List<SourceFileEdit>
* }
*/
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)
}));
/**
@@ -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<DartFixSuggestion> 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<DartFixSuggestion> fromJsonArray(JsonArray jsonArray) {
if (jsonArray == null) {
return EMPTY_LIST;
}
ArrayList<DartFixSuggestion> list = new ArrayList<DartFixSuggestion>(jsonArray.size());
Iterator<JsonElement> 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();
}
}
@@ -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)
* =&gt; 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)
* =&gt; 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
*/
+32 -12
View File
@@ -1958,35 +1958,36 @@
</field>
</params>
<result>
<field name="descriptionOfFixes">
<field name="suggestions">
<list>
<ref>String</ref>
<ref>DartFixSuggestion</ref>
</list>
<p>
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.
</p>
</field>
<field name="otherRecommendations">
<field name="otherSuggestions">
<list>
<ref>String</ref>
<ref>DartFixSuggestion</ref>
</list>
<p>
A list of human readable recommended changes that cannot be made automatically.
A list of recommended changes that could not be automatically made.
</p>
</field>
<field name="hasErrors">
<ref>bool</ref>
<p>
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.
</p>
</field>
<field name="fixes">
<field name="edits">
<list>
<ref>SourceFileEdit</ref>
</list>
<p>
The suggested fixes.
A list of source edits to apply the recommended changes.
</p>
</field>
</result>
@@ -3313,9 +3314,9 @@
<type name="RuntimeCompletionExpression">
<p>
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'.
</p>
<object>
<field name="offset">
@@ -4232,6 +4233,25 @@
request.
</p>
</type>
<type name="DartFixSuggestion" experimental="true">
<p>
A suggestion from an edit.dartfix request.
</p>
<object>
<field name="description">
<ref>String</ref>
<p>
A human readable description of the suggested change.
</p>
</field>
<field name="location" optional="true">
<ref>Location</ref>
<p>
The location of the suggested change.
</p>
</field>
</object>
</type>
<type name="SearchResult">
<p>
A single result from a search request.
@@ -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';
@@ -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<String, dynamic> toJson() {
Map<String, dynamic> 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<String>
* "otherRecommendations": List<String>
* "suggestions": List<DartFixSuggestion>
* "otherSuggestions": List<DartFixSuggestion>
* "hasErrors": bool
* "fixes": List<SourceFileEdit>
* "edits": List<SourceFileEdit>
* }
*
* Clients may not extend, implement or mix-in this class.
*/
class EditDartfixResult implements ResponseResult {
List<String> _descriptionOfFixes;
List<DartFixSuggestion> _suggestions;
List<String> _otherRecommendations;
List<DartFixSuggestion> _otherSuggestions;
bool _hasErrors;
List<SourceFileEdit> _fixes;
List<SourceFileEdit> _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<String> get descriptionOfFixes => _descriptionOfFixes;
List<DartFixSuggestion> 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<String> value) {
void set suggestions(List<DartFixSuggestion> 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<String> get otherRecommendations => _otherRecommendations;
List<DartFixSuggestion> 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<String> value) {
void set otherSuggestions(List<DartFixSuggestion> 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<SourceFileEdit> get fixes => _fixes;
List<SourceFileEdit> get edits => _edits;
/**
* The suggested fixes.
* A list of source edits to apply the recommended changes.
*/
void set fixes(List<SourceFileEdit> value) {
void set edits(List<SourceFileEdit> value) {
assert(value != null);
this._fixes = value;
this._edits = value;
}
EditDartfixResult(
List<String> descriptionOfFixes,
List<String> otherRecommendations,
List<DartFixSuggestion> suggestions,
List<DartFixSuggestion> otherSuggestions,
bool hasErrors,
List<SourceFileEdit> fixes) {
this.descriptionOfFixes = descriptionOfFixes;
this.otherRecommendations = otherRecommendations;
List<SourceFileEdit> 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<String> descriptionOfFixes;
if (json.containsKey("descriptionOfFixes")) {
descriptionOfFixes = jsonDecoder.decodeList(
jsonPath + ".descriptionOfFixes",
json["descriptionOfFixes"],
jsonDecoder.decodeString);
List<DartFixSuggestion> 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<String> otherRecommendations;
if (json.containsKey("otherRecommendations")) {
otherRecommendations = jsonDecoder.decodeList(
jsonPath + ".otherRecommendations",
json["otherRecommendations"],
jsonDecoder.decodeString);
List<DartFixSuggestion> 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<SourceFileEdit> fixes;
if (json.containsKey("fixes")) {
fixes = jsonDecoder.decodeList(
jsonPath + ".fixes",
json["fixes"],
List<SourceFileEdit> 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<String, dynamic> toJson() {
Map<String, dynamic> 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);
}
}
+24 -15
View File
@@ -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<String> descriptions) {
if (descriptions.isNotEmpty) {
void showDescriptions(String title, List<DartFixSuggestion> suggestions) {
if (suggestions.isNotEmpty) {
logger.stdout('');
logger.stdout(ansi.emphasized('$title:'));
List<String> sorted = new List.from(descriptions)..sort();
for (String line in sorted) {
logger.stdout(' $line');
List<DartFixSuggestion> 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.