From e3966fcfb9f20da28fc6f80dc7ed39ab0da3e77a Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Fri, 11 May 2018 22:23:42 +0000 Subject: [PATCH] Protocol for runtime completion. This protocol will be used to support "Evaluate Expression" and "Watch" window features in IDEs. Change-Id: Ibde1e88507a66d8c487836a227dfeb13ffd77d9d Reviewed-on: https://dart-review.googlesource.com/54629 Commit-Queue: Konstantin Shcheglov Reviewed-by: Jacob Richman Reviewed-by: Brian Wilkerson --- pkg/analysis_server/doc/api.html | 209 +++- .../lib/protocol/protocol_constants.dart | 9 + .../lib/protocol/protocol_generated.dart | 995 ++++++++++++++++++ .../test/integration/coverage.md | 1 + .../support/integration_test_methods.dart | 92 ++ .../support/protocol_matchers.dart | 102 ++ .../spec/generated/java/AnalysisServer.java | 32 + .../types/RuntimeCompletionExpression.java | 151 +++ .../RuntimeCompletionExpressionType.java | 255 +++++ .../RuntimeCompletionExpressionTypeKind.java | 24 + .../java/types/RuntimeCompletionVariable.java | 128 +++ pkg/analysis_server/tool/spec/spec_input.html | 231 ++++ 12 files changed, 2228 insertions(+), 1 deletion(-) create mode 100644 pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java create mode 100644 pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionType.java create mode 100644 pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionTypeKind.java create mode 100644 pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionVariable.java diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html index 453a5203754..98a977c59d7 100644 --- a/pkg/analysis_server/doc/api.html +++ b/pkg/analysis_server/doc/api.html @@ -276,6 +276,7 @@ a:focus, a:hover {

Execution

