Reorganize InheritedRefenenceContributor to serve angular completion

Take the parts that I would have simply copied and pasted them into my
own code, and create new public & private methods to serve the new
organization.

Add an option to get the current class's accessors/methods along with
the inherited ones, because that's what I want.

Make opType a public member of DartCompletionRequest

Its used so heavily by so many of the autocomplete targets, it really
doesn't need to be hidden on the Impl.

Besides, I want to use InheritedReferenceContributor, which upcasts the
DartCompletionRequest into the Impl in order to grab this field -- but
for a large number of reasons I can't really use
DartCompletionRequestImpl. Therefore I need my own class to implement
DartCompletionRequest and therefore my code will only work with
InheritedReferenceContributor if it doesn't have to look outside the
interface for this information.

Change to enable the angular analysis project to do autocomplete

We don't have CompilationUnits, but we do have dart ASTs, and we want to
find the completion target with the same algorithm. The CompletionTarget
class's functionality doesn't depend on its member compilationUnit not
being null, so that isn't a problem for us. However, in `forOffset()`,
we cannot pass in a null unit, even though its only used to set the
`compilationUnit` member and otherwise only used as an `AstNode` which
we have.

So rather than using `compilationUnit` as the entry point of the
algorithm, add a new `AstNode` parameter to represent the entry point,
and when its null simply use the value of `compilationUnit`.

BUG=
R=brianwilkerson@google.com

Review-Url: https://codereview.chromium.org/2565633002 .
This commit is contained in:
Mike Fairhurst 2016-12-12 14:29:53 -08:00
parent 53e93e8b05
commit d8c9645c26
5 changed files with 82 additions and 36 deletions

View file

