IDEA-62053 Smart completion should suggest "public static final members" of any class

This commit is contained in:
peter
2012-08-06 23:50:30 +02:00
parent fea09a458f
commit 8d74a7248c
15 changed files with 181 additions and 139 deletions
@@ -68,10 +68,12 @@ public class BasicExpressionCompletionContributor {
}
if (!AFTER_DOT.accepts(element)) {
new CollectionsUtilityMethodsProvider(parameters.getPosition(),
parameters.getExpectedType(),
parameters.getDefaultType(), result)
.addCompletions(parameters.getParameters().getInvocationCount() > 1 || StringUtil.isNotEmpty(matcher.getPrefix()));
if (parameters.getParameters().getInvocationCount() <= 1) {
new CollectionsUtilityMethodsProvider(parameters.getPosition(),
parameters.getExpectedType(),
parameters.getDefaultType(), result)
.addCompletions(StringUtil.isNotEmpty(matcher.getPrefix()));
}
ClassLiteralGetter.addCompletions(parameters, result, matcher);
final PsiElement position = parameters.getPosition();
@@ -540,7 +540,7 @@ public class JavaCompletionData extends JavaAwareCompletionData {
static void addExpectedTypeMembers(CompletionParameters parameters, final CompletionResultSet result, PsiElement position) {
for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
new JavaMembersGetter(info.getDefaultType(), position).addMembers(parameters, parameters.getInvocationCount() > 1, new Consumer<LookupElement>() {
new JavaMembersGetter(info.getDefaultType(), parameters).addMembers(parameters.getInvocationCount() > 1, new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
result.addElement(element);
@@ -311,9 +311,9 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
PsiElement position = params.getPosition();
if (!BasicExpressionCompletionContributor.AFTER_DOT.accepts(position)) {
for (ExpectedTypeInfo info : mergedInfos) {
new JavaMembersGetter(info.getType(), position).addMembers(params, !quick, consumer);
new JavaMembersGetter(info.getType(), params).addMembers(!quick, consumer);
if (!info.getDefaultType().equals(info.getType())) {
new JavaMembersGetter(info.getDefaultType(), position).addMembers(params, !quick, consumer);
new JavaMembersGetter(info.getDefaultType(), params).addMembers(!quick, consumer);
}
}
}
@@ -37,37 +37,34 @@ import java.util.Set;
*/
public class JavaMembersGetter extends MembersGetter {
private final PsiType myExpectedType;
private final PsiElement myPlace;
public JavaMembersGetter(@NotNull PsiType expectedType, PsiElement place) {
myPlace = place;
public JavaMembersGetter(@NotNull PsiType expectedType, CompletionParameters parameters) {
super(new JavaStaticMemberProcessor(parameters), parameters.getPosition());
myExpectedType = JavaCompletionUtil.originalize(expectedType);
}
public void addMembers(CompletionParameters parameters, boolean searchInheritors, final Consumer<LookupElement> results) {
final StaticMemberProcessor processor = new JavaStaticMemberProcessor(parameters);
final PsiElement position = parameters.getPosition();
public void addMembers(boolean searchInheritors, final Consumer<LookupElement> results) {
if (myExpectedType instanceof PsiPrimitiveType && PsiType.DOUBLE.isAssignableFrom(myExpectedType)) {
addConstantsFromTargetClass(position, results, searchInheritors, processor);
addConstantsFromReferencedClassesInSwitch(position, results, processor);
addConstantsFromTargetClass(results, searchInheritors);
addConstantsFromReferencedClassesInSwitch(results);
}
if (position.getParent().getParent() instanceof PsiSwitchLabelStatement) {
if (myPlace.getParent().getParent() instanceof PsiSwitchLabelStatement) {
return; //non-enum values are processed above, enum values will be suggested by reference completion
}
final PsiClass psiClass = PsiUtil.resolveClassInType(myExpectedType);
processMembers(position, results, psiClass, PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) != null, searchInheritors, processor);
processMembers(results, psiClass, PsiTreeUtil.getParentOfType(myPlace, PsiAnnotation.class) == null, searchInheritors);
}
private void addConstantsFromReferencedClassesInSwitch(PsiElement position, final Consumer<LookupElement> results, final StaticMemberProcessor processor) {
final Set<PsiField> fields = ReferenceExpressionCompletionContributor.findConstantsUsedInSwitch(position);
private void addConstantsFromReferencedClassesInSwitch(final Consumer<LookupElement> results) {
final Set<PsiField> fields = ReferenceExpressionCompletionContributor.findConstantsUsedInSwitch(myPlace);
final Set<PsiClass> classes = new HashSet<PsiClass>();
for (PsiField field : fields) {
ContainerUtil.addIfNotNull(classes, field.getContainingClass());
}
for (PsiClass aClass : classes) {
processMembers(position, new Consumer<LookupElement>() {
processMembers(new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
//noinspection SuspiciousMethodCalls
@@ -75,14 +72,12 @@ public class JavaMembersGetter extends MembersGetter {
results.consume(TailTypeDecorator.withTail(element, TailType.CASE_COLON));
}
}
}, aClass, false, false, processor);
}, aClass, true, false);
}
}
private void addConstantsFromTargetClass(PsiElement position,
Consumer<LookupElement> results,
boolean searchInheritors, final StaticMemberProcessor processor) {
PsiElement parent = position.getParent();
private void addConstantsFromTargetClass(Consumer<LookupElement> results, boolean searchInheritors) {
PsiElement parent = myPlace.getParent();
if (!(parent instanceof PsiReferenceExpression)) {
return;
}
@@ -94,8 +89,8 @@ public class JavaMembersGetter extends MembersGetter {
final IElementType op = binaryExpression.getOperationTokenType();
if (JavaTokenType.EQEQ == op || JavaTokenType.NE == op) {
if (prev == binaryExpression.getROperand()) {
processMembers(position, results, getCalledClass(binaryExpression.getLOperand()), false, searchInheritors,
processor);
processMembers(results, getCalledClass(binaryExpression.getLOperand()), true, searchInheritors
);
}
return;
}
@@ -103,7 +98,7 @@ public class JavaMembersGetter extends MembersGetter {
parent = parent.getParent();
}
if (parent instanceof PsiExpressionList) {
processMembers(position, results, getCalledClass(parent.getParent()), false, searchInheritors, processor);
processMembers(results, getCalledClass(parent.getParent()), true, searchInheritors);
}
}
@@ -22,11 +22,13 @@ import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.codeInsight.completion.StaticMemberProcessor;
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.filters.TrueFilter;
import com.intellij.psi.impl.java.stubs.index.JavaStaticMemberTypeIndex;
import com.intellij.psi.scope.processor.FilterScopeProcessor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -35,10 +37,7 @@ import com.intellij.util.PairConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* @author ik
@@ -46,89 +45,107 @@ import java.util.Set;
*/
public abstract class MembersGetter {
public static final Key<Boolean> EXPECTED_TYPE_INHERITOR_MEMBER = Key.create("EXPECTED_TYPE_INHERITOR_MEMBER");
private final Set<PsiMember> myImportedStatically = new HashSet<PsiMember>();
private final List<PsiClass> myPlaceClasses = new ArrayList<PsiClass>();
private final List<PsiMethod> myPlaceMethods = new ArrayList<PsiMethod>();
protected final PsiElement myPlace;
public void processMembers(@NotNull final PsiElement context, final Consumer<LookupElement> results, @Nullable final PsiClass where,
final boolean acceptMethods, boolean searchInheritors,
StaticMemberProcessor processor) {
if (where == null) return;
final List<PsiClass> placeClasses = new ArrayList<PsiClass>();
PsiClass current = PsiTreeUtil.getContextOfType(context, PsiClass.class);
while (current != null) {
current = CompletionUtil.getOriginalOrSelf(current);
placeClasses.add(current);
current = PsiTreeUtil.getContextOfType(current, PsiClass.class);
}
final Set<PsiMember> importedStatically = new HashSet<PsiMember>();
protected MembersGetter(StaticMemberProcessor processor, @NotNull final PsiElement place) {
myPlace = place;
processor.processMembersOfRegisteredClasses(PrefixMatcher.ALWAYS_TRUE, new PairConsumer<PsiMember, PsiClass>() {
@Override
public void consume(PsiMember member, PsiClass psiClass) {
importedStatically.add(member);
myImportedStatically.add(member);
}
});
final Condition<PsiClass> mayProcessMembers = new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
if (psiClass == null) {
return false;
}
psiClass = CompletionUtil.getOriginalOrSelf(psiClass);
for (PsiClass placeClass : placeClasses) {
if (InheritanceUtil.isInheritorOrSelf(placeClass, psiClass, true)) {
return false;
}
}
return true;
PsiClass current = PsiTreeUtil.getContextOfType(place, PsiClass.class);
while (current != null) {
current = CompletionUtil.getOriginalOrSelf(current);
myPlaceClasses.add(current);
current = PsiTreeUtil.getContextOfType(current, PsiClass.class);
}
PsiMethod eachMethod = PsiTreeUtil.getContextOfType(place, PsiMethod.class);
while (eachMethod != null) {
eachMethod = CompletionUtil.getOriginalOrSelf(eachMethod);
myPlaceMethods.add(eachMethod);
eachMethod = PsiTreeUtil.getContextOfType(eachMethod, PsiMethod.class);
}
}
private boolean mayProcessMembers(@Nullable PsiClass psiClass) {
if (psiClass == null) {
return false;
}
for (PsiClass placeClass : myPlaceClasses) {
if (InheritanceUtil.isInheritorOrSelf(placeClass, psiClass, true)) {
return false;
}
};
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper();
}
return true;
}
final PsiClassType baseType = JavaPsiFacade.getElementFactory(where.getProject()).createType(where);
public void processMembers(final Consumer<LookupElement> results, @Nullable final PsiClass where,
final boolean acceptMethods, final boolean searchInheritors) {
if (where == null) return;
final boolean searchFactoryMethods = searchInheritors &&
!CommonClassNames.JAVA_LANG_OBJECT.equals(where.getQualifiedName()) &&
!CommonClassNames.JAVA_LANG_STRING.equals(where.getQualifiedName());
final Project project = myPlace.getProject();
final GlobalSearchScope scope = myPlace.getResolveScope();
final PsiClassType baseType = JavaPsiFacade.getElementFactory(project).createType(where);
Consumer<PsiType> consumer = new Consumer<PsiType>() {
@Override
public void consume(PsiType psiType) {
PsiClass psiClass = PsiUtil.resolveClassInType(psiType);
if (mayProcessMembers.value(psiClass)) {
psiClass = CompletionUtil.getOriginalOrSelf(psiClass);
for (PsiClass placeClass : placeClasses) {
if (InheritanceUtil.isInheritorOrSelf(placeClass, psiClass, true)) {
return;
}
if (psiClass == null) {
return;
}
psiClass = CompletionUtil.getOriginalOrSelf(psiClass);
if (mayProcessMembers(psiClass)) {
final FilterScopeProcessor<PsiElement> declProcessor = new FilterScopeProcessor<PsiElement>(TrueFilter.INSTANCE);
psiClass.processDeclarations(declProcessor, ResolveState.initial(), null, myPlace);
doProcessMembers(acceptMethods, results, psiType != baseType, declProcessor.getResults());
String name = psiClass.getName();
if (name != null && searchFactoryMethods) {
Collection<PsiMember> factoryMethods = JavaStaticMemberTypeIndex.getInstance().getStaticMembers(name, project, scope);
doProcessMembers(acceptMethods, results, psiType != baseType, factoryMethods);
}
processClassDeclaredMembers(psiClass, context, acceptMethods, results, resolveHelper, importedStatically, psiType != baseType);
}
}
};
consumer.consume(baseType);
if (searchInheritors && !CommonClassNames.JAVA_LANG_OBJECT.equals(where.getQualifiedName())) {
CodeInsightUtil.processSubTypes(baseType, context, true, PrefixMatcher.ALWAYS_TRUE, consumer);
CodeInsightUtil.processSubTypes(baseType, myPlace, true, PrefixMatcher.ALWAYS_TRUE, consumer);
}
}
private void processClassDeclaredMembers(PsiClass where,
PsiElement context,
boolean acceptMethods,
Consumer<LookupElement> results, final PsiResolveHelper resolveHelper, final Set<PsiMember> importedStatically, boolean isInheritor) {
final FilterScopeProcessor<PsiElement> processor = new FilterScopeProcessor<PsiElement>(TrueFilter.INSTANCE);
where.processDeclarations(processor, ResolveState.initial(), null, context);
for (final PsiElement result : processor.getResults()) {
private void doProcessMembers(boolean acceptMethods,
Consumer<LookupElement> results,
boolean isInheritor, Collection<? extends PsiElement> declarations) {
for (final PsiElement result : declarations) {
if (result instanceof PsiMember && !(result instanceof PsiClass)) {
final PsiMember member = (PsiMember)result;
if (JavaCompletionUtil.isInExcludedPackage(member, false) || importedStatically.contains(member)) continue;
if (member.hasModifierProperty(PsiModifier.STATIC) && resolveHelper.isAccessible(member, context, null)) {
if (result instanceof PsiField && !member.hasModifierProperty(PsiModifier.FINAL)) continue;
if (result instanceof PsiMethod && acceptMethods) continue;
final LookupElement item = result instanceof PsiMethod ? createMethodElement((PsiMethod)result) : createFieldElement((PsiField)result);
if (item != null) {
item.putUserData(EXPECTED_TYPE_INHERITOR_MEMBER, isInheritor);
results.consume(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(item));
}
if (!member.hasModifierProperty(PsiModifier.STATIC)) continue;
if (result instanceof PsiField && !member.hasModifierProperty(PsiModifier.FINAL)) continue;
if (result instanceof PsiMethod && (!acceptMethods || myPlaceMethods.contains(result))) continue;
if (JavaCompletionUtil.isInExcludedPackage(member, false) || myImportedStatically.contains(member)) continue;
if (!JavaPsiFacade.getInstance(myPlace.getProject()).getResolveHelper().isAccessible(member, myPlace, null)) {
continue;
}
final LookupElement item = result instanceof PsiMethod ? createMethodElement((PsiMethod)result) : createFieldElement((PsiField)result);
if (item != null) {
item.putUserData(EXPECTED_TYPE_INHERITOR_MEMBER, isInheritor);
results.consume(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(item));
}
}
}
@@ -42,7 +42,7 @@ public class JavaStaticMemberTypeIndex extends StringStubIndexExtension<PsiMembe
return JavaStubIndexKeys.JVM_STATIC_MEMBERS_TYPES;
}
public Collection<PsiMember> getStaticMembers(final String shortTypeText, final Project project, @NotNull final GlobalSearchScope scope) {
public Collection<PsiMember> getStaticMembers(@NotNull final String shortTypeText, final Project project, @NotNull final GlobalSearchScope scope) {
return super.get(shortTypeText, project, new JavaSourceFilterScope(scope));
}
@@ -0,0 +1,27 @@
import java.lang.Object;
class Super<T> {
}
class SubString extends Super<String> {
public static SubString createSubString() {}
}
class SubInt extends Super<Integer> { }
class SubGeneric<T> extends Super<T> { }
class SubRaw extends Super { }
class Factory {
public static Object createObject() {}
public static <T> Super<T> createExpected() {}
public static Super<Integer> createSuperInt() {}
public static SubInt createSubInt() {}
public static <T> SubGeneric<T> createSubGeneric() {}
public static SubRaw createSubRaw() {}
}
class Intermediate {
Super<String> s = create<caret>x
}
@@ -2,7 +2,7 @@ public class Test {
static MyEnum getAvailabilityStatusCode() {
return get<caret>
return getAS<caret>
}
@@ -2,7 +2,7 @@ class C{
{
Object o;
if (o instanceof Foo) {
new Object().do<caret>
new Object().dofo<caret>
}
}
}
@@ -2,23 +2,25 @@ package com.intellij.codeInsight.completion;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.lookup.*;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.annotations.NonNls;
@SuppressWarnings({"ALL"})
public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
private static final String BASE_PATH = "/codeInsight/completion/smartType/second";
public class SecondSmartTypeCompletionTest extends LightFixtureCompletionTestCase {
public SecondSmartTypeCompletionTest() {
setType(CompletionType.SMART);
@Override
protected void complete() {
myItems = myFixture.complete(CompletionType.SMART, 2);
}
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath();
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/smartType/second";
}
public void testMethodAsQualifier() throws Throwable { doTest(); }
@@ -69,7 +71,7 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
public void testDontIgnoreToStringInsideIt() throws Throwable { doTest(); }
public void testDontIgnoreToStringInStringBuilders() throws Throwable {
configure();
assertStringItems("bar.substring", "bar.substring", "bar.toString");
myFixture.assertPreferredCompletionItems(0, "bar.substring", "bar.substring", "bar.toString");
}
public void testNoObjectMethodsAsFirstPart() throws Throwable { doTest(); }
@@ -81,7 +83,7 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
public void testNewStaticProblem() throws Throwable { doTest(); }
public void testChainingPerformance() throws Throwable {
configureByFileNoComplete(BASE_PATH + "/" + getTestName(false) + ".java");
myFixture.configureByFile(getTestName(false) + ".java");
PlatformTestUtil.startPerformanceTest(getTestName(false), 1000, new ThrowableRunnable() {
@Override
public void run() throws Exception {
@@ -103,12 +105,12 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
}
private void configure() {
configureByFile(BASE_PATH + "/" + getTestName(false) + ".java");
configureByFile(getTestName(false) + ".java");
}
public void testNoArraysAsListCommonPrefix() throws Throwable {
configure();
checkResultByFile(BASE_PATH + "/" + getTestName(false) + ".java");
checkResultByFile(getTestName(false) + ".java");
assertStringItems("bar()", "foo()");
assertEquals("Arrays.asList(f.bar())", ((LookupItem)((LookupElementDecorator)myItems[0]).getDelegate()).getPresentableText());
assertEquals("Arrays.asList(f.foo())", ((LookupItem)((LookupElementDecorator)myItems[1]).getDelegate()).getPresentableText());
@@ -117,11 +119,11 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
}
public void testRestoreInitialPrefix() throws Throwable {
configureByFileNoComplete(BASE_PATH + "/" + getTestName(false) + ".java");
complete(1);
myFixture.configureByFile(getTestName(false) + ".java");
myFixture.complete(CompletionType.SMART);
assertStringItems("MyEnum.Bar", "MyEnum.Foo");
checkResult();
complete(1);
myFixture.complete(CompletionType.SMART);
assertStringItems("my.getEnum", "MyEnum.Bar", "MyEnum.Foo");
}
@@ -138,8 +140,8 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
private void doAntiTest() throws Exception {
configure();
assertNull(myItems);
checkResultByFile(BASE_PATH + "/" + getTestName(false) + ".java");
assertEmpty(myItems);
checkResultByFile(getTestName(false) + ".java");
}
public void testCastInstanceofedQualifier() throws Throwable { doTest(); }
@@ -157,7 +159,7 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
public void testNoRedundantCasts() throws Throwable {
configure();
checkResultByFile(BASE_PATH + "/" + getTestName(false) + ".java");
checkResultByFile(getTestName(false) + ".java");
assertStringItems("o.gggg", "false", "true");
}
@@ -168,7 +170,7 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
}
private void checkResult() {
checkResultByFile(BASE_PATH + "/" + getTestName(false) + "-out.java");
checkResultByFile(getTestName(false) + "-out.java");
}
public void testSingletonMap() throws Throwable {
@@ -202,6 +204,11 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
checkResult();
}
public void testGlobalFactoryMethods() {
configure();
assertStringItems("createExpected", "createSubGeneric", "createSubRaw", "createSubString");
}
public void testEmptyMapPresentation() {
configure();
LookupElementPresentation presentation = new LookupElementPresentation();
@@ -216,13 +223,6 @@ public class SecondSmartTypeCompletionTest extends LightCompletionTestCase {
assertEquals("Collections.emptyMap", presentation.getItemText());
}
@Override
protected void complete() {
complete(2);
LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(myEditor);
myItems = lookup == null ? null : lookup.getItems().toArray(LookupElement.EMPTY_ARRAY);
}
protected void tearDown() throws Exception {
LookupManager.getInstance(getProject()).hideActiveLookup();
super.tearDown();
@@ -301,9 +301,8 @@ public class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase {
myFixture.addClass("package foo; public class FList<T> implements java.util.List<T> { public static <T> FList<T> emptyList() {} }");
configureNoCompletion(getTestName(false) + ".java");
myFixture.complete(CompletionType.SMART, 2);
assertPreferredItems(0, "local", "local.subList", "locMethod", "locMethod().subList", "emptyList", "singletonList", "unmodifiableList", "emptyList");
assert 'Collections.emptyList' in lookup.items[4].allLookupStrings
assert 'FList.emptyList' in lookup.items[7].allLookupStrings
assert lookup.items.findIndexOf { 'Collections.emptyList' in it.allLookupStrings } < lookup.items.findIndexOf { 'FList.emptyList' in it.allLookupStrings }
assertPreferredItems(0, "local", "local.subList", "locMethod")
}
public void testDispreferGetterInSetterCall() {
@@ -23,7 +23,6 @@ import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NonNls;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
@@ -103,7 +102,7 @@ public abstract class LightCompletionTestCase extends LightCodeInsightTestCase {
}
protected void assertStringItems(@NonNls String... items) {
assertEquals(getLookupStrings(new ArrayList<String>()), Arrays.asList(items));
assertOrderedEquals(getLookupStrings(new ArrayList<String>()), items);
}
protected void assertContainsItems(final String... expected) {
@@ -363,7 +363,7 @@ public abstract class CompletionPhase implements Disposable {
@Override
public int newCompletionStarted(int time, boolean repeated) {
CompletionServiceImpl.setCompletionPhase(NoCompletion);
return time + 1;
return repeated ? time + 1 : time;
}
}
@@ -18,7 +18,10 @@ package org.jetbrains.plugins.groovy.lang.completion;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.*;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.psi.filters.getters.MembersGetter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Consumer;
@@ -31,23 +34,19 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt
*/
class GroovyMembersGetter extends MembersGetter {
private final PsiClassType myExpectedType;
private final GroovyPsiElement myContext;
private final CompletionParameters myParameters;
GroovyMembersGetter(PsiClassType expectedType, PsiElement context, CompletionParameters parameters) {
myParameters = parameters;
GroovyMembersGetter(PsiClassType expectedType, CompletionParameters parameters) {
super(GroovyCompletionContributor.completeStaticMembers(parameters), parameters.getPosition());
myExpectedType = JavaCompletionUtil.originalize(expectedType);
myContext = (GroovyPsiElement)context;
}
public void processMembers(boolean searchInheritors, final Consumer<LookupElement> results) {
processMembers(myContext, results, myExpectedType.resolve(), PsiTreeUtil.getParentOfType(myContext, GrAnnotation.class) != null, searchInheritors,
GroovyCompletionContributor.completeStaticMembers(myParameters));
processMembers(results, myExpectedType.resolve(), PsiTreeUtil.getParentOfType(myPlace, GrAnnotation.class) == null, searchInheritors);
}
@Override
protected LookupElement createFieldElement(PsiField field) {
if (!TypesUtil.isAssignable(myExpectedType, field.getType(), myContext)) {
if (!isSuitableType(field.getType())) {
return null;
}
@@ -57,10 +56,14 @@ class GroovyMembersGetter extends MembersGetter {
@Override
protected LookupElement createMethodElement(PsiMethod method) {
PsiType type = method.getReturnType();
if (type == null || !TypesUtil.isAssignable(myExpectedType, type, myContext)) {
if (!isSuitableType(type)) {
return null;
}
return GroovyCompletionContributor.createGlobalMemberElement(method, method.getContainingClass(), false);
}
private boolean isSuitableType(PsiType type) {
return type != null && TypesUtil.isAssignable(myExpectedType, type, (GroovyPsiElement)myPlace.getParent());
}
}
@@ -208,10 +208,10 @@ public class GroovySmartCompletionContributor extends CompletionContributor {
PsiType defType = info.getDefaultType();
boolean searchInheritors = params.getInvocationCount() > 1;
if (type instanceof PsiClassType) {
new GroovyMembersGetter((PsiClassType)type, reference, params).processMembers(searchInheritors, consumer);
new GroovyMembersGetter((PsiClassType)type, params).processMembers(searchInheritors, consumer);
}
if (!defType.equals(type) && defType instanceof PsiClassType) {
new GroovyMembersGetter((PsiClassType)defType, reference, params).processMembers(searchInheritors, consumer);
new GroovyMembersGetter((PsiClassType)defType, params).processMembers(searchInheritors, consumer);
}
}
}