From 52a4227091487c000b2dd077ec2081eda8021e03 Mon Sep 17 00:00:00 2001 From: "peter.gromov" Date: Wed, 17 Nov 2010 16:58:24 +0300 Subject: [PATCH] in basic completion in java, act like smart completion for class names after new --- .../JavaClassNameCompletionContributor.java | 2 +- .../completion/JavaCompletionContributor.java | 56 +++++++++++++++--- .../completion/JavaCompletionUtil.java | 13 +++++ .../completion/JavaInheritorsGetter.java | 57 ++++++++++++------- .../className/importAfterNew/after2.java | 2 +- .../completion/normal/ClassNameAnonymous.java | 9 +++ .../normal/ClassNameAnonymous_after.java | 14 +++++ .../completion/normal/ClassNameGenerics.java | 7 +++ .../normal/ClassNameGenerics_after.java | 8 +++ .../completion/normal/ClassNameWithInner.java | 11 ++++ .../normal/ClassNameWithInner_after.java | 11 ++++ .../completion/NormalCompletionTest.groovy | 14 +++++ 12 files changed, 173 insertions(+), 31 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java index 26b51fa27baa..cbc9f4264c91 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java @@ -66,7 +66,7 @@ public class JavaClassNameCompletionContributor extends CompletionContributor { return; } - if (completingRawConstructor(context, item)) { + if (completingRawConstructor(context, item) && !JavaCompletionUtil.hasAccessibleInnerClass(item.getObject(), file)) { ConstructorInsertHandler.insertParentheses(context, item, item.getObject()); DefaultInsertHandler.addImportForItem(context.getFile(), context.getStartOffset(), item); } else { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index a23b9c7ce0a3..9c70c205ebb5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -18,10 +18,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix; import com.intellij.codeInsight.hint.ShowParameterInfoHandler; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.codeInsight.lookup.LookupItemUtil; -import com.intellij.codeInsight.lookup.TailTypeDecorator; +import com.intellij.codeInsight.lookup.*; import com.intellij.lang.LangBundle; import com.intellij.lang.StdLanguages; import com.intellij.openapi.actionSystem.IdeActions; @@ -51,6 +48,8 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.Consumer; import com.intellij.util.PairConsumer; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -186,7 +185,25 @@ public class JavaCompletionContributor extends CompletionContributor { return; } - addReferenceVariants(parameters, result); + final Set addedClasses = new THashSet(); + if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) { + final JavaInheritorsGetter getter = new JavaInheritorsGetter(ConstructorInsertHandler.BASIC_INSTANCE); + getter.generateVariants(parameters, result.getPrefixMatcher(), new Consumer() { + @Override + public void consume(LookupElement lookupElement) { + final Object object = lookupElement.getObject(); + if (object instanceof PsiClass) { + final PsiClass psiClass = (PsiClass)object; + if (JavaCompletionUtil.hasAccessibleInnerClass(psiClass, position)) return; + + ContainerUtil.addIfNotNull(addedClasses, psiClass.getQualifiedName()); + } + result.addElement(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElement)); + } + }); + } + + addReferenceVariants(parameters, result, addedClasses); addKeywords(parameters, result); @@ -196,14 +213,16 @@ public class JavaCompletionContributor extends CompletionContributor { new Consumer() { @Override public void consume(LookupElement lookupElement) { - result.addElement(lookupElement); + if (!isAlreadyAdded(lookupElement, addedClasses)) { + result.addElement(lookupElement); + } } }); } result.stopHere(); } - private static void addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result) { + private static void addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, final Set addedClasses) { final PsiElement position = parameters.getPosition(); final boolean checkAccess = parameters.getInvocationCount() <= 1; LegacyCompletionContributor.processReferences(parameters, result, new PairConsumer() { @@ -218,6 +237,10 @@ public class JavaCompletionContributor extends CompletionContributor { new ElementExtractorFilter(filter), checkAccess, result.getPrefixMatcher(), parameters)) { + if (isAlreadyAdded(element, addedClasses)) { + continue; + } + if (isSwitchLabel) { result.addElement(TailTypeDecorator.withTail(element, TailType.createSimpleTailType(':'))); } @@ -244,11 +267,15 @@ public class JavaCompletionContributor extends CompletionContributor { if (completion == null) { LOG.error("Position=" + position + "\n;Reference=" + reference + "\n;variants=" + Arrays.toString(variants)); } - if (completion instanceof LookupElement) { + if (completion instanceof LookupElement && !isAlreadyAdded((LookupElement)completion, addedClasses)) { result.addElement((LookupElement)completion); } else if (completion instanceof PsiClass) { - result.addElement(JavaClassNameCompletionContributor.createClassLookupItem((PsiClass)completion, true)); + final PsiClass psiClass = (PsiClass)completion; + final String qname = psiClass.getQualifiedName(); + if (qname == null || !addedClasses.contains(qname)) { + result.addElement(JavaClassNameCompletionContributor.createClassLookupItem(psiClass, true)); + } } else { result.addElement(LookupItemUtil.objectToLookupItem(completion)); @@ -258,6 +285,17 @@ public class JavaCompletionContributor extends CompletionContributor { }); } + private static boolean isAlreadyAdded(LookupElement element, Set addedClasses) { + final Object object = element.getObject(); + if (object instanceof PsiClass) { + final String qualifiedName = ((PsiClass)object).getQualifiedName(); + if (qualifiedName != null && addedClasses.contains(qualifiedName)) { + return true; + } + } + return false; + } + private static void addKeywords(CompletionParameters parameters, CompletionResultSet result) { PsiElement position = parameters.getPosition(); final Set lookupSet = new LinkedHashSet(); 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 5a7f23960f39..8b07edc1ab5b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -992,4 +992,17 @@ public class JavaCompletionUtil { JavaCodeStyleManager.getInstance(file.getProject()).shortenClassReferences((PsiJavaCodeReferenceElement)ref); } } + + public static boolean hasAccessibleInnerClass(@NotNull PsiClass psiClass, @NotNull PsiElement position) { + final PsiClass[] inners = psiClass.getAllInnerClasses(); + if (inners.length > 0) { + PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(position.getProject()).getResolveHelper(); + for (PsiClass inner : inners) { + if (resolveHelper.isAccessible(inner, position, null)) { + return true; + } + } + } + return false; + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java index 7ad7bf1636e5..5c57e2b47252 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java @@ -17,10 +17,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.ExpectedTypeInfo; -import com.intellij.codeInsight.lookup.AutoCompletionPolicy; -import com.intellij.codeInsight.lookup.LookupElementDecorator; -import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.codeInsight.lookup.PsiTypeLookupItem; +import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.filters.getters.ExpectedTypesGetter; @@ -35,6 +32,7 @@ import com.intellij.util.Processor; import com.intellij.util.SmartList; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; @@ -53,28 +51,46 @@ public class JavaInheritorsGetter extends CompletionProvider infoCollection = Arrays.asList(infos); - processInheritors(parameters, extractClassTypes(infos), result.getPrefixMatcher(), new Consumer() { - public void consume(final PsiType type) { - addExpectedType(result, type, parameters, infoCollection); + generateVariants(parameters, result.getPrefixMatcher(), infos, new Consumer() { + @Override + public void consume(LookupElement lookupElement) { + result.addElement(JavaSmartCompletionContributor.decorate(lookupElement, infoCollection)); } }); } - private static void addArrayTypes(CompletionResultSet result, - PsiElement identifierCopy, - ExpectedTypeInfo[] infos) { + public void generateVariants(final CompletionParameters parameters, final PrefixMatcher prefixMatcher, final Consumer consumer) { + generateVariants(parameters, prefixMatcher, JavaSmartCompletionContributor.getExpectedTypes(parameters), consumer); + } + + private void generateVariants(final CompletionParameters parameters, final PrefixMatcher prefixMatcher, + final ExpectedTypeInfo[] infos, final Consumer consumer) { + + addArrayTypes(parameters.getPosition(), infos, prefixMatcher, consumer); + + processInheritors(parameters, extractClassTypes(infos), prefixMatcher, new Consumer() { + public void consume(final PsiType type) { + final LookupElement element = addExpectedType(type, parameters); + if (element != null) { + consumer.consume(element); + } + } + }); + } + + private static void addArrayTypes(PsiElement identifierCopy, + ExpectedTypeInfo[] infos, PrefixMatcher matcher, final Consumer consumer) { for (final PsiType type : ExpectedTypesGetter.extractTypes(infos, true)) { - if (type instanceof PsiArrayType) { + if (type instanceof PsiArrayType && matcher.prefixMatches(type.getCanonicalText())) { + final LookupItem item = PsiTypeLookupItem.createLookupItem(JavaCompletionUtil.eliminateWildcards(type), identifierCopy); if (item.getObject() instanceof PsiClass) { JavaCompletionUtil.setShowFQN(item); } item.setInsertHandler(new DefaultInsertHandler()); //braces & shortening - result.addElement(JavaSmartCompletionContributor.decorate(item, Arrays.asList(infos))); + consumer.consume(item); } } } @@ -92,18 +108,20 @@ public class JavaInheritorsGetter extends CompletionProvider infos) { - if (!JavaCompletionUtil.hasAccessibleConstructor(type)) return; + @Nullable + private LookupElement addExpectedType(final PsiType type, + final CompletionParameters parameters) { + if (!JavaCompletionUtil.hasAccessibleConstructor(type)) return null; final PsiClass psiClass = PsiUtil.resolveClassInType(type); - if (psiClass == null) return; + if (psiClass == null) return null; final PsiClass parentClass = psiClass.getContainingClass(); if (parentClass != null && !psiClass.hasModifierProperty(PsiModifier.STATIC) && !PsiTreeUtil.isAncestor(parentClass, parameters.getPosition(), false) && !(parentClass.getContainingFile().equals(parameters.getOriginalFile()) && parentClass.getTextRange().contains(parameters.getOffset()))) { - return; + return null; } final LookupItem item = PsiTypeLookupItem.createLookupItem(JavaCompletionUtil.eliminateWildcards(type), parameters.getPosition()); @@ -114,8 +132,7 @@ public class JavaInheritorsGetter extends CompletionProvider + new WithInnerAClass } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous.java new file mode 100644 index 000000000000..449600704f68 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous.java @@ -0,0 +1,9 @@ +class Foo { + { + Zzoo l = new Zz + } +} + +interface Zzoo { + void run(); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous_after.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous_after.java new file mode 100644 index 000000000000..47114349916a --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameAnonymous_after.java @@ -0,0 +1,14 @@ +class Foo { + { + Zzoo l = new Zzoo() { + @Override + public void run() { + //To change body of implemented methods use File | Settings | File Templates. + } + } + } +} + +interface Zzoo { + void run(); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics.java new file mode 100644 index 000000000000..6cd8c280d94c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics.java @@ -0,0 +1,7 @@ +import java.util.List; + +class Foo { + { + List l = new ArraLi + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics_after.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics_after.java new file mode 100644 index 000000000000..77077f665ab3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameGenerics_after.java @@ -0,0 +1,8 @@ +import java.util.ArrayList; +import java.util.List; + +class Foo { + { + List l = new ArrayList() + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner.java new file mode 100644 index 000000000000..58913ee34e16 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner.java @@ -0,0 +1,11 @@ +class Foo { + { + Zzoo l = new Zz + } +} + +interface Zzoo { + void run(); + + class Impl implements Zzoo {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner_after.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner_after.java new file mode 100644 index 000000000000..6225e351dd9a --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInner_after.java @@ -0,0 +1,11 @@ +class Foo { + { + Zzoo l = new Zzoo + } +} + +interface Zzoo { + void run(); + + class Impl implements Zzoo {} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 2dbcebb0b407..11f075ae5adb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -582,6 +582,20 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { doTest(); } + public void testClassNameGenerics() throws Throwable { + configure() + type '\n' + checkResult(); + } + + public void testClassNameAnonymous() throws Throwable { + configure() + type '\n' + checkResult(); + } + + public void testClassNameWithInner() throws Throwable { doTest() } + public void testDoubleFalse() throws Throwable { configureByFile(getTestName(false) + ".java"); assertStringItems("false", "finalize");