@ -99,6 +99,12 @@ abstract class DartCompletionRequest extends CompletionRequest {
*/
DartType get objectType;
/**
* The [OpType] which describes which types of suggestions would fit the
* request.
*/
OpType get opType;
/**
* Return the [SourceFactory] of the request.
*/

View file

@ -137,16 +137,20 @@ class CompletionTarget {
/**
* Compute the appropriate [CompletionTarget] for the given [offset] within
* the [compilationUnit].
*
* Optionally, start the search from within [entryPoint] instead of using
* the [compilationUnit], which is useful for analyzing ASTs that have no
* [compilationUnit] such as dart expressions within angular templates.
*/
factory CompletionTarget.forOffset(
CompilationUnit compilationUnit, int offset) {
CompilationUnit compilationUnit, int offset, {AstNode entryPoint}) {
// The precise algorithm is as follows. We perform a depth-first search of
// all edges in the parse tree (both those that point to AST nodes and
// those that point to tokens), visiting parents before children. The
// first edge which points to an entity satisfying either _isCandidateToken
// or _isCandidateNode is the completion target. If no edge is found that
// satisfies these two predicates, then we set the completion target entity
// to null and the containingNode to the compilationUnit.
// to null and the containingNode to the entryPoint.
//
// Note that if a token is not a candidate target, then none of the tokens
// that precede it are candidate targets either. Therefore any entity
@ -154,7 +158,8 @@ class CompletionTarget {
// prune the search to the point where no recursion is necessary; at each
// step in the process we know exactly which child node we need to proceed
// to.
AstNode containingNode = compilationUnit;
entryPoint ??= compilationUnit;
AstNode containingNode = entryPoint;
outerLoop: while (true) {
if (containingNode is Comment) {
// Comments are handled specially: we descend into any CommentReference
@ -232,13 +237,13 @@ class CompletionTarget {
// the first time through the outer loop (since we only jump to the start
// of the outer loop after determining that the completion target is
// inside an entity). We can check that assumption by verifying that
// containingNode is still the compilationUnit.
assert(identical(containingNode, compilationUnit));
// containingNode is still the entryPoint.
assert(identical(containingNode, entryPoint));
// Since no completion target was found, we set the completion target
// entity to null and use the compilationUnit as the parent.
// entity to null and use the entryPoint as the parent.
return new CompletionTarget._(
compilationUnit, offset, compilationUnit, null, false);
compilationUnit, offset, entryPoint, null, false);
}
}

View file

@ -192,12 +192,7 @@ class DartCompletionRequestImpl implements DartCompletionRequest {
@override
bool get includeIdentifiers {
opType; // <<< ensure _opType is initialized
return !_opType.isPrefixed &&
(_opType.includeReturnValueSuggestions ||
_opType.includeTypeNameSuggestions ||
_opType.includeVoidReturnSuggestions ||
_opType.includeConstructorSuggestions);
return opType.includeIdentifiers;
}
@override

View file

@ -62,37 +62,54 @@ class InheritedReferenceContributor extends DartCompletionContributor
if (!request.includeIdentifiers) {
return EMPTY_LIST;
}
ClassDeclaration classDecl = _enclosingClass(request.target);
if (classDecl == null || classDecl.element == null) {
return EMPTY_LIST;
}
containingLibrary = request.libraryElement;
return _computeSuggestionsForClass2(resolutionMap
.elementDeclaredByClassDeclaration(classDecl), request);
}
List<CompletionSuggestion> _computeSuggestionsForClass2(
ClassElement classElement, DartCompletionRequest request,
{bool skipChildClass: true}) {
bool isFunctionalArgument = request.target.isFunctionalArgument();
kind = isFunctionalArgument
? CompletionSuggestionKind.IDENTIFIER
: CompletionSuggestionKind.INVOCATION;
OpType optype = (request as DartCompletionRequestImpl).opType;
for (InterfaceType type in resolutionMap
.elementDeclaredByClassDeclaration(classDecl)
.allSupertypes) {
if (!isFunctionalArgument) {
for (PropertyAccessorElement elem in type.accessors) {
if (elem.isGetter) {
if (optype.includeReturnValueSuggestions) {
addSuggestion(elem);
}
} else {
if (optype.includeVoidReturnSuggestions) {
addSuggestion(elem);
}
}
}
}
for (MethodElement elem in type.methods) {
if (elem.returnType == null) {
addSuggestion(elem);
} else if (!elem.returnType.isVoid) {
OpType optype = request.opType;
if (!skipChildClass) {
_addSuggestionsForType(classElement.type, optype,
isFunctionalArgument: isFunctionalArgument);
}
for (InterfaceType type in classElement.allSupertypes) {
_addSuggestionsForType(type, optype,
isFunctionalArgument: isFunctionalArgument);
}
return suggestions;
}
List<CompletionSuggestion> computeSuggestionsForClass(
ClassElement classElement, DartCompletionRequest request,
{bool skipChildClass: true}) {
if (!request.includeIdentifiers) {
return EMPTY_LIST;
}
containingLibrary = request.libraryElement;
return _computeSuggestionsForClass2(classElement, request,
skipChildClass: skipChildClass);
}
_addSuggestionsForType(InterfaceType type, OpType optype,
{bool isFunctionalArgument: false}) {
if (!isFunctionalArgument) {
for (PropertyAccessorElement elem in type.accessors) {
if (elem.isGetter) {
if (optype.includeReturnValueSuggestions) {
addSuggestion(elem);
}
@ -103,6 +120,18 @@ class InheritedReferenceContributor extends DartCompletionContributor
}
}
}
return suggestions;
for (MethodElement elem in type.methods) {
if (elem.returnType == null) {
addSuggestion(elem);
} else if (!elem.returnType.isVoid) {
if (optype.includeReturnValueSuggestions) {
addSuggestion(elem);
}
} else {
if (optype.includeVoidReturnSuggestions) {
addSuggestion(elem);
}
}
}
}
}

View file

@ -134,6 +134,17 @@ class OpType {
!includeReturnValueSuggestions &&
!includeVoidReturnSuggestions;
/**
* Return `true` if free standing identifiers should be suggested
*/
bool get includeIdentifiers {
return !isPrefixed &&
(includeReturnValueSuggestions ||
includeTypeNameSuggestions ||
includeVoidReturnSuggestions ||
includeConstructorSuggestions);
}
/**
* Indicate whether only type names should be suggested
*/