@@ -2263,6 +2264,7 @@ a:focus, a:hover { +

Requests

execution.createContext
request: {
   "id": String
   "method": "execution.createContext"
@@ -2317,6 +2319,110 @@ a:focus, a:hover {
         

The identifier of the execution context that is to be deleted.

+
execution.getSuggestions
request: {
+  "id": String
+  "method": "execution.getSuggestions"
+  "params": {
+    "code": String
+    "offset": int
+    "contextFile": FilePath
+    "contextOffset": int
+    "variables": List<RuntimeCompletionVariable>
+    "expressions": optional List<RuntimeCompletionExpression>
+  }
+}

response: {
+  "id": String
+  "error": optional RequestError
+  "result": {
+    "suggestions": optional List<CompletionSuggestion>
+    "expressions": optional List<RuntimeCompletionExpression>
+  }
+}
+

+ Request completion suggestions for the given runtime context. +

+

+ It might take one or two requests of this type to get completion + suggestions. The first request should have only "code", "offset", + and "variables", but not "expressions". If there are sub-expressions that + can have different runtime types, and are considered to be safe to + evaluate at runtime (e.g. getters), so using their actual runtime types + can improve completion results, the server will not include the + "suggestions" field in the response, and instead will return the + "expressions" field. The client will use debug API to get current runtime + types for these sub-expressions and send another request, this time with + "expressions". If there are no interesting sub-expressions to get + runtime types for, or when the "expressions" field is provided by the + client, the server will return "suggestions" in the response. +

+ + +

parameters:

code: String
+ +

+ The code to get suggestions in. +

+
offset: int
+ +

+ The offset within the code to get suggestions at. +

+
contextFile: FilePath
+ +

+ The path of the context file, e.g. the file of the current debugger + frame. The combination of the context file and context offset can + be used to ensure that all variables of the context are available + for completion (with their static types). +

+
contextOffset: int
+ +

+ The offset in the context file, e.g. the line offset in the current + debugger frame. +

+
variables: List<RuntimeCompletionVariable>
+ +

+ The runtime context variables that are potentially referenced in the + code. +

+
expressions: List<RuntimeCompletionExpression> (optional)
+ +

+ The list of sub-expressions in the code for which the client wants + to provide runtime types. It does not have to be the full list of + expressions requested by the server, for missing expressions their + static types will be used. +

+

+ When this field is omitted, the server will return completion + suggestions only when there are no interesting sub-expressions in the + given code. The client may provide an empty list, in this case the + server will return completion suggestions. +

+

returns:

suggestions: List<CompletionSuggestion> (optional)
+ +

+ The completion suggestions. In contrast to usual completion request, + suggestions for private elements also will be provided. +

+

+ If there are sub-expressions that can have different runtime types, + and are considered to be safe to evaluate at runtime (e.g. getters), + so using their actual runtime types can improve completion results, + the server omits this field in the response, and instead will return + the "expressions" field. +

+
expressions: List<RuntimeCompletionExpression> (optional)
+ +

+ The list of sub-expressions in the code for which the server would + like to know runtime types to provide better completion suggestions. +

+

+ This field is omitted the field "suggestions" is returned. +

execution.mapUri
request: {
   "id": String
   "method": "execution.mapUri"
@@ -2538,6 +2644,10 @@ a:focus, a:hover {
   
   
   
+  
+  
+  
+  
   
   
   
@@ -4250,6 +4360,103 @@ a:focus, a:hover {
           API reaches version 1.0.
         

+
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`. +

+ +
offset: int
+ +

+ The offset of the expression in the code for completion. +

+
length: int
+ +

+ The length of the expression in the code for completion. +

+
type: RuntimeCompletionExpressionType (optional)
+ +

+ When the expression is sent from the server to the client, the + type is omitted. The client should fill the type when it sends the + request to the server again. +

+
RuntimeCompletionExpressionType: object
+

+ A type at runtime. +

+ +
libraryPath: FilePath (optional)
+ +

+ The path of the library that has this type. + Omitted if the type is not declared in any library, e.g. "dynamic", + or "void". +

+
kind: RuntimeCompletionExpressionTypeKind
+ +

+ The kind of the type. +

+
name: String (optional)
+ +

+ The name of the type. Omitted if the type does not have a name, e.g. + an inline function type. +

+
typeArguments: List<RuntimeCompletionExpressionType> (optional)
+ +

+ The type arguments of the type. + Omitted if the type does not have type parameters. +

+
returnType: RuntimeCompletionExpressionType (optional)
+ +

+ If the type is a function type, the return type of the function. + Omitted if the type is not a function type. +

+
parameterTypes: List<RuntimeCompletionExpressionType> (optional)
+ +

+ If the type is a function type, the types of the function parameters + of all kinds - required, optional positional, and optional named. + Omitted if the type is not a function type. +

+
parameterNames: List<String> (optional)
+ +

+ If the type is a function type, the names of the function parameters + of all kinds - required, optional positional, and optional named. + The names of positional parameters are empty strings. + Omitted if the type is not a function type. +

+
RuntimeCompletionExpressionTypeKind: String
+

+ An enumeration of the kinds of runtime expression types. +

+ +
DYNAMIC
FUNCTION
INTERFACE
RuntimeCompletionVariable: object
+

+ A variable in a runtime context. +

+ +
name: String
+ +

+ The name of the variable. + The name "this" has a special meaning and is used as an implicit + target for runtime completion, and in explicit "this" references. +

+
type: RuntimeCompletionExpressionType
+ +

+ The type of the variable. +

SearchId: String

@@ -4832,7 +5039,7 @@ a:focus, a:hover { TODO: TBD

Index

-

Domains

server ()

analysis ()

completion ()

Requests
Notifications

search ()

edit ()

execution ()

diagnostic ()

Types ()

Refactorings ()

+

Domains

server ()

analysis ()

completion ()

Requests
Notifications

search ()

edit ()

execution ()

diagnostic ()

Types ()

Refactorings ()

\ No newline at end of file diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart index 148fc494d14..fcd43003aed 100644 --- a/pkg/analysis_server/lib/protocol/protocol_constants.dart +++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart @@ -196,6 +196,13 @@ const String EXECUTION_REQUEST_CREATE_CONTEXT = 'execution.createContext'; const String EXECUTION_REQUEST_CREATE_CONTEXT_CONTEXT_ROOT = 'contextRoot'; const String EXECUTION_REQUEST_DELETE_CONTEXT = 'execution.deleteContext'; const String EXECUTION_REQUEST_DELETE_CONTEXT_ID = 'id'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS = 'execution.getSuggestions'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS_CODE = 'code'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS_CONTEXT_FILE = 'contextFile'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS_CONTEXT_OFFSET = 'contextOffset'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS_EXPRESSIONS = 'expressions'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS_OFFSET = 'offset'; +const String EXECUTION_REQUEST_GET_SUGGESTIONS_VARIABLES = 'variables'; const String EXECUTION_REQUEST_MAP_URI = 'execution.mapUri'; const String EXECUTION_REQUEST_MAP_URI_FILE = 'file'; const String EXECUTION_REQUEST_MAP_URI_ID = 'id'; @@ -204,6 +211,8 @@ const String EXECUTION_REQUEST_SET_SUBSCRIPTIONS = 'execution.setSubscriptions'; const String EXECUTION_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS = 'subscriptions'; const String EXECUTION_RESPONSE_CREATE_CONTEXT_ID = 'id'; +const String EXECUTION_RESPONSE_GET_SUGGESTIONS_EXPRESSIONS = 'expressions'; +const String EXECUTION_RESPONSE_GET_SUGGESTIONS_SUGGESTIONS = 'suggestions'; const String EXECUTION_RESPONSE_MAP_URI_FILE = 'file'; const String EXECUTION_RESPONSE_MAP_URI_URI = 'uri'; const String FLUTTER_NOTIFICATION_OUTLINE = 'flutter.outline'; diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart index cd60f28dab4..7175c14044c 100644 --- a/pkg/analysis_server/lib/protocol/protocol_generated.dart +++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart @@ -9503,6 +9503,422 @@ class ExecutionDeleteContextResult implements ResponseResult { } } +/** + * execution.getSuggestions params + * + * { + * "code": String + * "offset": int + * "contextFile": FilePath + * "contextOffset": int + * "variables": List + * "expressions": optional List + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class ExecutionGetSuggestionsParams implements RequestParams { + String _code; + + int _offset; + + String _contextFile; + + int _contextOffset; + + List _variables; + + List _expressions; + + /** + * The code to get suggestions in. + */ + String get code => _code; + + /** + * The code to get suggestions in. + */ + void set code(String value) { + assert(value != null); + this._code = value; + } + + /** + * The offset within the code to get suggestions at. + */ + int get offset => _offset; + + /** + * The offset within the code to get suggestions at. + */ + void set offset(int value) { + assert(value != null); + this._offset = value; + } + + /** + * The path of the context file, e.g. the file of the current debugger frame. + * The combination of the context file and context offset can be used to + * ensure that all variables of the context are available for completion + * (with their static types). + */ + String get contextFile => _contextFile; + + /** + * The path of the context file, e.g. the file of the current debugger frame. + * The combination of the context file and context offset can be used to + * ensure that all variables of the context are available for completion + * (with their static types). + */ + void set contextFile(String value) { + assert(value != null); + this._contextFile = value; + } + + /** + * The offset in the context file, e.g. the line offset in the current + * debugger frame. + */ + int get contextOffset => _contextOffset; + + /** + * The offset in the context file, e.g. the line offset in the current + * debugger frame. + */ + void set contextOffset(int value) { + assert(value != null); + this._contextOffset = value; + } + + /** + * The runtime context variables that are potentially referenced in the code. + */ + List get variables => _variables; + + /** + * The runtime context variables that are potentially referenced in the code. + */ + void set variables(List value) { + assert(value != null); + this._variables = value; + } + + /** + * The list of sub-expressions in the code for which the client wants to + * provide runtime types. It does not have to be the full list of expressions + * requested by the server, for missing expressions their static types will + * be used. + * + * When this field is omitted, the server will return completion suggestions + * only when there are no interesting sub-expressions in the given code. The + * client may provide an empty list, in this case the server will return + * completion suggestions. + */ + List get expressions => _expressions; + + /** + * The list of sub-expressions in the code for which the client wants to + * provide runtime types. It does not have to be the full list of expressions + * requested by the server, for missing expressions their static types will + * be used. + * + * When this field is omitted, the server will return completion suggestions + * only when there are no interesting sub-expressions in the given code. The + * client may provide an empty list, in this case the server will return + * completion suggestions. + */ + void set expressions(List value) { + this._expressions = value; + } + + ExecutionGetSuggestionsParams(String code, int offset, String contextFile, + int contextOffset, List variables, + {List expressions}) { + this.code = code; + this.offset = offset; + this.contextFile = contextFile; + this.contextOffset = contextOffset; + this.variables = variables; + this.expressions = expressions; + } + + factory ExecutionGetSuggestionsParams.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String code; + if (json.containsKey("code")) { + code = jsonDecoder.decodeString(jsonPath + ".code", json["code"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "code"); + } + int offset; + if (json.containsKey("offset")) { + offset = jsonDecoder.decodeInt(jsonPath + ".offset", json["offset"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "offset"); + } + String contextFile; + if (json.containsKey("contextFile")) { + contextFile = jsonDecoder.decodeString( + jsonPath + ".contextFile", json["contextFile"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "contextFile"); + } + int contextOffset; + if (json.containsKey("contextOffset")) { + contextOffset = jsonDecoder.decodeInt( + jsonPath + ".contextOffset", json["contextOffset"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "contextOffset"); + } + List variables; + if (json.containsKey("variables")) { + variables = jsonDecoder.decodeList( + jsonPath + ".variables", + json["variables"], + (String jsonPath, Object json) => + new RuntimeCompletionVariable.fromJson( + jsonDecoder, jsonPath, json)); + } else { + throw jsonDecoder.mismatch(jsonPath, "variables"); + } + List expressions; + if (json.containsKey("expressions")) { + expressions = jsonDecoder.decodeList( + jsonPath + ".expressions", + json["expressions"], + (String jsonPath, Object json) => + new RuntimeCompletionExpression.fromJson( + jsonDecoder, jsonPath, json)); + } + return new ExecutionGetSuggestionsParams( + code, offset, contextFile, contextOffset, variables, + expressions: expressions); + } else { + throw jsonDecoder.mismatch( + jsonPath, "execution.getSuggestions params", json); + } + } + + factory ExecutionGetSuggestionsParams.fromRequest(Request request) { + return new ExecutionGetSuggestionsParams.fromJson( + new RequestDecoder(request), "params", request.params); + } + + @override + Map toJson() { + Map result = {}; + result["code"] = code; + result["offset"] = offset; + result["contextFile"] = contextFile; + result["contextOffset"] = contextOffset; + result["variables"] = variables + .map((RuntimeCompletionVariable value) => value.toJson()) + .toList(); + if (expressions != null) { + result["expressions"] = expressions + .map((RuntimeCompletionExpression value) => value.toJson()) + .toList(); + } + return result; + } + + @override + Request toRequest(String id) { + return new Request(id, "execution.getSuggestions", toJson()); + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is ExecutionGetSuggestionsParams) { + return code == other.code && + offset == other.offset && + contextFile == other.contextFile && + contextOffset == other.contextOffset && + listEqual( + variables, + other.variables, + (RuntimeCompletionVariable a, RuntimeCompletionVariable b) => + a == b) && + listEqual( + expressions, + other.expressions, + (RuntimeCompletionExpression a, RuntimeCompletionExpression b) => + a == b); + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, code.hashCode); + hash = JenkinsSmiHash.combine(hash, offset.hashCode); + hash = JenkinsSmiHash.combine(hash, contextFile.hashCode); + hash = JenkinsSmiHash.combine(hash, contextOffset.hashCode); + hash = JenkinsSmiHash.combine(hash, variables.hashCode); + hash = JenkinsSmiHash.combine(hash, expressions.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + +/** + * execution.getSuggestions result + * + * { + * "suggestions": optional List + * "expressions": optional List + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class ExecutionGetSuggestionsResult implements ResponseResult { + List _suggestions; + + List _expressions; + + /** + * The completion suggestions. In contrast to usual completion request, + * suggestions for private elements also will be provided. + * + * If there are sub-expressions that can have different runtime types, and + * are considered to be safe to evaluate at runtime (e.g. getters), so using + * their actual runtime types can improve completion results, the server + * omits this field in the response, and instead will return the + * "expressions" field. + */ + List get suggestions => _suggestions; + + /** + * The completion suggestions. In contrast to usual completion request, + * suggestions for private elements also will be provided. + * + * If there are sub-expressions that can have different runtime types, and + * are considered to be safe to evaluate at runtime (e.g. getters), so using + * their actual runtime types can improve completion results, the server + * omits this field in the response, and instead will return the + * "expressions" field. + */ + void set suggestions(List value) { + this._suggestions = value; + } + + /** + * The list of sub-expressions in the code for which the server would like to + * know runtime types to provide better completion suggestions. + * + * This field is omitted the field "suggestions" is returned. + */ + List get expressions => _expressions; + + /** + * The list of sub-expressions in the code for which the server would like to + * know runtime types to provide better completion suggestions. + * + * This field is omitted the field "suggestions" is returned. + */ + void set expressions(List value) { + this._expressions = value; + } + + ExecutionGetSuggestionsResult( + {List suggestions, + List expressions}) { + this.suggestions = suggestions; + this.expressions = expressions; + } + + factory ExecutionGetSuggestionsResult.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + List suggestions; + if (json.containsKey("suggestions")) { + suggestions = jsonDecoder.decodeList( + jsonPath + ".suggestions", + json["suggestions"], + (String jsonPath, Object json) => + new CompletionSuggestion.fromJson(jsonDecoder, jsonPath, json)); + } + List expressions; + if (json.containsKey("expressions")) { + expressions = jsonDecoder.decodeList( + jsonPath + ".expressions", + json["expressions"], + (String jsonPath, Object json) => + new RuntimeCompletionExpression.fromJson( + jsonDecoder, jsonPath, json)); + } + return new ExecutionGetSuggestionsResult( + suggestions: suggestions, expressions: expressions); + } else { + throw jsonDecoder.mismatch( + jsonPath, "execution.getSuggestions result", json); + } + } + + factory ExecutionGetSuggestionsResult.fromResponse(Response response) { + return new ExecutionGetSuggestionsResult.fromJson( + new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)), + "result", + response.result); + } + + @override + Map toJson() { + Map result = {}; + if (suggestions != null) { + result["suggestions"] = suggestions + .map((CompletionSuggestion value) => value.toJson()) + .toList(); + } + if (expressions != null) { + result["expressions"] = expressions + .map((RuntimeCompletionExpression value) => value.toJson()) + .toList(); + } + return result; + } + + @override + Response toResponse(String id) { + return new Response(id, result: toJson()); + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is ExecutionGetSuggestionsResult) { + return listEqual(suggestions, other.suggestions, + (CompletionSuggestion a, CompletionSuggestion b) => a == b) && + listEqual( + expressions, + other.expressions, + (RuntimeCompletionExpression a, RuntimeCompletionExpression b) => + a == b); + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, suggestions.hashCode); + hash = JenkinsSmiHash.combine(hash, expressions.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + /** * execution.launchData params * @@ -15226,6 +15642,585 @@ class RequestErrorCode implements Enum { String toJson() => name; } +/** + * RuntimeCompletionExpression + * + * { + * "offset": int + * "length": int + * "type": optional RuntimeCompletionExpressionType + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class RuntimeCompletionExpression implements HasToJson { + int _offset; + + int _length; + + RuntimeCompletionExpressionType _type; + + /** + * The offset of the expression in the code for completion. + */ + int get offset => _offset; + + /** + * The offset of the expression in the code for completion. + */ + void set offset(int value) { + assert(value != null); + this._offset = value; + } + + /** + * The length of the expression in the code for completion. + */ + int get length => _length; + + /** + * The length of the expression in the code for completion. + */ + void set length(int value) { + assert(value != null); + this._length = value; + } + + /** + * When the expression is sent from the server to the client, the type is + * omitted. The client should fill the type when it sends the request to the + * server again. + */ + RuntimeCompletionExpressionType get type => _type; + + /** + * When the expression is sent from the server to the client, the type is + * omitted. The client should fill the type when it sends the request to the + * server again. + */ + void set type(RuntimeCompletionExpressionType value) { + this._type = value; + } + + RuntimeCompletionExpression(int offset, int length, + {RuntimeCompletionExpressionType type}) { + this.offset = offset; + this.length = length; + this.type = type; + } + + factory RuntimeCompletionExpression.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + int offset; + if (json.containsKey("offset")) { + offset = jsonDecoder.decodeInt(jsonPath + ".offset", json["offset"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "offset"); + } + int length; + if (json.containsKey("length")) { + length = jsonDecoder.decodeInt(jsonPath + ".length", json["length"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "length"); + } + RuntimeCompletionExpressionType type; + if (json.containsKey("type")) { + type = new RuntimeCompletionExpressionType.fromJson( + jsonDecoder, jsonPath + ".type", json["type"]); + } + return new RuntimeCompletionExpression(offset, length, type: type); + } else { + throw jsonDecoder.mismatch(jsonPath, "RuntimeCompletionExpression", json); + } + } + + @override + Map toJson() { + Map result = {}; + result["offset"] = offset; + result["length"] = length; + if (type != null) { + result["type"] = type.toJson(); + } + return result; + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is RuntimeCompletionExpression) { + return offset == other.offset && + length == other.length && + type == other.type; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, offset.hashCode); + hash = JenkinsSmiHash.combine(hash, length.hashCode); + hash = JenkinsSmiHash.combine(hash, type.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + +/** + * RuntimeCompletionExpressionType + * + * { + * "libraryPath": optional FilePath + * "kind": RuntimeCompletionExpressionTypeKind + * "name": optional String + * "typeArguments": optional List + * "returnType": optional RuntimeCompletionExpressionType + * "parameterTypes": optional List + * "parameterNames": optional List + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class RuntimeCompletionExpressionType implements HasToJson { + String _libraryPath; + + RuntimeCompletionExpressionTypeKind _kind; + + String _name; + + List _typeArguments; + + RuntimeCompletionExpressionType _returnType; + + List _parameterTypes; + + List _parameterNames; + + /** + * The path of the library that has this type. Omitted if the type is not + * declared in any library, e.g. "dynamic", or "void". + */ + String get libraryPath => _libraryPath; + + /** + * The path of the library that has this type. Omitted if the type is not + * declared in any library, e.g. "dynamic", or "void". + */ + void set libraryPath(String value) { + this._libraryPath = value; + } + + /** + * The kind of the type. + */ + RuntimeCompletionExpressionTypeKind get kind => _kind; + + /** + * The kind of the type. + */ + void set kind(RuntimeCompletionExpressionTypeKind value) { + assert(value != null); + this._kind = value; + } + + /** + * The name of the type. Omitted if the type does not have a name, e.g. an + * inline function type. + */ + String get name => _name; + + /** + * The name of the type. Omitted if the type does not have a name, e.g. an + * inline function type. + */ + void set name(String value) { + this._name = value; + } + + /** + * The type arguments of the type. Omitted if the type does not have type + * parameters. + */ + List get typeArguments => _typeArguments; + + /** + * The type arguments of the type. Omitted if the type does not have type + * parameters. + */ + void set typeArguments(List value) { + this._typeArguments = value; + } + + /** + * If the type is a function type, the return type of the function. Omitted + * if the type is not a function type. + */ + RuntimeCompletionExpressionType get returnType => _returnType; + + /** + * If the type is a function type, the return type of the function. Omitted + * if the type is not a function type. + */ + void set returnType(RuntimeCompletionExpressionType value) { + this._returnType = value; + } + + /** + * If the type is a function type, the types of the function parameters of + * all kinds - required, optional positional, and optional named. Omitted if + * the type is not a function type. + */ + List get parameterTypes => _parameterTypes; + + /** + * If the type is a function type, the types of the function parameters of + * all kinds - required, optional positional, and optional named. Omitted if + * the type is not a function type. + */ + void set parameterTypes(List value) { + this._parameterTypes = value; + } + + /** + * If the type is a function type, the names of the function parameters of + * all kinds - required, optional positional, and optional named. The names + * of positional parameters are empty strings. Omitted if the type is not a + * function type. + */ + List get parameterNames => _parameterNames; + + /** + * If the type is a function type, the names of the function parameters of + * all kinds - required, optional positional, and optional named. The names + * of positional parameters are empty strings. Omitted if the type is not a + * function type. + */ + void set parameterNames(List value) { + this._parameterNames = value; + } + + RuntimeCompletionExpressionType(RuntimeCompletionExpressionTypeKind kind, + {String libraryPath, + String name, + List typeArguments, + RuntimeCompletionExpressionType returnType, + List parameterTypes, + List parameterNames}) { + this.libraryPath = libraryPath; + this.kind = kind; + this.name = name; + this.typeArguments = typeArguments; + this.returnType = returnType; + this.parameterTypes = parameterTypes; + this.parameterNames = parameterNames; + } + + factory RuntimeCompletionExpressionType.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String libraryPath; + if (json.containsKey("libraryPath")) { + libraryPath = jsonDecoder.decodeString( + jsonPath + ".libraryPath", json["libraryPath"]); + } + RuntimeCompletionExpressionTypeKind kind; + if (json.containsKey("kind")) { + kind = new RuntimeCompletionExpressionTypeKind.fromJson( + jsonDecoder, jsonPath + ".kind", json["kind"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "kind"); + } + String name; + if (json.containsKey("name")) { + name = jsonDecoder.decodeString(jsonPath + ".name", json["name"]); + } + List typeArguments; + if (json.containsKey("typeArguments")) { + typeArguments = jsonDecoder.decodeList( + jsonPath + ".typeArguments", + json["typeArguments"], + (String jsonPath, Object json) => + new RuntimeCompletionExpressionType.fromJson( + jsonDecoder, jsonPath, json)); + } + RuntimeCompletionExpressionType returnType; + if (json.containsKey("returnType")) { + returnType = new RuntimeCompletionExpressionType.fromJson( + jsonDecoder, jsonPath + ".returnType", json["returnType"]); + } + List parameterTypes; + if (json.containsKey("parameterTypes")) { + parameterTypes = jsonDecoder.decodeList( + jsonPath + ".parameterTypes", + json["parameterTypes"], + (String jsonPath, Object json) => + new RuntimeCompletionExpressionType.fromJson( + jsonDecoder, jsonPath, json)); + } + List parameterNames; + if (json.containsKey("parameterNames")) { + parameterNames = jsonDecoder.decodeList(jsonPath + ".parameterNames", + json["parameterNames"], jsonDecoder.decodeString); + } + return new RuntimeCompletionExpressionType(kind, + libraryPath: libraryPath, + name: name, + typeArguments: typeArguments, + returnType: returnType, + parameterTypes: parameterTypes, + parameterNames: parameterNames); + } else { + throw jsonDecoder.mismatch( + jsonPath, "RuntimeCompletionExpressionType", json); + } + } + + @override + Map toJson() { + Map result = {}; + if (libraryPath != null) { + result["libraryPath"] = libraryPath; + } + result["kind"] = kind.toJson(); + if (name != null) { + result["name"] = name; + } + if (typeArguments != null) { + result["typeArguments"] = typeArguments + .map((RuntimeCompletionExpressionType value) => value.toJson()) + .toList(); + } + if (returnType != null) { + result["returnType"] = returnType.toJson(); + } + if (parameterTypes != null) { + result["parameterTypes"] = parameterTypes + .map((RuntimeCompletionExpressionType value) => value.toJson()) + .toList(); + } + if (parameterNames != null) { + result["parameterNames"] = parameterNames; + } + return result; + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is RuntimeCompletionExpressionType) { + return libraryPath == other.libraryPath && + kind == other.kind && + name == other.name && + listEqual( + typeArguments, + other.typeArguments, + (RuntimeCompletionExpressionType a, + RuntimeCompletionExpressionType b) => + a == b) && + returnType == other.returnType && + listEqual( + parameterTypes, + other.parameterTypes, + (RuntimeCompletionExpressionType a, + RuntimeCompletionExpressionType b) => + a == b) && + listEqual(parameterNames, other.parameterNames, + (String a, String b) => a == b); + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, libraryPath.hashCode); + hash = JenkinsSmiHash.combine(hash, kind.hashCode); + hash = JenkinsSmiHash.combine(hash, name.hashCode); + hash = JenkinsSmiHash.combine(hash, typeArguments.hashCode); + hash = JenkinsSmiHash.combine(hash, returnType.hashCode); + hash = JenkinsSmiHash.combine(hash, parameterTypes.hashCode); + hash = JenkinsSmiHash.combine(hash, parameterNames.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + +/** + * RuntimeCompletionExpressionTypeKind + * + * enum { + * DYNAMIC + * FUNCTION + * INTERFACE + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class RuntimeCompletionExpressionTypeKind implements Enum { + static const RuntimeCompletionExpressionTypeKind DYNAMIC = + const RuntimeCompletionExpressionTypeKind._("DYNAMIC"); + + static const RuntimeCompletionExpressionTypeKind FUNCTION = + const RuntimeCompletionExpressionTypeKind._("FUNCTION"); + + static const RuntimeCompletionExpressionTypeKind INTERFACE = + const RuntimeCompletionExpressionTypeKind._("INTERFACE"); + + /** + * A list containing all of the enum values that are defined. + */ + static const List VALUES = + const [DYNAMIC, FUNCTION, INTERFACE]; + + @override + final String name; + + const RuntimeCompletionExpressionTypeKind._(this.name); + + factory RuntimeCompletionExpressionTypeKind(String name) { + switch (name) { + case "DYNAMIC": + return DYNAMIC; + case "FUNCTION": + return FUNCTION; + case "INTERFACE": + return INTERFACE; + } + throw new Exception('Illegal enum value: $name'); + } + + factory RuntimeCompletionExpressionTypeKind.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json is String) { + try { + return new RuntimeCompletionExpressionTypeKind(json); + } catch (_) { + // Fall through + } + } + throw jsonDecoder.mismatch( + jsonPath, "RuntimeCompletionExpressionTypeKind", json); + } + + @override + String toString() => "RuntimeCompletionExpressionTypeKind.$name"; + + String toJson() => name; +} + +/** + * RuntimeCompletionVariable + * + * { + * "name": String + * "type": RuntimeCompletionExpressionType + * } + * + * Clients may not extend, implement or mix-in this class. + */ +class RuntimeCompletionVariable implements HasToJson { + String _name; + + RuntimeCompletionExpressionType _type; + + /** + * The name of the variable. The name "this" has a special meaning and is + * used as an implicit target for runtime completion, and in explicit "this" + * references. + */ + String get name => _name; + + /** + * The name of the variable. The name "this" has a special meaning and is + * used as an implicit target for runtime completion, and in explicit "this" + * references. + */ + void set name(String value) { + assert(value != null); + this._name = value; + } + + /** + * The type of the variable. + */ + RuntimeCompletionExpressionType get type => _type; + + /** + * The type of the variable. + */ + void set type(RuntimeCompletionExpressionType value) { + assert(value != null); + this._type = value; + } + + RuntimeCompletionVariable(String name, RuntimeCompletionExpressionType type) { + this.name = name; + this.type = type; + } + + factory RuntimeCompletionVariable.fromJson( + JsonDecoder jsonDecoder, String jsonPath, Object json) { + if (json == null) { + json = {}; + } + if (json is Map) { + String name; + if (json.containsKey("name")) { + name = jsonDecoder.decodeString(jsonPath + ".name", json["name"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "name"); + } + RuntimeCompletionExpressionType type; + if (json.containsKey("type")) { + type = new RuntimeCompletionExpressionType.fromJson( + jsonDecoder, jsonPath + ".type", json["type"]); + } else { + throw jsonDecoder.mismatch(jsonPath, "type"); + } + return new RuntimeCompletionVariable(name, type); + } else { + throw jsonDecoder.mismatch(jsonPath, "RuntimeCompletionVariable", json); + } + } + + @override + Map toJson() { + Map result = {}; + result["name"] = name; + result["type"] = type.toJson(); + return result; + } + + @override + String toString() => json.encode(toJson()); + + @override + bool operator ==(other) { + if (other is RuntimeCompletionVariable) { + return name == other.name && type == other.type; + } + return false; + } + + @override + int get hashCode { + int hash = 0; + hash = JenkinsSmiHash.combine(hash, name.hashCode); + hash = JenkinsSmiHash.combine(hash, type.hashCode); + return JenkinsSmiHash.finish(hash); + } +} + /** * search.findElementReferences params * diff --git a/pkg/analysis_server/test/integration/coverage.md b/pkg/analysis_server/test/integration/coverage.md index 4dbeae6cb73..466c66e3d5d 100644 --- a/pkg/analysis_server/test/integration/coverage.md +++ b/pkg/analysis_server/test/integration/coverage.md @@ -53,6 +53,7 @@ server calls. This file is validated by `coverage_test.dart`. ## execution domain - [x] execution.createContext - [x] execution.deleteContext +- [ ] execution.getSuggestions - [x] execution.mapUri - [x] execution.setSubscriptions - [ ] execution.launchData 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 9b77a47b4b3..41880e3d8fe 100644 --- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart +++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart @@ -1829,6 +1829,98 @@ abstract class IntegrationTestMixin { return null; } + /** + * Request completion suggestions for the given runtime context. + * + * It might take one or two requests of this type to get completion + * suggestions. The first request should have only "code", "offset", and + * "variables", but not "expressions". If there are sub-expressions that can + * have different runtime types, and are considered to be safe to evaluate at + * runtime (e.g. getters), so using their actual runtime types can improve + * completion results, the server will not include the "suggestions" field in + * the response, and instead will return the "expressions" field. The client + * will use debug API to get current runtime types for these sub-expressions + * and send another request, this time with "expressions". If there are no + * interesting sub-expressions to get runtime types for, or when the + * "expressions" field is provided by the client, the server will return + * "suggestions" in the response. + * + * Parameters + * + * code: String + * + * The code to get suggestions in. + * + * offset: int + * + * The offset within the code to get suggestions at. + * + * contextFile: FilePath + * + * The path of the context file, e.g. the file of the current debugger + * frame. The combination of the context file and context offset can be + * used to ensure that all variables of the context are available for + * completion (with their static types). + * + * contextOffset: int + * + * The offset in the context file, e.g. the line offset in the current + * debugger frame. + * + * variables: List + * + * The runtime context variables that are potentially referenced in the + * code. + * + * expressions: List (optional) + * + * The list of sub-expressions in the code for which the client wants to + * provide runtime types. It does not have to be the full list of + * expressions requested by the server, for missing expressions their + * static types will be used. + * + * When this field is omitted, the server will return completion + * suggestions only when there are no interesting sub-expressions in the + * given code. The client may provide an empty list, in this case the + * server will return completion suggestions. + * + * Returns + * + * suggestions: List (optional) + * + * The completion suggestions. In contrast to usual completion request, + * suggestions for private elements also will be provided. + * + * If there are sub-expressions that can have different runtime types, and + * are considered to be safe to evaluate at runtime (e.g. getters), so + * using their actual runtime types can improve completion results, the + * server omits this field in the response, and instead will return the + * "expressions" field. + * + * expressions: List (optional) + * + * The list of sub-expressions in the code for which the server would like + * to know runtime types to provide better completion suggestions. + * + * This field is omitted the field "suggestions" is returned. + */ + Future sendExecutionGetSuggestions( + String code, + int offset, + String contextFile, + int contextOffset, + List variables, + {List expressions}) async { + var params = new ExecutionGetSuggestionsParams( + code, offset, contextFile, contextOffset, variables, + expressions: expressions) + .toJson(); + var result = await server.send("execution.getSuggestions", params); + ResponseDecoder decoder = new ResponseDecoder(null); + return new ExecutionGetSuggestionsResult.fromJson( + decoder, 'result', result); + } + /** * Map a URI from the execution context to the file that it corresponds to, * or map a file to the URI that it corresponds to in the execution context. diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart index dd07221863e..549934276fc 100644 --- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart +++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart @@ -1276,6 +1276,70 @@ final Matcher isRequestErrorCode = new MatchesEnum("RequestErrorCode", [ "UNSUPPORTED_FEATURE" ]); +/** + * RuntimeCompletionExpression + * + * { + * "offset": int + * "length": int + * "type": optional RuntimeCompletionExpressionType + * } + */ +final Matcher isRuntimeCompletionExpression = new LazyMatcher(() => + new MatchesJsonObject( + "RuntimeCompletionExpression", {"offset": isInt, "length": isInt}, + optionalFields: {"type": isRuntimeCompletionExpressionType})); + +/** + * RuntimeCompletionExpressionType + * + * { + * "libraryPath": optional FilePath + * "kind": RuntimeCompletionExpressionTypeKind + * "name": optional String + * "typeArguments": optional List + * "returnType": optional RuntimeCompletionExpressionType + * "parameterTypes": optional List + * "parameterNames": optional List + * } + */ +final Matcher isRuntimeCompletionExpressionType = new LazyMatcher( + () => new MatchesJsonObject("RuntimeCompletionExpressionType", { + "kind": isRuntimeCompletionExpressionTypeKind + }, optionalFields: { + "libraryPath": isFilePath, + "name": isString, + "typeArguments": isListOf(isRuntimeCompletionExpressionType), + "returnType": isRuntimeCompletionExpressionType, + "parameterTypes": isListOf(isRuntimeCompletionExpressionType), + "parameterNames": isListOf(isString) + })); + +/** + * RuntimeCompletionExpressionTypeKind + * + * enum { + * DYNAMIC + * FUNCTION + * INTERFACE + * } + */ +final Matcher isRuntimeCompletionExpressionTypeKind = new MatchesEnum( + "RuntimeCompletionExpressionTypeKind", + ["DYNAMIC", "FUNCTION", "INTERFACE"]); + +/** + * RuntimeCompletionVariable + * + * { + * "name": String + * "type": RuntimeCompletionExpressionType + * } + */ +final Matcher isRuntimeCompletionVariable = new LazyMatcher(() => + new MatchesJsonObject("RuntimeCompletionVariable", + {"name": isString, "type": isRuntimeCompletionExpressionType})); + /** * SearchId * @@ -2328,6 +2392,44 @@ final Matcher isExecutionDeleteContextParams = new LazyMatcher(() => */ final Matcher isExecutionDeleteContextResult = isNull; +/** + * execution.getSuggestions params + * + * { + * "code": String + * "offset": int + * "contextFile": FilePath + * "contextOffset": int + * "variables": List + * "expressions": optional List + * } + */ +final Matcher isExecutionGetSuggestionsParams = new LazyMatcher( + () => new MatchesJsonObject("execution.getSuggestions params", { + "code": isString, + "offset": isInt, + "contextFile": isFilePath, + "contextOffset": isInt, + "variables": isListOf(isRuntimeCompletionVariable) + }, optionalFields: { + "expressions": isListOf(isRuntimeCompletionExpression) + })); + +/** + * execution.getSuggestions result + * + * { + * "suggestions": optional List + * "expressions": optional List + * } + */ +final Matcher isExecutionGetSuggestionsResult = new LazyMatcher(() => + new MatchesJsonObject("execution.getSuggestions result", null, + optionalFields: { + "suggestions": isListOf(isCompletionSuggestion), + "expressions": isListOf(isRuntimeCompletionExpression) + })); + /** * execution.launchData params * diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java index 62726497089..f8991b5304c 100644 --- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java +++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java @@ -575,6 +575,38 @@ public interface AnalysisServer { */ public void execution_deleteContext(String id); + /** + * {@code execution.getSuggestions} + * + * Request completion suggestions for the given runtime context. + * + * It might take one or two requests of this type to get completion suggestions. The first request + * should have only "code", "offset", and "variables", but not "expressions". If there are + * sub-expressions that can have different runtime types, and are considered to be safe to evaluate + * at runtime (e.g. getters), so using their actual runtime types can improve completion results, + * the server will not include the "suggestions" field in the response, and instead will return the + * "expressions" field. The client will use debug API to get current runtime types for these + * sub-expressions and send another request, this time with "expressions". If there are no + * interesting sub-expressions to get runtime types for, or when the "expressions" field is + * provided by the client, the server will return "suggestions" in the response. + * + * @param code The code to get suggestions in. + * @param offset The offset within the code to get suggestions at. + * @param contextFile The path of the context file, e.g. the file of the current debugger frame. + * The combination of the context file and context offset can be used to ensure that all + * variables of the context are available for completion (with their static types). + * @param contextOffset The offset in the context file, e.g. the line offset in the current + * debugger frame. + * @param variables The runtime context variables that are potentially referenced in the code. + * @param expressions The list of sub-expressions in the code for which the client wants to provide + * runtime types. It does not have to be the full list of expressions requested by the + * server, for missing expressions their static types will be used. When this field is + * omitted, the server will return completion suggestions only when there are no + * interesting sub-expressions in the given code. The client may provide an empty list, in + * this case the server will return completion suggestions. + */ + public void execution_getSuggestions(String code, int offset, String contextFile, int contextOffset, List variables, List expressions, GetSuggestionsConsumer consumer); + /** * {@code execution.mapUri} * diff --git a/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java new file mode 100644 index 00000000000..c0cc239f246 --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpression.java @@ -0,0 +1,151 @@ +/* + * 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; + +/** + * 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 + */ +@SuppressWarnings("unused") +public class RuntimeCompletionExpression { + + public static final RuntimeCompletionExpression[] EMPTY_ARRAY = new RuntimeCompletionExpression[0]; + + public static final List EMPTY_LIST = Lists.newArrayList(); + + /** + * The offset of the expression in the code for completion. + */ + private final int offset; + + /** + * The length of the expression in the code for completion. + */ + private final int length; + + /** + * When the expression is sent from the server to the client, the type is omitted. The client + * should fill the type when it sends the request to the server again. + */ + private final RuntimeCompletionExpressionType type; + + /** + * Constructor for {@link RuntimeCompletionExpression}. + */ + public RuntimeCompletionExpression(int offset, int length, RuntimeCompletionExpressionType type) { + this.offset = offset; + this.length = length; + this.type = type; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RuntimeCompletionExpression) { + RuntimeCompletionExpression other = (RuntimeCompletionExpression) obj; + return + other.offset == offset && + other.length == length && + ObjectUtilities.equals(other.type, type); + } + return false; + } + + public static RuntimeCompletionExpression fromJson(JsonObject jsonObject) { + int offset = jsonObject.get("offset").getAsInt(); + int length = jsonObject.get("length").getAsInt(); + RuntimeCompletionExpressionType type = jsonObject.get("type") == null ? null : RuntimeCompletionExpressionType.fromJson(jsonObject.get("type").getAsJsonObject()); + return new RuntimeCompletionExpression(offset, length, type); + } + + 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; + } + + /** + * The length of the expression in the code for completion. + */ + public int getLength() { + return length; + } + + /** + * The offset of the expression in the code for completion. + */ + public int getOffset() { + return offset; + } + + /** + * When the expression is sent from the server to the client, the type is omitted. The client + * should fill the type when it sends the request to the server again. + */ + public RuntimeCompletionExpressionType getType() { + return type; + } + + @Override + public int hashCode() { + HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(offset); + builder.append(length); + builder.append(type); + return builder.toHashCode(); + } + + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("offset", offset); + jsonObject.addProperty("length", length); + if (type != null) { + jsonObject.add("type", type.toJson()); + } + return jsonObject; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append("offset="); + builder.append(offset + ", "); + builder.append("length="); + builder.append(length + ", "); + builder.append("type="); + builder.append(type); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionType.java b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionType.java new file mode 100644 index 00000000000..c31710a7b69 --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionType.java @@ -0,0 +1,255 @@ +/* + * 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 type at runtime. + * + * @coverage dart.server.generated.types + */ +@SuppressWarnings("unused") +public class RuntimeCompletionExpressionType { + + public static final RuntimeCompletionExpressionType[] EMPTY_ARRAY = new RuntimeCompletionExpressionType[0]; + + public static final List EMPTY_LIST = Lists.newArrayList(); + + /** + * The path of the library that has this type. Omitted if the type is not declared in any library, + * e.g. "dynamic", or "void". + */ + private final String libraryPath; + + /** + * The kind of the type. + */ + private final String kind; + + /** + * The name of the type. Omitted if the type does not have a name, e.g. an inline function type. + */ + private final String name; + + /** + * The type arguments of the type. Omitted if the type does not have type parameters. + */ + private final List typeArguments; + + /** + * If the type is a function type, the return type of the function. Omitted if the type is not a + * function type. + */ + private final RuntimeCompletionExpressionType returnType; + + /** + * If the type is a function type, the types of the function parameters of all kinds - required, + * optional positional, and optional named. Omitted if the type is not a function type. + */ + private final List parameterTypes; + + /** + * If the type is a function type, the names of the function parameters of all kinds - required, + * optional positional, and optional named. The names of positional parameters are empty strings. + * Omitted if the type is not a function type. + */ + private final List parameterNames; + + /** + * Constructor for {@link RuntimeCompletionExpressionType}. + */ + public RuntimeCompletionExpressionType(String libraryPath, String kind, String name, List typeArguments, RuntimeCompletionExpressionType returnType, List parameterTypes, List parameterNames) { + this.libraryPath = libraryPath; + this.kind = kind; + this.name = name; + this.typeArguments = typeArguments; + this.returnType = returnType; + this.parameterTypes = parameterTypes; + this.parameterNames = parameterNames; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RuntimeCompletionExpressionType) { + RuntimeCompletionExpressionType other = (RuntimeCompletionExpressionType) obj; + return + ObjectUtilities.equals(other.libraryPath, libraryPath) && + ObjectUtilities.equals(other.kind, kind) && + ObjectUtilities.equals(other.name, name) && + ObjectUtilities.equals(other.typeArguments, typeArguments) && + ObjectUtilities.equals(other.returnType, returnType) && + ObjectUtilities.equals(other.parameterTypes, parameterTypes) && + ObjectUtilities.equals(other.parameterNames, parameterNames); + } + return false; + } + + public static RuntimeCompletionExpressionType fromJson(JsonObject jsonObject) { + String libraryPath = jsonObject.get("libraryPath") == null ? null : jsonObject.get("libraryPath").getAsString(); + String kind = jsonObject.get("kind").getAsString(); + String name = jsonObject.get("name") == null ? null : jsonObject.get("name").getAsString(); + List typeArguments = jsonObject.get("typeArguments") == null ? null : RuntimeCompletionExpressionType.fromJsonArray(jsonObject.get("typeArguments").getAsJsonArray()); + RuntimeCompletionExpressionType returnType = jsonObject.get("returnType") == null ? null : RuntimeCompletionExpressionType.fromJson(jsonObject.get("returnType").getAsJsonObject()); + List parameterTypes = jsonObject.get("parameterTypes") == null ? null : RuntimeCompletionExpressionType.fromJsonArray(jsonObject.get("parameterTypes").getAsJsonArray()); + List parameterNames = jsonObject.get("parameterNames") == null ? null : JsonUtilities.decodeStringList(jsonObject.get("parameterNames").getAsJsonArray()); + return new RuntimeCompletionExpressionType(libraryPath, kind, name, typeArguments, returnType, parameterTypes, parameterNames); + } + + 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; + } + + /** + * The kind of the type. + */ + public String getKind() { + return kind; + } + + /** + * The path of the library that has this type. Omitted if the type is not declared in any library, + * e.g. "dynamic", or "void". + */ + public String getLibraryPath() { + return libraryPath; + } + + /** + * The name of the type. Omitted if the type does not have a name, e.g. an inline function type. + */ + public String getName() { + return name; + } + + /** + * If the type is a function type, the names of the function parameters of all kinds - required, + * optional positional, and optional named. The names of positional parameters are empty strings. + * Omitted if the type is not a function type. + */ + public List getParameterNames() { + return parameterNames; + } + + /** + * If the type is a function type, the types of the function parameters of all kinds - required, + * optional positional, and optional named. Omitted if the type is not a function type. + */ + public List getParameterTypes() { + return parameterTypes; + } + + /** + * If the type is a function type, the return type of the function. Omitted if the type is not a + * function type. + */ + public RuntimeCompletionExpressionType getReturnType() { + return returnType; + } + + /** + * The type arguments of the type. Omitted if the type does not have type parameters. + */ + public List getTypeArguments() { + return typeArguments; + } + + @Override + public int hashCode() { + HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(libraryPath); + builder.append(kind); + builder.append(name); + builder.append(typeArguments); + builder.append(returnType); + builder.append(parameterTypes); + builder.append(parameterNames); + return builder.toHashCode(); + } + + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject(); + if (libraryPath != null) { + jsonObject.addProperty("libraryPath", libraryPath); + } + jsonObject.addProperty("kind", kind); + if (name != null) { + jsonObject.addProperty("name", name); + } + if (typeArguments != null) { + JsonArray jsonArrayTypeArguments = new JsonArray(); + for (RuntimeCompletionExpressionType elt : typeArguments) { + jsonArrayTypeArguments.add(elt.toJson()); + } + jsonObject.add("typeArguments", jsonArrayTypeArguments); + } + if (returnType != null) { + jsonObject.add("returnType", returnType.toJson()); + } + if (parameterTypes != null) { + JsonArray jsonArrayParameterTypes = new JsonArray(); + for (RuntimeCompletionExpressionType elt : parameterTypes) { + jsonArrayParameterTypes.add(elt.toJson()); + } + jsonObject.add("parameterTypes", jsonArrayParameterTypes); + } + if (parameterNames != null) { + JsonArray jsonArrayParameterNames = new JsonArray(); + for (String elt : parameterNames) { + jsonArrayParameterNames.add(new JsonPrimitive(elt)); + } + jsonObject.add("parameterNames", jsonArrayParameterNames); + } + return jsonObject; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append("libraryPath="); + builder.append(libraryPath + ", "); + builder.append("kind="); + builder.append(kind + ", "); + builder.append("name="); + builder.append(name + ", "); + builder.append("typeArguments="); + builder.append(StringUtils.join(typeArguments, ", ") + ", "); + builder.append("returnType="); + builder.append(returnType + ", "); + builder.append("parameterTypes="); + builder.append(StringUtils.join(parameterTypes, ", ") + ", "); + builder.append("parameterNames="); + builder.append(StringUtils.join(parameterNames, ", ")); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionTypeKind.java b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionTypeKind.java new file mode 100644 index 00000000000..c544c9fc89a --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionExpressionTypeKind.java @@ -0,0 +1,24 @@ +/* + * 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; + +/** + * An enumeration of the kinds of runtime expression types. + * + * @coverage dart.server.generated.types + */ +public class RuntimeCompletionExpressionTypeKind { + + public static final String DYNAMIC = "DYNAMIC"; + + public static final String FUNCTION = "FUNCTION"; + + public static final String INTERFACE = "INTERFACE"; + +} diff --git a/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionVariable.java b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionVariable.java new file mode 100644 index 00000000000..05e90400528 --- /dev/null +++ b/pkg/analysis_server/tool/spec/generated/java/types/RuntimeCompletionVariable.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 variable in a runtime context. + * + * @coverage dart.server.generated.types + */ +@SuppressWarnings("unused") +public class RuntimeCompletionVariable { + + public static final RuntimeCompletionVariable[] EMPTY_ARRAY = new RuntimeCompletionVariable[0]; + + public static final List EMPTY_LIST = Lists.newArrayList(); + + /** + * The name of the variable. The name "this" has a special meaning and is used as an implicit + * target for runtime completion, and in explicit "this" references. + */ + private final String name; + + /** + * The type of the variable. + */ + private final RuntimeCompletionExpressionType type; + + /** + * Constructor for {@link RuntimeCompletionVariable}. + */ + public RuntimeCompletionVariable(String name, RuntimeCompletionExpressionType type) { + this.name = name; + this.type = type; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RuntimeCompletionVariable) { + RuntimeCompletionVariable other = (RuntimeCompletionVariable) obj; + return + ObjectUtilities.equals(other.name, name) && + ObjectUtilities.equals(other.type, type); + } + return false; + } + + public static RuntimeCompletionVariable fromJson(JsonObject jsonObject) { + String name = jsonObject.get("name").getAsString(); + RuntimeCompletionExpressionType type = RuntimeCompletionExpressionType.fromJson(jsonObject.get("type").getAsJsonObject()); + return new RuntimeCompletionVariable(name, type); + } + + 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; + } + + /** + * The name of the variable. The name "this" has a special meaning and is used as an implicit + * target for runtime completion, and in explicit "this" references. + */ + public String getName() { + return name; + } + + /** + * The type of the variable. + */ + public RuntimeCompletionExpressionType getType() { + return type; + } + + @Override + public int hashCode() { + HashCodeBuilder builder = new HashCodeBuilder(); + builder.append(name); + builder.append(type); + return builder.toHashCode(); + } + + public JsonObject toJson() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name", name); + jsonObject.add("type", type.toJson()); + return jsonObject; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append("name="); + builder.append(name + ", "); + builder.append("type="); + builder.append(type); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html index 0d1c534dcef..90588f91b96 100644 --- a/pkg/analysis_server/tool/spec/spec_input.html +++ b/pkg/analysis_server/tool/spec/spec_input.html @@ -2307,6 +2307,111 @@ + +

+ Request completion suggestions for the given runtime context. +

+

+ It might take one or two requests of this type to get completion + suggestions. The first request should have only "code", "offset", + and "variables", but not "expressions". If there are sub-expressions that + can have different runtime types, and are considered to be safe to + evaluate at runtime (e.g. getters), so using their actual runtime types + can improve completion results, the server will not include the + "suggestions" field in the response, and instead will return the + "expressions" field. The client will use debug API to get current runtime + types for these sub-expressions and send another request, this time with + "expressions". If there are no interesting sub-expressions to get + runtime types for, or when the "expressions" field is provided by the + client, the server will return "suggestions" in the response. +

+ + + String +

+ The code to get suggestions in. +

+
+ + int +

+ The offset within the code to get suggestions at. +

+
+ + FilePath +

+ The path of the context file, e.g. the file of the current debugger + frame. The combination of the context file and context offset can + be used to ensure that all variables of the context are available + for completion (with their static types). +

+
+ + int +

+ The offset in the context file, e.g. the line offset in the current + debugger frame. +

+
+ + + RuntimeCompletionVariable + +

+ The runtime context variables that are potentially referenced in the + code. +

+
+ + + RuntimeCompletionExpression + +

+ The list of sub-expressions in the code for which the client wants + to provide runtime types. It does not have to be the full list of + expressions requested by the server, for missing expressions their + static types will be used. +

+

+ When this field is omitted, the server will return completion + suggestions only when there are no interesting sub-expressions in the + given code. The client may provide an empty list, in this case the + server will return completion suggestions. +

+
+
+ + + + CompletionSuggestion + +

+ The completion suggestions. In contrast to usual completion request, + suggestions for private elements also will be provided. +

+

+ If there are sub-expressions that can have different runtime types, + and are considered to be safe to evaluate at runtime (e.g. getters), + so using their actual runtime types can improve completion results, + the server omits this field in the response, and instead will return + the "expressions" field. +

+
+ + + RuntimeCompletionExpression + +

+ The list of sub-expressions in the code for which the server would + like to know runtime types to provide better completion suggestions. +

+

+ This field is omitted the field "suggestions" is returned. +

+
+
+

Map a URI from the execution context to the file that it corresponds @@ -3075,6 +3180,132 @@ The identifier for a execution context.

+ +

+ 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`. +

+ + + int +

+ The offset of the expression in the code for completion. +

+
+ + int +

+ The length of the expression in the code for completion. +

+
+ + RuntimeCompletionExpressionType +

+ When the expression is sent from the server to the client, the + type is omitted. The client should fill the type when it sends the + request to the server again. +

+
+
+
+ +

+ A variable in a runtime context. +

+ + + String +

+ The name of the variable. + The name "this" has a special meaning and is used as an implicit + target for runtime completion, and in explicit "this" references. +

+
+ + RuntimeCompletionExpressionType +

+ The type of the variable. +

+
+
+
+ +

+ A type at runtime. +

+ + + FilePath +

+ The path of the library that has this type. + Omitted if the type is not declared in any library, e.g. "dynamic", + or "void". +

+
+ + RuntimeCompletionExpressionTypeKind +

+ The kind of the type. +

+
+ + String +

+ The name of the type. Omitted if the type does not have a name, e.g. + an inline function type. +

+
+ + + RuntimeCompletionExpressionType + +

+ The type arguments of the type. + Omitted if the type does not have type parameters. +

+
+ + RuntimeCompletionExpressionType +

+ If the type is a function type, the return type of the function. + Omitted if the type is not a function type. +

+
+ + + RuntimeCompletionExpressionType + +

+ If the type is a function type, the types of the function parameters + of all kinds - required, optional positional, and optional named. + Omitted if the type is not a function type. +

+
+ + + String + +

+ If the type is a function type, the names of the function parameters + of all kinds - required, optional positional, and optional named. + The names of positional parameters are empty strings. + Omitted if the type is not a function type. +

+
+
+
+ +

+ An enumeration of the kinds of runtime expression types. +

+ + DYNAMIC + FUNCTION + INTERFACE + +

An enumeration of the services provided by the execution