Skip to content

Commit

Permalink
Fix for #803: generate proposals for all layers of enclosing closure
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Feb 4, 2019
1 parent 34412d7 commit 7660da0
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2018 the original author or authors.
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -555,6 +555,47 @@ final class FieldCompletionTests extends CompletionTestSuite {
applyProposalAndCheck(proposals[one].displayString != 'zzz : String - A' ? proposals[one] : proposals[two], contents.replace('zz //', 'delegate.zzz //'))
}

@Test // https://github.com/groovy/groovy-eclipse/issues/803
void testClosure13() {
String contents = '''\
class A {
String zzz
static class B {
String zzz
}
static class C {
String zzz
}
def foo(@DelegatesTo(value=B, strategy=Closure.OWNER_FIRST) Closure c) {}
def bar(@DelegatesTo(value=C, strategy=Closure.OWNER_FIRST) Closure c) {}
void test() {
foo {
bar {
zz // delegate is C, owner.delegate is B, owner.owner is A
}
}
}
}
'''.stripIndent()
ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'zz'))

proposalExists(proposals, 'zzz', 3)

proposals.each { ICompletionProposal proposal ->
switch (proposal.displayString) {
case 'zzz : String - A':
//applyProposalAndCheck(proposal, contents.replace('zz //', 'owner.owner.zzz //'))
break
case 'zzz : String - B':
//applyProposalAndCheck(proposal, contents.replace('zz //', 'owner.delegate.zzz //'))
break
case 'zzz : String - C':
applyProposalAndCheck(proposal, contents.replace('zz //', 'delegate.zzz //'))
break
}
}
}

@Test
void testArrayLength1() {
String contents = 'int[] arr; arr.len'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,37 +137,11 @@ public List<ICompletionProposal> generateProposals(IProgressMonitor monitor) {
context.lhsType = requestor.lhsType;
completionType = getCompletionType(completionNode, context, requestor);

int closureStrategy = -1;
if (isPrimary && requestor.currentScope.getEnclosingClosure() != null) {
closureStrategy = requestor.currentScope.getEnclosingClosureResolveStrategy();
}

VariableScope currentScope = requestor.currentScope;
List<IProposalCreator> creators = chooseProposalCreators(context);
// if completionType is delegate, use instance (non-static) semantics
boolean isStatic1 = (isStatic && closureStrategy < Closure.OWNER_FIRST);
proposalCreatorLoop(groovyProposals, creators, requestor, context, options, completionType, isStatic1, isPrimary);

if (completionType.equals(VariableScope.CLASS_CLASS_NODE) && completionType.isUsingGenerics() &&
!completionType.getGenericsTypes()[0].getType().equals(VariableScope.CLASS_CLASS_NODE) &&
!completionType.getGenericsTypes()[0].getType().equals(VariableScope.OBJECT_CLASS_NODE)) {
// "Foo.bar" and "Foo.@bar" are static; "Foo.&bar" and "Foo::bar" are not static
boolean isStatic2 = !METHOD_POINTER_COMPLETION.matcher(context.fullCompletionExpression).matches();
proposalCreatorLoop(groovyProposals, creators, requestor, context, options, completionType.getGenericsTypes()[0].getType(), isStatic2, isPrimary);
}

// within a closure, include content assist for the enclosing type (aka "owner")
if (closureStrategy >= Closure.OWNER_FIRST) {
ClassNode enclosingType = requestor.currentScope.getOwner();
if (enclosingType != null && !enclosingType.equals(completionType)) {
List<IGroovyProposal> ownerProposals = new ArrayList<>(); // keep proposals separate
proposalCreatorLoop(ownerProposals, creators, requestor, context, options, enclosingType, isStatic, isPrimary);

// if "delegate" and/or "owner" qualifiers are required, add them now
setClosureQualifiers(groovyProposals, ownerProposals, closureStrategy);
proposalCreatorOuterLoop(groovyProposals, creators, requestor, context, options, completionType, isStatic, isPrimary);

groovyProposals.addAll(ownerProposals);
}
}
requestor.currentScope = currentScope;

if (isPrimary) {
// if receiver type is an enum, propose its constants directly
Expand Down Expand Up @@ -282,7 +256,48 @@ private List<IProposalCreator> chooseProposalCreators(ContentAssistContext conte
return creators;
}

private void proposalCreatorLoop(Collection<IGroovyProposal> proposals, Collection<IProposalCreator> creators,
private void proposalCreatorOuterLoop(Collection<IGroovyProposal> groovyProposals, Collection<IProposalCreator> creators,
ExpressionCompletionRequestor requestor, ContentAssistContext context, AssistOptions options,
ClassNode completionType, boolean isStatic, boolean isPrimary) {

int closureStrategy = -1;
if (isPrimary && requestor.currentScope.getEnclosingClosure() != null) {
closureStrategy = requestor.currentScope.getEnclosingClosureResolveStrategy();
}

// if completionType is delegate, use instance (non-static) semantics
boolean isStatic1 = (isStatic && closureStrategy < Closure.OWNER_FIRST);
proposalCreatorInnerLoop(groovyProposals, creators, requestor, context, options, completionType, isStatic1, isPrimary);

if (completionType.equals(VariableScope.CLASS_CLASS_NODE) && completionType.isUsingGenerics() &&
!completionType.getGenericsTypes()[0].getType().equals(VariableScope.CLASS_CLASS_NODE) &&
!completionType.getGenericsTypes()[0].getType().equals(VariableScope.OBJECT_CLASS_NODE)) {
// "Foo.bar" and "Foo.@bar" are static; "Foo.&bar" and "Foo::bar" are not static
boolean isStatic2 = !METHOD_POINTER_COMPLETION.matcher(context.fullCompletionExpression).matches();
proposalCreatorInnerLoop(groovyProposals, creators, requestor, context, options, completionType.getGenericsTypes()[0].getType(), isStatic2, isPrimary);
}

// within a closure, include content assist for the enclosing type (aka "owner")
if (closureStrategy >= Closure.OWNER_FIRST) {
ClassNode enclosingType = requestor.currentScope.getOwner();
if (enclosingType != null && !enclosingType.equals(completionType)) {
List<IGroovyProposal> ownerProposals = new ArrayList<>(); // keep proposals separate
proposalCreatorInnerLoop(ownerProposals, creators, requestor, context, options, enclosingType, isStatic, isPrimary);

// if "delegate" and/or "owner" qualifiers are required, add them now
setClosureQualifiers(groovyProposals, ownerProposals, closureStrategy);

groovyProposals.addAll(ownerProposals);

requestor.currentScope = enclosingType.getNodeMetaData("outer.scope");
if (requestor.currentScope != null) { // TODO: add another "owner." qualifier
proposalCreatorOuterLoop(groovyProposals, creators, requestor, context, options, requestor.currentScope.getDelegate(), isStatic, isPrimary);
}
}
}
}

private void proposalCreatorInnerLoop(Collection<IGroovyProposal> groovyProposals, Collection<IProposalCreator> creators,
ExpressionCompletionRequestor requestor, ContentAssistContext context, AssistOptions options,
ClassNode completionType, boolean isStatic, boolean isPrimary) {

Expand All @@ -295,7 +310,7 @@ private void proposalCreatorLoop(Collection<IGroovyProposal> proposals, Collecti
});
}
String completionExpression = context.getPerceivedCompletionExpression();
proposals.addAll(
groovyProposals.addAll(
creator.findAllProposals(completionType, requestor.categories, completionExpression, isStatic, isPrimary));
}
}
Expand Down

0 comments on commit 7660da0

Please sign in to comment.