From 6f5c70f2547b83be277bd519266b2d2736f0cf19 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 8 Sep 2010 19:01:05 +0100 Subject: [PATCH] IDEA-43317 Complete static members from classes that are already mentioned in import block as static import (groovy fields) --- .../completion/JavaCompletionUtil.java | 7 +- .../JavaGlobalMemberLookupElement.java | 29 +++---- .../completion/StaticMemberProcessor.java | 78 +++++++++---------- .../GroovyCompletionContributor.java | 39 ++++++++-- .../groovy/GroovyCompletionTest.groovy | 30 ++++++- 5 files changed, 117 insertions(+), 66 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 4e4bfd7204b3..68a03728e24a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -884,10 +884,11 @@ public class JavaCompletionUtil { } if (javaReference instanceof PsiReferenceExpression && !((PsiReferenceExpression)javaReference).isQualified()) { - JavaGlobalMemberNameCompletionContributor.completeStaticMembers(element).processMembersOfRegisteredClasses(matcher, new Consumer() { + final StaticMemberProcessor memberProcessor = JavaGlobalMemberNameCompletionContributor.completeStaticMembers(element); + memberProcessor.processMembersOfRegisteredClasses(matcher, new PairConsumer() { @Override - public void consume(LookupElement element) { - set.add(element); + public void consume(PsiMember member, PsiClass psiClass) { + set.add(memberProcessor.createLookupElement(member, psiClass, true)); } }); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberLookupElement.java index 57340cae05e4..2cf7d6b61fe1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberLookupElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberLookupElement.java @@ -4,28 +4,27 @@ import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiSubstitutor; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import com.intellij.psi.util.PsiFormatUtil; import org.jetbrains.annotations.NotNull; +import static com.intellij.util.ObjectUtils.assertNotNull; + /** * @author peter */ public class JavaGlobalMemberLookupElement extends LookupElement implements StaticallyImportable { - private final PsiMethod myMethod; + private final PsiMember myMember; private final PsiClass myContainingClass; private final InsertHandler myQualifiedInsertion; private final InsertHandler myImportInsertion; private boolean myShouldImport = false; - public JavaGlobalMemberLookupElement(PsiMethod method, + public JavaGlobalMemberLookupElement(PsiMember member, PsiClass containingClass, InsertHandler qualifiedInsertion, InsertHandler importInsertion, boolean shouldImport) { - myMethod = method; + myMember = member; myContainingClass = containingClass; myQualifiedInsertion = qualifiedInsertion; myImportInsertion = importInsertion; @@ -34,8 +33,8 @@ public class JavaGlobalMemberLookupElement extends LookupElement implements Stat @NotNull @Override - public PsiMethod getObject() { - return myMethod; + public PsiMember getObject() { + return myMember; } @NotNull @@ -46,7 +45,7 @@ public class JavaGlobalMemberLookupElement extends LookupElement implements Stat @NotNull @Override public String getLookupString() { - return myMethod.getName(); + return assertNotNull(myMember.getName()); } @Override @@ -55,23 +54,25 @@ public class JavaGlobalMemberLookupElement extends LookupElement implements Stat presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this, presentation.isReal())); - final String methodName = myMethod.getName(); + final String methodName = myMember.getName(); if (Boolean.FALSE.equals(myShouldImport) && StringUtil.isNotEmpty(className)) { presentation.setItemText(className + "." + methodName); } else { presentation.setItemText(methodName); } - final String params = PsiFormatUtil.formatMethod(myMethod, PsiSubstitutor.EMPTY, + final String params = myMember instanceof PsiMethod + ? PsiFormatUtil.formatMethod((PsiMethod)myMember, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_PARAMETERS, - PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE); + PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE) + : ""; if (Boolean.TRUE.equals(myShouldImport) && StringUtil.isNotEmpty(className)) { presentation.setTailText(params + " in " + className); } else { presentation.setTailText(params); } - final PsiType type = myMethod.getReturnType(); + final PsiType type = myMember instanceof PsiMethod ? ((PsiMethod)myMember).getReturnType() : ((PsiField) myMember).getType(); if (type != null) { presentation.setTypeText(type.getPresentableText()); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java b/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java index baeb6a25d2c9..a864f70aca1c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/StaticMemberProcessor.java @@ -10,9 +10,13 @@ import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.util.Consumer; +import com.intellij.util.PairConsumer; +import com.intellij.util.containers.CollectionFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import static com.intellij.util.containers.CollectionFactory.hashSet; @@ -64,7 +68,24 @@ public abstract class StaticMemberProcessor { for (final PsiMethod method : methods) { ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { - processMember(method, consumer); + if (isStaticallyImportable(method)) { + final PsiClass containingClass = method.getContainingClass(); + assert containingClass != null; + + final boolean shouldImport = myStaticImportedClasses.contains(containingClass); + if (!myHintShown && + !shouldImport && + FeatureUsageTracker.getInstance().isToBeShown(JavaCompletionFeatures.IMPORT_STATIC, myProject) && + CompletionService.getCompletionService().getAdvertisementText() == null) { + final String shortcut = CompletionContributor.getActionShortcut("EditorRight"); + if (shortcut != null) { + CompletionService.getCompletionService().setAdvertisementText("To import a method statically, press " + shortcut); + } + myHintShown = true; + } + + consumer.consume(createLookupElement(method, containingClass, shouldImport)); + } } }); @@ -73,63 +94,40 @@ public abstract class StaticMemberProcessor { } } - public void processMembersOfRegisteredClasses(@Nullable final PrefixMatcher matcher, final Consumer consumer) { + public List processMembersOfRegisteredClasses(@Nullable final PrefixMatcher matcher, PairConsumer consumer) { + final ArrayList result = CollectionFactory.arrayList(); for (final PsiClass psiClass : myStaticImportedClasses) { - final PsiMethod[] classMethods = ApplicationManager.getApplication().runReadAction(new Computable() { - public PsiMethod[] compute() { - return psiClass.getAllMethods(); - } - }); - for (final PsiMethod method : classMethods) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (matcher == null || matcher.prefixMatches(method.getName())) { - processMember(method, consumer); - } + for (final PsiMethod method : psiClass.getAllMethods()) { + if (matcher == null || matcher.prefixMatches(method.getName())) { + if (isStaticallyImportable(method)) { + consumer.consume(method, psiClass); } - }); + } } - final PsiField[] fields = ApplicationManager.getApplication().runReadAction(new Computable() { - public PsiField[] compute() { - return psiClass.getAllFields(); - } - }); - for (final PsiField field : fields) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (matcher == null || matcher.prefixMatches(field.getName())) { - processMember(field, consumer); - } + for (final PsiField field : psiClass.getAllFields()) { + if (matcher == null || matcher.prefixMatches(field.getName())) { + if (isStaticallyImportable(field)) { + consumer.consume(field, psiClass); } - }); + } } } + return result; } - private void processMember(final PsiMember member, final Consumer consumer) { + private boolean isStaticallyImportable(final PsiMember member) { if (member.hasModifierProperty(PsiModifier.STATIC) && myResolveHelper.isAccessible(member, myPosition, null)) { final PsiClass containingClass = member.getContainingClass(); if (containingClass != null) { if (!JavaCompletionUtil.isInExcludedPackage(containingClass) && (!(member instanceof PsiMethod) || !StaticImportMethodFix.isExcluded((PsiMethod)member))) { - final boolean shouldImport = myStaticImportedClasses.contains(containingClass); - if (!myHintShown && - !shouldImport && - FeatureUsageTracker.getInstance().isToBeShown(JavaCompletionFeatures.IMPORT_STATIC, myProject) && - CompletionService.getCompletionService().getAdvertisementText() == null) { - final String shortcut = CompletionContributor.getActionShortcut("EditorRight"); - if (shortcut != null) { - CompletionService.getCompletionService().setAdvertisementText("To import a method statically, press " + shortcut); - } - myHintShown = true; - } - - consumer.consume(createLookupElement(member, containingClass, shouldImport)); + return true; } } } + return false; } @NotNull diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java index 9266bca95c2d..b988dde7cbb4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java @@ -31,6 +31,7 @@ import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.Consumer; +import com.intellij.util.PairConsumer; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; @@ -58,6 +59,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrC import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; @@ -145,13 +147,41 @@ public class GroovyCompletionContributor extends CompletionContributor { final PsiElement position = parameters.getPosition(); final PsiElement reference = position.getParent(); if (reference instanceof GrReferenceElement) { + final Map staticMembers = hashMap(); + ((GrReferenceElement)reference).processVariants(new Consumer() { public void consume(Object element) { - result.addElement(element instanceof PsiClass + final LookupElement lookupElement = element instanceof PsiClass ? AllClassesGetter.createLookupItem((PsiClass)element) - : GroovyCompletionUtil.getLookupElement(element)); + : GroovyCompletionUtil.getLookupElement(element); + final Object object = lookupElement.getObject(); + if ((object instanceof PsiMethod || object instanceof PsiField) && ((PsiModifierListOwner)object).hasModifierProperty(PsiModifier.STATIC)) { + if (lookupElement.getLookupString().equals(((PsiMember)object).getName())) { + staticMembers.put((PsiModifierListOwner)object, lookupElement); + return; + } + } + result.addElement(lookupElement); } }); + + if (((GrReferenceElement)reference).getQualifier() == null) { + completeStaticMembers(position).processMembersOfRegisteredClasses(null, new PairConsumer() { + @Override + public void consume(PsiMember member, PsiClass psiClass) { + if (member instanceof GrAccessorMethod) { + member = ((GrAccessorMethod)member).getProperty(); + } + final String name = member.getName(); + if (name == null || !result.getPrefixMatcher().prefixMatches(name)) { + staticMembers.remove(member); + return; + } + staticMembers.put(member, new JavaGlobalMemberLookupElement(member, psiClass, QUALIFIED_METHOD_INSERT_HANDLER, STATIC_IMPORT_INSERT_HANDLER, true)); + } + }); + } + result.addAllElements(staticMembers.values()); } } }); @@ -374,10 +404,7 @@ public class GroovyCompletionContributor extends CompletionContributor { @NotNull @Override protected LookupElement createLookupElement(@NotNull PsiMember member, @NotNull PsiClass containingClass, boolean shouldImport) { - if (member instanceof PsiMethod) { - return new JavaGlobalMemberLookupElement((PsiMethod)member, containingClass, QUALIFIED_METHOD_INSERT_HANDLER, STATIC_IMPORT_INSERT_HANDLER, shouldImport); - } - return GroovyCompletionUtil.getLookupElement(member); //todo; + return new JavaGlobalMemberLookupElement(member, containingClass, QUALIFIED_METHOD_INSERT_HANDLER, STATIC_IMPORT_INSERT_HANDLER, shouldImport); } }; final PsiFile file = position.getContainingFile(); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.groovy index f5c7f2afedf1..5219af8dafa2 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.groovy @@ -238,7 +238,7 @@ public class GroovyCompletionTest extends GroovyCompletionTestBase { public void testSmartCastCompletionWithoutRParenth() {doSmartTest();} public void testSmartCastCompletionWithRParenth() {doSmartTest();} - public void testWhenSiblingIsStaticallyImported() throws Exception { + public void testWhenSiblingIsStaticallyImported_Method() throws Exception { myFixture.addFileToProject "foo/Foo.groovy", """package foo class Foo { static def abcMethod() {} @@ -249,7 +249,7 @@ public class GroovyCompletionTest extends GroovyCompletionTestBase { myFixture.configureByText("a.groovy", """ import static foo.Foo.abcMethod - abdMethod() + abcMethod() defM """) myFixture.completeBasic() @@ -257,8 +257,32 @@ public class GroovyCompletionTest extends GroovyCompletionTestBase { import static foo.Foo.abcMethod import static foo.Foo.defMethod - abdMethod() + abcMethod() defMethod() """ } + + public void testWhenSiblingIsStaticallyImported_Field() throws Exception { + myFixture.addFileToProject "foo/Foo.groovy", """package foo + class Foo { + static def abcField = 4 + static def defField = 2 + } + """ + + myFixture.configureByText("a.groovy", """ + import static foo.Foo.abcField + + println abcField + defF + """) + myFixture.completeBasic() + myFixture.checkResult """ + import static foo.Foo.abcField + import static foo.Foo.defField + + println abcField + defField + """ + } } \ No newline at end of file