step toward new completion API
- separate new CompletionContributor and DartCompletionContributor APIs - revise new APIs and implementations to be Futures based - revise InheritedContributor to use new Futures based contributor API - remove unused classes R=brianwilkerson@google.com, scheglov@google.com Review URL: https://codereview.chromium.org/1467023002 .
This commit is contained in:
@@ -4,12 +4,20 @@
|
||||
|
||||
library analysis_server.src.provisional.completion.completion_core;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/task/model.dart';
|
||||
|
||||
/**
|
||||
* An empty list returned by [CompletionContributor]s
|
||||
* when they have no suggestions to contribute.
|
||||
*/
|
||||
const EMPTY_LIST = const <CompletionSuggestion>[];
|
||||
|
||||
/**
|
||||
* A method or function called when the requested analysis has been performed.
|
||||
*/
|
||||
@@ -41,14 +49,15 @@ class AnalysisRequest<V> {
|
||||
/**
|
||||
* An object used to produce completions at a specific location within a file.
|
||||
*
|
||||
* Clients may extend this class when implementing plugins.
|
||||
* Clients may implement this class when implementing plugins.
|
||||
*/
|
||||
abstract class CompletionContributor {
|
||||
/**
|
||||
* Compute a list of completion suggestions based on the given completion
|
||||
* [request]. Return the suggestions that were computed.
|
||||
* Return a [Future] that completes with a list of suggestions
|
||||
* for the given completion [request].
|
||||
*/
|
||||
List<CompletionSuggestion> computeSuggestions(CompletionRequest request);
|
||||
Future<List<CompletionSuggestion>> computeSuggestions(
|
||||
CompletionRequest request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,55 +4,25 @@
|
||||
|
||||
library analysis_server.src.provisional.completion.completion_dart;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart';
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
|
||||
/**
|
||||
* An object used to produce completions for a specific error within a Dart
|
||||
* file. Completion contributors are long-lived objects and must not retain any
|
||||
* state between invocations of [computeSuggestions].
|
||||
* An object used to produce completions
|
||||
* at a specific location within a Dart file.
|
||||
*
|
||||
* Clients may extend this class when implementing plugins.
|
||||
* Clients may implement this class when implementing plugins.
|
||||
*/
|
||||
abstract class DartCompletionContributor implements CompletionContributor {
|
||||
@override
|
||||
List<CompletionSuggestion> computeSuggestions(CompletionRequest request) {
|
||||
if (request is DartCompletionRequest) {
|
||||
return internalComputeSuggestions(request);
|
||||
}
|
||||
AnalysisContext context = request.context;
|
||||
Source source = request.source;
|
||||
List<Source> libraries = context.getLibrariesContaining(source);
|
||||
if (libraries.length < 1) {
|
||||
return null;
|
||||
}
|
||||
// CompilationUnit unit =
|
||||
// context.getResolvedCompilationUnit2(source, libraries[0]);
|
||||
// bool isResolved = true;
|
||||
// if (unit == null) {
|
||||
// // TODO(brianwilkerson) Implement a method for getting a parsed
|
||||
// // compilation unit without parsing the unit if it hasn't been parsed.
|
||||
// unit = context.getParsedCompilationUnit(source);
|
||||
// if (unit == null) {
|
||||
// return null;
|
||||
// }
|
||||
// isResolved = false;
|
||||
// }
|
||||
// DartCompletionRequest dartRequest =
|
||||
// new DartCompletionRequestImpl(request, unit, isResolved);
|
||||
// return internalComputeSuggestions(dartRequest);
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract class DartCompletionContributor {
|
||||
/**
|
||||
* Compute a list of completion suggestions based on the given completion
|
||||
* [request]. Return the suggestions that were computed.
|
||||
* Return a [Future] that completes with a list of suggestions
|
||||
* for the given completion [request].
|
||||
*/
|
||||
List<CompletionSuggestion> internalComputeSuggestions(
|
||||
Future<List<CompletionSuggestion>> computeSuggestions(
|
||||
DartCompletionRequest request);
|
||||
}
|
||||
|
||||
@@ -62,11 +32,6 @@ abstract class DartCompletionContributor implements CompletionContributor {
|
||||
* Clients may not extend, implement or mix-in this class.
|
||||
*/
|
||||
abstract class DartCompletionRequest extends CompletionRequest {
|
||||
/**
|
||||
* Return `true` if the compilation [unit] is resolved.
|
||||
*/
|
||||
bool get isResolved;
|
||||
|
||||
/**
|
||||
* Return the completion target. This determines what part of the parse tree
|
||||
* will receive the newly inserted text.
|
||||
@@ -74,17 +39,12 @@ abstract class DartCompletionRequest extends CompletionRequest {
|
||||
CompletionTarget get target;
|
||||
|
||||
/**
|
||||
* Cached information from a prior code completion operation.
|
||||
* Return a [Future] that completes with a compilation unit in which
|
||||
* all declarations in all scopes containing [target] have been resolved.
|
||||
* The [Future] may return `null` if the unit cannot be resolved
|
||||
* (e.g. unlinked part file).
|
||||
* Any information obtained from [target] prior to calling this method
|
||||
* should be discarded as it may have changed.
|
||||
*/
|
||||
//DartCompletionCache get cache;
|
||||
|
||||
/**
|
||||
* Return the compilation unit in which the completion was requested.
|
||||
*/
|
||||
CompilationUnit get unit;
|
||||
|
||||
/**
|
||||
* Information about the types of suggestions that should be included.
|
||||
*/
|
||||
//OpType get _optype;
|
||||
Future<CompilationUnit> resolveDeclarationsInScope();
|
||||
}
|
||||
|
||||
@@ -4,11 +4,21 @@
|
||||
|
||||
library analysis_server.src.services.completion.completion_dart;
|
||||
|
||||
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/src/provisional/completion/completion_core.dart'
|
||||
show CompletionRequest;
|
||||
import 'package:analysis_server/src/provisional/completion/completion_dart.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart';
|
||||
import 'package:analysis_server/src/services/completion/completion_core.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/context/context.dart'
|
||||
show AnalysisFutureHelper, AnalysisContextImpl;
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl;
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/task/dart.dart';
|
||||
import 'package:analyzer/task/dart.dart';
|
||||
|
||||
/**
|
||||
* The information about a requested list of completions within a Dart file.
|
||||
@@ -16,26 +26,75 @@ import 'package:analyzer/src/generated/ast.dart';
|
||||
class DartCompletionRequestImpl extends CompletionRequestImpl
|
||||
implements DartCompletionRequest {
|
||||
/**
|
||||
* The compilation unit in which the completion was requested.
|
||||
* The cached completion target or `null` if not computed yet.
|
||||
*/
|
||||
final CompilationUnit unit;
|
||||
CompletionTarget _target;
|
||||
|
||||
/**
|
||||
* A flag indicating whether the compilation [unit] is resolved.
|
||||
* `true` if [resolveDeclarationsInScope] has partially resolved the unit
|
||||
* referenced by [target], else `false`.
|
||||
*/
|
||||
final bool isResolved;
|
||||
bool _haveResolveDeclarationsInScope = false;
|
||||
|
||||
/**
|
||||
* The completion target. This determines what part of the parse tree
|
||||
* will receive the newly inserted text.
|
||||
* Initialize a newly created completion request based on the given request.
|
||||
*/
|
||||
final CompletionTarget target;
|
||||
factory DartCompletionRequestImpl.forRequest(CompletionRequest request) {
|
||||
return new DartCompletionRequestImpl._(request.context,
|
||||
request.resourceProvider, request.source, request.offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a newly created completion request based on the given arguments.
|
||||
*/
|
||||
DartCompletionRequestImpl(
|
||||
CompletionRequest request, this.unit, this.isResolved, this.target)
|
||||
: super(request.context, request.resourceProvider, request.source,
|
||||
request.offset);
|
||||
DartCompletionRequestImpl._(AnalysisContext context,
|
||||
ResourceProvider resourceProvider, Source source, int offset)
|
||||
: super(context, resourceProvider, source, offset);
|
||||
|
||||
@override
|
||||
Future<CompilationUnit> resolveDeclarationsInScope() async {
|
||||
CompilationUnit unit = target.unit;
|
||||
if (_haveResolveDeclarationsInScope) {
|
||||
return unit;
|
||||
}
|
||||
|
||||
// Determine the library source
|
||||
Source librarySource;
|
||||
if (unit.directives.any((d) => d is PartOfDirective)) {
|
||||
List<Source> libraries = context.getLibrariesContaining(source);
|
||||
if (libraries.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
librarySource = libraries[0];
|
||||
} else {
|
||||
librarySource = source;
|
||||
}
|
||||
|
||||
// Resolve declarations in the target unit
|
||||
CompilationUnit resolvedUnit =
|
||||
await new AnalysisFutureHelper<CompilationUnit>(
|
||||
context,
|
||||
new LibrarySpecificUnit(librarySource, source),
|
||||
RESOLVED_UNIT3).computeAsync();
|
||||
|
||||
// TODO(danrubel) determine if the underlying source has been modified
|
||||
// in a way that invalidates the completion request
|
||||
// and return null
|
||||
|
||||
// Gracefully degrade if unit cannot be resolved
|
||||
if (resolvedUnit == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Recompute the target for the newly resolved unit
|
||||
_target = new CompletionTarget.forOffset(resolvedUnit, offset);
|
||||
_haveResolveDeclarationsInScope = true;
|
||||
return resolvedUnit;
|
||||
}
|
||||
|
||||
@override
|
||||
CompletionTarget get target {
|
||||
if (_target == null) {
|
||||
CompilationUnit unit = context.computeResult(source, PARSED_UNIT);
|
||||
_target = new CompletionTarget.forOffset(unit, offset);
|
||||
}
|
||||
return _target;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
library services.completion.computer.dart.invocation;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/src/protocol_server.dart'
|
||||
show CompletionSuggestion, CompletionSuggestionKind, SourceChange;
|
||||
import 'package:analysis_server/src/protocol_server.dart' as protocol
|
||||
hide CompletionSuggestion, CompletionSuggestionKind;
|
||||
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/completion_dart.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart'
|
||||
show DART_RELEVANCE_HIGH;
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
// import 'package:analysis_server/plugin/edit/utilities/change_builder_dart.dart';
|
||||
// import 'package:analyzer/src/generated/engine.dart';
|
||||
|
||||
/**
|
||||
* A completion contributor used to suggest replacing partial identifiers inside
|
||||
* a class declaration with templates for inherited members.
|
||||
*/
|
||||
class InheritedContributor implements DartCompletionContributor {
|
||||
@override
|
||||
Future<List<CompletionSuggestion>> computeSuggestions(
|
||||
DartCompletionRequest request) async {
|
||||
// Determine if the target looks like a partial identifier
|
||||
// inside a class declaration
|
||||
SimpleIdentifier targetId = _getTargetId(request.target);
|
||||
if (targetId == null) {
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
|
||||
// Partially resolve the compilation unit
|
||||
CompilationUnit unit = await request.resolveDeclarationsInScope();
|
||||
// Gracefully degrade if the compilation unit could not be resolved
|
||||
// e.g. detached part file or source change
|
||||
if (unit == null) {
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
|
||||
// Recompute the target since resolution may have changed it
|
||||
targetId = _getTargetId(request.target);
|
||||
if (targetId == null) {
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
ClassDeclaration classDecl =
|
||||
targetId.getAncestor((p) => p is ClassDeclaration);
|
||||
if (classDecl == null) {
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
|
||||
// Generate a collection of inherited members
|
||||
ClassElement classElem = classDecl.element;
|
||||
InheritanceManager manager = new InheritanceManager(classElem.library);
|
||||
MemberMap map = manager.getMapOfMembersInheritedFromInterfaces(classElem);
|
||||
List<String> memberNames = _computeMemberNames(map, classElem);
|
||||
|
||||
// Build suggestions
|
||||
List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
|
||||
for (String memberName in memberNames) {
|
||||
ExecutableElement element = map.get(memberName);
|
||||
// Gracefully degrade if the overridden element has not been resolved.
|
||||
if (element.returnType != null) {
|
||||
CompletionSuggestion suggestion =
|
||||
_buildSuggestion(request, targetId, unit, element);
|
||||
if (suggestion != null) {
|
||||
suggestions.add(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the target looks like a partial identifier inside a class declaration
|
||||
* then return that identifier, otherwise return `null`.
|
||||
*/
|
||||
SimpleIdentifier _getTargetId(CompletionTarget target) {
|
||||
AstNode node = target.containingNode;
|
||||
if (node is ClassDeclaration) {
|
||||
Object entity = target.entity;
|
||||
if (entity is FieldDeclaration) {
|
||||
NodeList<VariableDeclaration> variables = entity.fields.variables;
|
||||
if (variables.length == 1) {
|
||||
SimpleIdentifier targetId = variables[0].name;
|
||||
if (targetId.name.isEmpty) {
|
||||
return targetId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a template for an override of the given [element] in the given
|
||||
* [source]. If selected, the template will replace [targetId].
|
||||
*/
|
||||
String _buildRepacementText(Source source, SimpleIdentifier targetId,
|
||||
CompilationUnit unit, ExecutableElement element) {
|
||||
// AnalysisContext context = element.context;
|
||||
// Inject partially resolved unit for use by change builder
|
||||
// DartChangeBuilder builder = new DartChangeBuilder(context, unit);
|
||||
// builder.addFileEdit(source, context.getModificationStamp(source),
|
||||
// (DartFileEditBuilder builder) {
|
||||
// builder.addReplacement(targetId.offset, targetId.length,
|
||||
// (DartEditBuilder builder) {
|
||||
// builder.writeOverrideOfInheritedMember(element);
|
||||
// });
|
||||
// });
|
||||
// return builder.sourceChange.edits[0].edits[0].replacement.trim();
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a suggestion to replace [targetId] in the given [unit]
|
||||
* with an override of the given [element].
|
||||
*/
|
||||
CompletionSuggestion _buildSuggestion(
|
||||
DartCompletionRequest request,
|
||||
SimpleIdentifier targetId,
|
||||
CompilationUnit unit,
|
||||
ExecutableElement element) {
|
||||
String completion =
|
||||
_buildRepacementText(request.source, targetId, unit, element);
|
||||
if (completion == null || completion.length == 0) {
|
||||
return null;
|
||||
}
|
||||
CompletionSuggestion suggestion = new CompletionSuggestion(
|
||||
CompletionSuggestionKind.IDENTIFIER,
|
||||
DART_RELEVANCE_HIGH,
|
||||
completion,
|
||||
targetId.offset,
|
||||
0,
|
||||
element.isDeprecated,
|
||||
false);
|
||||
suggestion.element = protocol.convertElement(element);
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list containing the names of all of the inherited but not
|
||||
* implemented members of the class represented by the given [element].
|
||||
* The [map] is used to find all of the members that are inherited.
|
||||
*/
|
||||
List<String> _computeMemberNames(MemberMap map, ClassElement element) {
|
||||
List<String> memberNames = <String>[];
|
||||
int count = map.size;
|
||||
for (int i = 0; i < count; i++) {
|
||||
String memberName = map.getKey(i);
|
||||
if (!_hasMember(element, memberName)) {
|
||||
memberNames.add(memberName);
|
||||
}
|
||||
}
|
||||
return memberNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the given [classElement] directly declares a member with
|
||||
* the given [memberName].
|
||||
*/
|
||||
bool _hasMember(ClassElement classElement, String memberName) {
|
||||
return classElement.getField(memberName) != null ||
|
||||
classElement.getGetter(memberName) != null ||
|
||||
classElement.getMethod(memberName) != null ||
|
||||
classElement.getSetter(memberName) != null;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import 'package:analysis_server/plugin/protocol/protocol.dart';
|
||||
import 'package:analysis_server/src/analysis_server.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/completion_core.dart'
|
||||
show AnalysisRequest, CompletionRequest;
|
||||
import 'package:analysis_server/src/provisional/completion/completion_dart.dart'
|
||||
as newApi;
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart';
|
||||
import 'package:analysis_server/src/services/completion/arglist_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/combinator_contributor.dart';
|
||||
@@ -26,15 +24,12 @@ import 'package:analysis_server/src/services/completion/optype.dart';
|
||||
import 'package:analysis_server/src/services/completion/prefixed_element_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/uri_contributor.dart';
|
||||
import 'package:analysis_server/src/services/search/search_engine.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/cancelable_future.dart';
|
||||
import 'package:analyzer/src/context/context.dart'
|
||||
show AnalysisFutureHelper, AnalysisContextImpl;
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl;
|
||||
import 'package:analyzer/src/generated/scanner.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/task/model.dart';
|
||||
|
||||
const int DART_RELEVANCE_COMMON_USAGE = 1200;
|
||||
const int DART_RELEVANCE_DEFAULT = 1000;
|
||||
@@ -459,82 +454,3 @@ class DartCompletionRequest extends CompletionRequestImpl {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around a new dart completion contributor that makes it usable where
|
||||
* an old dart completion contributor is expected.
|
||||
*/
|
||||
class NewCompletionWrapper implements DartCompletionContributor {
|
||||
/**
|
||||
* The new-style contributor that is being wrapped.
|
||||
*/
|
||||
final newApi.DartCompletionContributor contributor;
|
||||
|
||||
/**
|
||||
* Initialize a newly created wrapper for the given [contributor].
|
||||
*/
|
||||
NewCompletionWrapper(this.contributor);
|
||||
|
||||
@override
|
||||
bool computeFast(DartCompletionRequest request) {
|
||||
List<CompletionSuggestion> suggestions =
|
||||
contributor.computeSuggestions(new OldRequestWrapper(request));
|
||||
if (suggestions == null) {
|
||||
return false;
|
||||
}
|
||||
for (CompletionSuggestion suggestion in suggestions) {
|
||||
request.addSuggestion(suggestion);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> computeFull(DartCompletionRequest request) async {
|
||||
List<CompletionSuggestion> suggestions =
|
||||
contributor.computeSuggestions(new OldRequestWrapper(request));
|
||||
if (suggestions != null) {
|
||||
for (CompletionSuggestion suggestion in suggestions) {
|
||||
request.addSuggestion(suggestion);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'wrapped $contributor';
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around an old dart completion request that makes it usable where a
|
||||
* new dart completion request is expected.
|
||||
*/
|
||||
class OldRequestWrapper implements newApi.DartCompletionRequest {
|
||||
final DartCompletionRequest request;
|
||||
|
||||
OldRequestWrapper(this.request);
|
||||
|
||||
@override
|
||||
AnalysisContext get context => request.context;
|
||||
|
||||
@override
|
||||
bool get isResolved => request.unit.element != null;
|
||||
|
||||
@override
|
||||
int get offset => request.offset;
|
||||
|
||||
@override
|
||||
ResourceProvider get resourceProvider => request.resourceProvider;
|
||||
|
||||
@override
|
||||
Source get source => request.source;
|
||||
|
||||
@override
|
||||
CompletionTarget get target => request.target;
|
||||
|
||||
@override
|
||||
CompilationUnit get unit => request.unit;
|
||||
|
||||
@override
|
||||
String toString() => 'wrapped $request';
|
||||
}
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
library services.completion.computer.dart.invocation;
|
||||
|
||||
import 'package:analysis_server/plugin/edit/utilities/change_builder_dart.dart';
|
||||
import 'package:analysis_server/src/protocol_server.dart'
|
||||
show CompletionSuggestion, CompletionSuggestionKind, SourceChange;
|
||||
import 'package:analysis_server/src/protocol_server.dart' as protocol
|
||||
hide CompletionSuggestion, CompletionSuggestionKind;
|
||||
import 'package:analysis_server/src/provisional/completion/completion_dart.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart'
|
||||
show DART_RELEVANCE_HIGH;
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
|
||||
/**
|
||||
* A completion contributor used to suggest replacing partial identifiers inside
|
||||
* a class declaration with templates for inherited members.
|
||||
*/
|
||||
class InheritedContributor extends DartCompletionContributor {
|
||||
@override
|
||||
List<CompletionSuggestion> internalComputeSuggestions(
|
||||
DartCompletionRequest request) {
|
||||
if (!request.isResolved) {
|
||||
return null;
|
||||
}
|
||||
AstNode node = new NodeLocator(request.offset).searchWithin(request.unit);
|
||||
if (node == null || !_isMemberLevelIdentifier(node)) {
|
||||
return null;
|
||||
}
|
||||
ClassDeclaration classDeclaration =
|
||||
node.getAncestor((AstNode node) => node is ClassDeclaration);
|
||||
if (classDeclaration != null) {
|
||||
ClassElement element = classDeclaration.element;
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return _suggestInheritedMembers(request, node, element);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a template for an override of the given [element] in the given
|
||||
* [source]. If selected, the template will replace the given [identifier].
|
||||
*/
|
||||
String _buildRepacementText(
|
||||
Source source, SimpleIdentifier identifier, Element element) {
|
||||
AnalysisContext context = element.context;
|
||||
DartChangeBuilder builder = new DartChangeBuilder(context);
|
||||
builder.addFileEdit(source, context.getModificationStamp(source),
|
||||
(DartFileEditBuilder builder) {
|
||||
builder.addReplacement(identifier.offset, identifier.length,
|
||||
(DartEditBuilder builder) {
|
||||
builder.writeOverrideOfInheritedMember(element);
|
||||
});
|
||||
});
|
||||
return builder.sourceChange.edits[0].edits[0].replacement.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a suggestion to replace the partial [identifier] in the given
|
||||
* [source] with an override of the given [element].
|
||||
*/
|
||||
CompletionSuggestion _buildSuggestion(
|
||||
Source source, SimpleIdentifier identifier, Element element) {
|
||||
String completion = _buildRepacementText(source, identifier, element);
|
||||
CompletionSuggestion suggestion = new CompletionSuggestion(
|
||||
CompletionSuggestionKind.IDENTIFIER,
|
||||
DART_RELEVANCE_HIGH,
|
||||
completion,
|
||||
identifier.offset,
|
||||
0,
|
||||
element.isDeprecated,
|
||||
false);
|
||||
suggestion.element = protocol.convertElement(element);
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list containing the names of all of the inherited by not
|
||||
* implemented members of the class represented by the given [element] that
|
||||
* start with the given [prefix]. The [map] is used to find all of the members
|
||||
* that are inherited.
|
||||
*/
|
||||
List<String> _computeMemberNames(
|
||||
MemberMap map, ClassElement element, String prefix) {
|
||||
List<String> memberNames = <String>[];
|
||||
int count = map.size;
|
||||
for (int i = 0; i < count; i++) {
|
||||
String memberName = map.getKey(i);
|
||||
if (memberName.startsWith(prefix) && !_hasMember(element, memberName)) {
|
||||
memberNames.add(memberName);
|
||||
}
|
||||
}
|
||||
return memberNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the given [classElement] directly declares a member with
|
||||
* the given [memberName].
|
||||
*/
|
||||
bool _hasMember(ClassElement classElement, String memberName) {
|
||||
return classElement.getField(memberName) != null ||
|
||||
classElement.getGetter(memberName) != null ||
|
||||
classElement.getMethod(memberName) != null ||
|
||||
classElement.getSetter(memberName) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the given [node] looks like a partial identifier inside a
|
||||
* class declaration.
|
||||
*/
|
||||
bool _isMemberLevelIdentifier(AstNode node) {
|
||||
if (node is SimpleIdentifier) {
|
||||
AstNode parent1 = node.parent;
|
||||
if (parent1 is TypeName) {
|
||||
AstNode parent2 = parent1.parent;
|
||||
if (parent2 is VariableDeclarationList) {
|
||||
AstNode parent3 = parent2.parent;
|
||||
if (parent3 is FieldDeclaration) {
|
||||
NodeList<VariableDeclaration> variables = parent2.variables;
|
||||
return variables.length == 1 && variables[0].name.name.isEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any suggestions that are appropriate to the given [request], using the
|
||||
* given [element] to find inherited members whose name has the given
|
||||
* [identifier] as a prefix.
|
||||
*/
|
||||
List<CompletionSuggestion> _suggestInheritedMembers(
|
||||
DartCompletionRequest request,
|
||||
SimpleIdentifier identifier,
|
||||
ClassElement element) {
|
||||
String name = identifier.name;
|
||||
InheritanceManager manager = new InheritanceManager(element.library);
|
||||
MemberMap map = manager.getMapOfMembersInheritedFromInterfaces(element);
|
||||
List<String> memberNames = _computeMemberNames(map, element, name);
|
||||
memberNames.sort();
|
||||
List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
|
||||
for (String memberName in memberNames) {
|
||||
CompletionSuggestion suggestion =
|
||||
_buildSuggestion(request.source, identifier, map.get(memberName));
|
||||
if (suggestion != null) {
|
||||
suggestions.add(suggestion);
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Copyright (c) 2014, 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.
|
||||
|
||||
library test.services.completion.contributor.dart;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart' as protocol
|
||||
show Element, ElementKind;
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart'
|
||||
hide Element, ElementKind;
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart';
|
||||
import 'package:analysis_server/src/provisional/completion/completion_core.dart'
|
||||
show AnalysisRequest, CompletionContributor, CompletionRequest;
|
||||
import 'package:analysis_server/src/provisional/completion/completion_dart.dart';
|
||||
import 'package:analysis_server/src/services/completion/completion_dart.dart'
|
||||
show DartCompletionRequestImpl;
|
||||
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart'
|
||||
show DART_RELEVANCE_DEFAULT, DART_RELEVANCE_LOW;
|
||||
import 'package:analysis_server/src/services/index/index.dart';
|
||||
import 'package:analysis_server/src/services/index/local_memory_index.dart';
|
||||
import 'package:analysis_server/src/services/search/search_engine_internal.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
import '../../../abstract_context.dart';
|
||||
import 'package:analysis_server/src/services/completion/completion_core.dart';
|
||||
|
||||
int suggestionComparator(CompletionSuggestion s1, CompletionSuggestion s2) {
|
||||
String c1 = s1.completion.toLowerCase();
|
||||
String c2 = s2.completion.toLowerCase();
|
||||
return c1.compareTo(c2);
|
||||
}
|
||||
|
||||
abstract class DartCompletionContributorTest extends AbstractContextTest {
|
||||
Index index;
|
||||
SearchEngineImpl searchEngine;
|
||||
String testFile = '/completionTest.dart';
|
||||
Source testSource;
|
||||
int completionOffset;
|
||||
DartCompletionContributor contributor;
|
||||
CompletionRequest request;
|
||||
List<CompletionSuggestion> suggestions;
|
||||
|
||||
void addTestSource(String content) {
|
||||
expect(completionOffset, isNull, reason: 'Call addTestUnit exactly once');
|
||||
completionOffset = content.indexOf('^');
|
||||
expect(completionOffset, isNot(equals(-1)), reason: 'missing ^');
|
||||
int nextOffset = content.indexOf('^', completionOffset + 1);
|
||||
expect(nextOffset, equals(-1), reason: 'too many ^');
|
||||
content = content.substring(0, completionOffset) +
|
||||
content.substring(completionOffset + 1);
|
||||
testSource = addSource(testFile, content);
|
||||
}
|
||||
|
||||
void assertHasParameterInfo(CompletionSuggestion suggestion) {
|
||||
expect(suggestion.parameterNames, isNotNull);
|
||||
expect(suggestion.parameterTypes, isNotNull);
|
||||
expect(suggestion.parameterNames.length, suggestion.parameterTypes.length);
|
||||
expect(suggestion.requiredParameterCount,
|
||||
lessThanOrEqualTo(suggestion.parameterNames.length));
|
||||
expect(suggestion.hasNamedParameters, isNotNull);
|
||||
}
|
||||
|
||||
void assertNotSuggested(String completion) {
|
||||
CompletionSuggestion suggestion = suggestions.firstWhere(
|
||||
(CompletionSuggestion cs) => cs.completion == completion,
|
||||
orElse: () => null);
|
||||
if (suggestion != null) {
|
||||
failedCompletion('did not expect completion: $completion\n $suggestion');
|
||||
}
|
||||
}
|
||||
|
||||
CompletionSuggestion assertSuggest(String completion,
|
||||
{CompletionSuggestionKind csKind: CompletionSuggestionKind.INVOCATION,
|
||||
int relevance: DART_RELEVANCE_DEFAULT,
|
||||
String importUri,
|
||||
protocol.ElementKind elemKind: null,
|
||||
bool isDeprecated: false,
|
||||
bool isPotential: false,
|
||||
String elemFile,
|
||||
int elemOffset}) {
|
||||
CompletionSuggestion cs =
|
||||
getSuggest(completion: completion, csKind: csKind, elemKind: elemKind);
|
||||
if (cs == null) {
|
||||
failedCompletion('expected $completion $csKind $elemKind', suggestions);
|
||||
}
|
||||
expect(cs.kind, equals(csKind));
|
||||
if (isDeprecated) {
|
||||
expect(cs.relevance, equals(DART_RELEVANCE_LOW));
|
||||
} else {
|
||||
expect(cs.relevance, equals(relevance));
|
||||
}
|
||||
expect(cs.importUri, importUri);
|
||||
expect(cs.selectionOffset, equals(completion.length));
|
||||
expect(cs.selectionLength, equals(0));
|
||||
expect(cs.isDeprecated, equals(isDeprecated));
|
||||
expect(cs.isPotential, equals(isPotential));
|
||||
if (cs.element != null) {
|
||||
expect(cs.element.location, isNotNull);
|
||||
expect(cs.element.location.file, isNotNull);
|
||||
expect(cs.element.location.offset, isNotNull);
|
||||
expect(cs.element.location.length, isNotNull);
|
||||
expect(cs.element.location.startColumn, isNotNull);
|
||||
expect(cs.element.location.startLine, isNotNull);
|
||||
}
|
||||
if (elemFile != null) {
|
||||
expect(cs.element.location.file, elemFile);
|
||||
}
|
||||
if (elemOffset != null) {
|
||||
expect(cs.element.location.offset, elemOffset);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a [Future] that completes with the containing library information
|
||||
* after it is accessible via [context.getLibrariesContaining].
|
||||
*/
|
||||
Future computeLibrariesContaining([int times = 200]) {
|
||||
List<Source> libraries = context.getLibrariesContaining(testSource);
|
||||
if (libraries.isNotEmpty) {
|
||||
return new Future.value(libraries);
|
||||
}
|
||||
context.performAnalysisTask();
|
||||
// We use a delayed future to allow microtask events to finish. The
|
||||
// Future.value or Future() constructors use scheduleMicrotask themselves and
|
||||
// would therefore not wait for microtask callbacks that are scheduled after
|
||||
// invoking this method.
|
||||
return new Future.delayed(
|
||||
Duration.ZERO, () => computeLibrariesContaining(times - 1));
|
||||
}
|
||||
|
||||
Future computeSuggestions([int times = 200]) async {
|
||||
CompletionRequestImpl baseRequest = new CompletionRequestImpl(
|
||||
context, provider, testSource, completionOffset);
|
||||
request = new DartCompletionRequestImpl.forRequest(baseRequest);
|
||||
Completer<List<CompletionSuggestion>> completer =
|
||||
new Completer<List<CompletionSuggestion>>();
|
||||
|
||||
// Request completions
|
||||
contributor
|
||||
.computeSuggestions(request)
|
||||
.then((List<CompletionSuggestion> computedSuggestions) {
|
||||
completer.complete(computedSuggestions);
|
||||
});
|
||||
|
||||
// Perform analysis until the suggestions have been computed
|
||||
// or the max analysis cycles ([times]) has been reached
|
||||
suggestions = await performAnalysis(times, completer);
|
||||
expect(suggestions, isNotNull, reason: 'expected suggestions');
|
||||
}
|
||||
|
||||
DartCompletionContributor createContributor();
|
||||
|
||||
void failedCompletion(String message,
|
||||
[Iterable<CompletionSuggestion> completions]) {
|
||||
StringBuffer sb = new StringBuffer(message);
|
||||
if (completions != null) {
|
||||
sb.write('\n found');
|
||||
completions.toList()
|
||||
..sort(suggestionComparator)
|
||||
..forEach((CompletionSuggestion suggestion) {
|
||||
sb.write('\n ${suggestion.completion} -> $suggestion');
|
||||
});
|
||||
}
|
||||
fail(sb.toString());
|
||||
}
|
||||
|
||||
CompletionSuggestion getSuggest(
|
||||
{String completion: null,
|
||||
CompletionSuggestionKind csKind: null,
|
||||
protocol.ElementKind elemKind: null}) {
|
||||
CompletionSuggestion cs;
|
||||
if (suggestions != null) {
|
||||
suggestions.forEach((CompletionSuggestion s) {
|
||||
if (completion != null && completion != s.completion) {
|
||||
return;
|
||||
}
|
||||
if (csKind != null && csKind != s.kind) {
|
||||
return;
|
||||
}
|
||||
if (elemKind != null) {
|
||||
protocol.Element element = s.element;
|
||||
if (element == null || elemKind != element.kind) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cs == null) {
|
||||
cs = s;
|
||||
} else {
|
||||
failedCompletion('expected exactly one $cs',
|
||||
suggestions.where((s) => s.completion == completion));
|
||||
}
|
||||
});
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
Future performAnalysis(int times, Completer completer) {
|
||||
if (completer.isCompleted) return completer.future;
|
||||
if (times == 0 || context == null) return new Future.value();
|
||||
context.performAnalysisTask();
|
||||
// We use a delayed future to allow microtask events to finish. The
|
||||
// Future.value or Future() constructors use scheduleMicrotask themselves and
|
||||
// would therefore not wait for microtask callbacks that are scheduled after
|
||||
// invoking this method.
|
||||
return new Future.delayed(
|
||||
Duration.ZERO, () => performAnalysis(times - 1, completer));
|
||||
}
|
||||
|
||||
@override
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
index = createLocalMemoryIndex();
|
||||
searchEngine = new SearchEngineImpl(index);
|
||||
contributor = createContributor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
library test.services.completion.inherited_computer_test;
|
||||
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart'
|
||||
hide Element, ElementKind;
|
||||
import 'package:analysis_server/src/provisional/completion/completion_dart.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/inherited_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart'
|
||||
hide DartCompletionContributor;
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
// import '../../../utils.dart';
|
||||
import 'dart_completion_contributor_test.dart';
|
||||
|
||||
main() {
|
||||
// Revisit this contributor and these tests
|
||||
// once DartChangeBuilder API has solidified.
|
||||
// initializeTestEnvironment();
|
||||
// defineReflectiveTests(InheritedContributorTest);
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class InheritedContributorTest extends DartCompletionContributorTest {
|
||||
@override
|
||||
DartCompletionContributor createContributor() {
|
||||
return new InheritedContributor();
|
||||
}
|
||||
|
||||
test_fromMultipleSuperclasses() async {
|
||||
addTestSource(r'''
|
||||
class A {
|
||||
A suggested1(int x) => null;
|
||||
B suggested2(String y) => null;
|
||||
}
|
||||
class B extends A {
|
||||
B suggested2(String y) => null;
|
||||
C suggested3([String z]) => null;
|
||||
}
|
||||
class C extends B {
|
||||
sugg^
|
||||
}
|
||||
''');
|
||||
await computeSuggestions();
|
||||
_assertOverride('''@override
|
||||
A suggested1(int x) {
|
||||
// TODO: implement suggested1
|
||||
return null;
|
||||
}''');
|
||||
_assertOverride(
|
||||
'''@override\n A suggested1(int x) {\n // TODO: implement suggested1\n return null;\n }''');
|
||||
_assertOverride(
|
||||
'''@override\n B suggested2(String y) {\n // TODO: implement suggested2\n return null;\n }''');
|
||||
_assertOverride(
|
||||
'''@override\n C suggested3([String z]) {\n // TODO: implement suggested3\n return null;\n }''');
|
||||
}
|
||||
|
||||
test_fromPart() async {
|
||||
addSource(
|
||||
'/myLib.dart',
|
||||
'''
|
||||
library myLib;
|
||||
part '$testFile'
|
||||
part '/otherPart.dart'
|
||||
class A {
|
||||
A suggested1(int x) => null;
|
||||
B suggested2(String y) => null;
|
||||
}
|
||||
''');
|
||||
addSource(
|
||||
'/otherPart.dart',
|
||||
'''
|
||||
part of myLib;
|
||||
class B extends A {
|
||||
B suggested2(String y) => null;
|
||||
C suggested3([String z]) => null;
|
||||
}
|
||||
''');
|
||||
addTestSource(r'''
|
||||
part of myLib;
|
||||
class C extends B {
|
||||
sugg^
|
||||
}
|
||||
''');
|
||||
// assume information for context.getLibrariesContaining has been cached
|
||||
await computeLibrariesContaining();
|
||||
await computeSuggestions();
|
||||
_assertOverride('''@override
|
||||
A suggested1(int x) {
|
||||
// TODO: implement suggested1
|
||||
return null;
|
||||
}''');
|
||||
_assertOverride(
|
||||
'''@override\n A suggested1(int x) {\n // TODO: implement suggested1\n return null;\n }''');
|
||||
_assertOverride(
|
||||
'''@override\n B suggested2(String y) {\n // TODO: implement suggested2\n return null;\n }''');
|
||||
_assertOverride(
|
||||
'''@override\n C suggested3([String z]) {\n // TODO: implement suggested3\n return null;\n }''');
|
||||
}
|
||||
|
||||
CompletionSuggestion _assertOverride(String completion) {
|
||||
CompletionSuggestion cs = getSuggest(
|
||||
completion: completion,
|
||||
csKind: CompletionSuggestionKind.IDENTIFIER,
|
||||
elemKind: null);
|
||||
if (cs == null) {
|
||||
failedCompletion('expected $completion', suggestions);
|
||||
}
|
||||
expect(cs.kind, equals(CompletionSuggestionKind.IDENTIFIER));
|
||||
expect(cs.relevance, equals(DART_RELEVANCE_HIGH));
|
||||
expect(cs.importUri, null);
|
||||
// expect(cs.selectionOffset, equals(completion.length));
|
||||
// expect(cs.selectionLength, equals(0));
|
||||
expect(cs.isDeprecated, isFalse);
|
||||
expect(cs.isPotential, isFalse);
|
||||
expect(cs.element, isNotNull);
|
||||
return cs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2014, 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.
|
||||
|
||||
library test.services.completion.dart;
|
||||
|
||||
import 'package:unittest/unittest.dart';
|
||||
import 'inherited_contributor_test.dart' as inherited_contributor_test;
|
||||
|
||||
import '../../../utils.dart';
|
||||
|
||||
/// Utility for manually running all tests.
|
||||
main() {
|
||||
initializeTestEnvironment();
|
||||
group('dart/completion', () {
|
||||
inherited_contributor_test.main();
|
||||
});
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
library test.services.completion.inherited_computer_test;
|
||||
|
||||
import 'package:analysis_server/plugin/protocol/protocol.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart';
|
||||
import 'package:analysis_server/src/services/completion/inherited_contributor.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
import 'package:unittest/src/matcher/core_matchers.dart';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
import '../../utils.dart';
|
||||
import 'completion_test_util.dart';
|
||||
|
||||
main() {
|
||||
initializeTestEnvironment();
|
||||
// defineReflectiveTests(InheritedContributorTest);
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class InheritedContributorTest extends AbstractCompletionTest {
|
||||
@override
|
||||
void setUpContributor() {
|
||||
contributor = new NewCompletionWrapper(new InheritedContributor());
|
||||
}
|
||||
|
||||
test_fromMultipleSuperclasses() {
|
||||
addTestSource(r'''
|
||||
class A {
|
||||
notSuggested() => null;
|
||||
A suggested1(int x) => null;
|
||||
B suggested2(String y) => null;
|
||||
}
|
||||
class B extends A {
|
||||
B suggested2(String y) => null;
|
||||
C suggested3([String z]) => null;
|
||||
}
|
||||
class C extends B {
|
||||
sugg^
|
||||
}
|
||||
''');
|
||||
computeFast();
|
||||
return computeFull((bool result) {
|
||||
_assertOverride(
|
||||
'@override\n A suggested1(int x) {\n // TODO: implement suggested1\n return null;\n }');
|
||||
_assertOverride(
|
||||
'''@override\n B suggested2(String y) {\n // TODO: implement suggested2\n return null;\n }''');
|
||||
_assertOverride(
|
||||
'''@override\n C suggested3([String z]) {\n // TODO: implement suggested3\n return null;\n }''');
|
||||
assertNotSuggested(
|
||||
'''@override\n notSuggested() {\n // TODO: implement notSuggested\n return null;\n }''');
|
||||
});
|
||||
}
|
||||
|
||||
CompletionSuggestion _assertOverride(String completion) {
|
||||
CompletionSuggestion cs = getSuggest(
|
||||
completion: completion,
|
||||
csKind: CompletionSuggestionKind.IDENTIFIER,
|
||||
elemKind: null);
|
||||
if (cs == null) {
|
||||
failedCompletion('expected $completion', request.suggestions);
|
||||
}
|
||||
expect(cs.kind, equals(CompletionSuggestionKind.IDENTIFIER));
|
||||
expect(cs.relevance, equals(DART_RELEVANCE_HIGH));
|
||||
expect(cs.importUri, null);
|
||||
// expect(cs.selectionOffset, equals(completion.length));
|
||||
expect(cs.selectionLength, equals(0));
|
||||
expect(cs.isDeprecated, isFalse);
|
||||
expect(cs.isPotential, isFalse);
|
||||
expect(cs.element, isNotNull);
|
||||
return cs;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import 'common_usage_computer_test.dart' as common_usage_computer_test;
|
||||
import 'completion_computer_test.dart' as completion_computer_test;
|
||||
import 'completion_manager_test.dart' as completion_manager_test;
|
||||
import 'completion_target_test.dart' as completion_target_test;
|
||||
import 'dart/test_all.dart' as dart_contributor_tests;
|
||||
import 'imported_reference_contributor_test.dart' as imported_test;
|
||||
import 'inherited_computer_test.dart' as inherited_computer_test;
|
||||
import 'keyword_contributor_test.dart' as keyword_test;
|
||||
import 'local_declaration_visitor_test.dart' as local_declaration_visitor_test;
|
||||
import 'local_reference_contributor_test.dart'
|
||||
@@ -33,8 +33,8 @@ main() {
|
||||
completion_computer_test.main();
|
||||
completion_manager_test.main();
|
||||
completion_target_test.main();
|
||||
dart_contributor_tests.main();
|
||||
imported_test.main();
|
||||
inherited_computer_test.main();
|
||||
invocation_test.main();
|
||||
keyword_test.main();
|
||||
local_declaration_visitor_test.main();
|
||||
|
||||
Reference in New Issue
Block a user