From 8e87fb2551919197849a1d8de4ded1695115df51 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 24 Jul 2012 12:11:28 +0200 Subject: [PATCH] IDEA-89175 Completing an annotation on an @interface class should always suggest meta-annotations --- .../completion/JavaNoVariantsDelegator.java | 16 +- .../PreferApplicableAnnotations.java | 14 +- .../NormalCompletionOrderingTest.groovy | 11 +- .../completion/GroovyNoVariantsDelegator.java | 364 +++++++++--------- .../completion/GroovyCompletionTest.groovy | 16 +- 5 files changed, 220 insertions(+), 201 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java index 78a87644b8ed..af6dc31c8482 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java @@ -30,6 +30,9 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import static com.intellij.patterns.PsiJavaPatterns.psiClass; +import static com.intellij.patterns.PsiJavaPatterns.psiElement; + /** * @author peter */ @@ -37,17 +40,24 @@ public class JavaNoVariantsDelegator extends CompletionContributor { @Override public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) { - final boolean empty = containsOnlyPackages(result.runRemainingContributors(parameters, true)); + final boolean empty = containsOnlyPackages(result.runRemainingContributors(parameters, true)) || + suggestMetaAnnotations(parameters); if (!empty && parameters.getInvocationCount() == 0) { result.restartCompletionWhenNothingMatches(); } if (empty) { - delegate(parameters, result); + delegate(parameters, JavaCompletionSorting.addJavaSorting(parameters, result)); } } + private static boolean suggestMetaAnnotations(CompletionParameters parameters) { + PsiElement position = parameters.getPosition(); + return psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiAnnotation.class, PsiModifierList.class, PsiClass.class).accepts( position) && + psiElement().withSuperParent(4, psiClass().isAnnotationType()).accepts(position); + } + public static boolean containsOnlyPackages(LinkedHashSet results) { for (CompletionResult result : results) { if (!(CompletionUtil.getTargetElement(result.getLookupElement()) instanceof PsiPackage)) { @@ -152,7 +162,7 @@ public class JavaNoVariantsDelegator extends CompletionContributor { final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result); JavaClassNameCompletionContributor.addAllClasses(parameters, - true, JavaCompletionSorting.addJavaSorting(parameters, result).getPrefixMatcher(), new Consumer() { + true, result.getPrefixMatcher(), new Consumer() { @Override public void consume(LookupElement element) { JavaPsiClassReferenceElement classElement = element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY); diff --git a/java/java-tests/testData/codeInsight/completion/normalSorting/PreferApplicableAnnotations.java b/java/java-tests/testData/codeInsight/completion/normalSorting/PreferApplicableAnnotations.java index 75ba46a656f5..fe6080708ee9 100644 --- a/java/java-tests/testData/codeInsight/completion/normalSorting/PreferApplicableAnnotations.java +++ b/java/java-tests/testData/codeInsight/completion/normalSorting/PreferApplicableAnnotations.java @@ -1,12 +1,2 @@ -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -@Z -@interface Foo { -} - -@Target({ElementType.ANNOTATION_TYPE}) -@interface ZMetaAnno {} - -@Target({ElementType.LOCAL_VARIABLE}) -@interface ZLocalAnno {} \ No newline at end of file +@T +@interface Foo { } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy index aa532f06acb5..df1588a69eab 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -343,7 +343,16 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase { } public void testPreferApplicableAnnotations() throws Throwable { - checkPreferredItems 0, 'ZMetaAnno', 'ZLocalAnno' + myFixture.addClass ''' +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.ANNOTATION_TYPE}) +@interface TMetaAnno {} + +@Target({ElementType.LOCAL_VARIABLE}) +@interface TLocalAnno {}''' + checkPreferredItems 0, 'TMetaAnno', 'Target', 'TreeSelectionMode', 'TLocalAnno' } public void testJComponentAddNewWithStats() throws Throwable { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java index ed5c200dac8c..0c485f1bfa63 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyNoVariantsDelegator.java @@ -1,176 +1,188 @@ -/* - * Copyright 2000-2011 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.plugins.groovy.lang.completion; - -import com.intellij.codeInsight.completion.*; -import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; -import com.intellij.codeInsight.lookup.AutoCompletionPolicy; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.psi.*; -import com.intellij.psi.search.PsiShortNamesCache; -import com.intellij.util.Consumer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * @author peter - */ -public class GroovyNoVariantsDelegator extends CompletionContributor { - - @Override - public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) { - final boolean empty = JavaNoVariantsDelegator.containsOnlyPackages(result.runRemainingContributors(parameters, true)); - - if (!empty && parameters.getInvocationCount() == 0) { - result.restartCompletionWhenNothingMatches(); - } - - if (empty) { - delegate(parameters, result); - } - } - - private static void delegate(CompletionParameters parameters, CompletionResultSet result) { - if (parameters.getCompletionType() == CompletionType.BASIC) { - if (parameters.getInvocationCount() <= 1 && - JavaCompletionContributor.mayStartClassName(result) && - GroovyCompletionContributor.isClassNamePossible(parameters.getPosition()) && - !MapArgumentCompletionProvider.isMapKeyCompletion(parameters)) { - suggestNonImportedClasses(parameters, result); - } - - suggestChainedCalls(parameters, result); - } - } - - private static void suggestNonImportedClasses(CompletionParameters parameters, CompletionResultSet result) { - final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result); - - GroovyCompletionContributor.addAllClasses(parameters, new Consumer() { - @Override - public void consume(LookupElement element) { - JavaPsiClassReferenceElement classElement = - element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY); - if (classElement != null) { - classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); - } - merger.consume(classElement); - } - }, new InheritorsHolder(parameters.getPosition(), result), result.getPrefixMatcher()); - - merger.finishedClassProcessing(); - } - - private static void suggestChainedCalls(CompletionParameters parameters, CompletionResultSet result) { - PsiElement position = parameters.getPosition(); - PsiElement parent = position.getParent(); - if (!(parent instanceof GrReferenceElement)) { - return; - } - PsiElement qualifier = ((GrReferenceElement)parent).getQualifier(); - if (!(qualifier instanceof GrReferenceElement) || - ((GrReferenceElement)qualifier).getQualifier() != null) { - return; - } - PsiElement target = ((GrReferenceElement)qualifier).resolve(); - if (target != null && !(target instanceof PsiPackage)) { - return; - } - - String fullPrefix = position.getContainingFile().getText().substring(parent.getTextRange().getStartOffset(), parameters.getOffset()); - final CompletionResultSet qualifiedCollector = result.withPrefixMatcher(fullPrefix); - InheritorsHolder inheritors = new InheritorsHolder(position, result); - for (final LookupElement base : suggestQualifierItems(parameters, (GrReferenceElement)qualifier, inheritors)) { - final PsiType type = JavaCompletionUtil.getLookupElementType(base); - if (type != null && !PsiType.VOID.equals(type)) { - GrReferenceElement ref = createMockReference(position, type, base); - PsiElement refName = ref.getReferenceNameElement(); - assert refName != null; - CompletionParameters newParams = parameters.withPosition(refName, refName.getTextRange().getStartOffset()); - GroovyCompletionContributor.completeReference(newParams, ref, inheritors, result.getPrefixMatcher(), new Consumer() { - @Override - public void consume(LookupElement element) { - qualifiedCollector.addElement(new JavaChainLookupElement(base, element) { - @Override - protected boolean shouldParenthesizeQualifier(PsiFile file, int startOffset, int endOffset) { - return false; - } - }); - } - }); - } - } - } - - private static GrReferenceElement createMockReference(final PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) { - GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(place.getProject()); - if (qualifierItem.getObject() instanceof PsiClass) { - return factory - .createReferenceExpressionFromText(((PsiClass)qualifierItem.getObject()).getQualifiedName() + ".xxx", place); - } - - return factory.createReferenceExpressionFromText("xxx.xxx", - JavaCompletionUtil - .createContextWithXxxVariable(place, qualifierType)); - } - - - private static Set suggestQualifierItems(CompletionParameters _parameters, - GrReferenceElement qualifier, - InheritorsHolder inheritors) { - CompletionParameters parameters = - _parameters.withPosition(qualifier.getReferenceNameElement(), qualifier.getTextRange().getEndOffset()); - String referenceName = qualifier.getReferenceName(); - if (referenceName == null) { - return Collections.emptySet(); - } - - final PrefixMatcher qMatcher = new CamelHumpMatcher(referenceName); - final Set variants = new LinkedHashSet(); - GroovyCompletionContributor.completeReference(parameters, qualifier, inheritors, qMatcher, new Consumer() { - @Override - public void consume(LookupElement element) { - if (qMatcher.prefixMatches(element)) { - variants.add(element); - } - } - }); - - for (PsiClass aClass : PsiShortNamesCache.getInstance(qualifier.getProject()).getClassesByName(referenceName, qualifier.getResolveScope())) { - variants.add(GroovyCompletionUtil.createClassLookupItem(aClass)); - } - - - if (variants.isEmpty()) { - GroovyCompletionContributor.addAllClasses(parameters, new Consumer() { - @Override - public void consume(LookupElement element) { - if (qMatcher.prefixMatches(element)) { - variants.add(element); - } - } - }, inheritors, qMatcher); - } - return variants; - } - - -} +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.lang.completion; + +import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; +import com.intellij.codeInsight.lookup.AutoCompletionPolicy; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.psi.*; +import com.intellij.psi.search.PsiShortNamesCache; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnnotationTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import static com.intellij.patterns.PsiJavaPatterns.psiElement; + +/** + * @author peter + */ +public class GroovyNoVariantsDelegator extends CompletionContributor { + + private static boolean suggestMetaAnnotations(CompletionParameters parameters) { + PsiElement position = parameters.getPosition(); + return psiElement().withParents(GrCodeReferenceElement.class, GrAnnotation.class, GrModifierList.class, GrAnnotationTypeDefinition.class).accepts(position); + } + + @Override + public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) { + final boolean empty = JavaNoVariantsDelegator.containsOnlyPackages(result.runRemainingContributors(parameters, true)) || + suggestMetaAnnotations(parameters); + + if (!empty && parameters.getInvocationCount() == 0) { + result.restartCompletionWhenNothingMatches(); + } + + if (empty) { + delegate(parameters, result); + } + } + + private static void delegate(CompletionParameters parameters, CompletionResultSet result) { + if (parameters.getCompletionType() == CompletionType.BASIC) { + if (parameters.getInvocationCount() <= 1 && + JavaCompletionContributor.mayStartClassName(result) && + GroovyCompletionContributor.isClassNamePossible(parameters.getPosition()) && + !MapArgumentCompletionProvider.isMapKeyCompletion(parameters)) { + suggestNonImportedClasses(parameters, result); + } + + suggestChainedCalls(parameters, result); + } + } + + private static void suggestNonImportedClasses(CompletionParameters parameters, CompletionResultSet result) { + final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result); + + GroovyCompletionContributor.addAllClasses(parameters, new Consumer() { + @Override + public void consume(LookupElement element) { + JavaPsiClassReferenceElement classElement = + element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY); + if (classElement != null) { + classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + } + merger.consume(classElement); + } + }, new InheritorsHolder(parameters.getPosition(), result), result.getPrefixMatcher()); + + merger.finishedClassProcessing(); + } + + private static void suggestChainedCalls(CompletionParameters parameters, CompletionResultSet result) { + PsiElement position = parameters.getPosition(); + PsiElement parent = position.getParent(); + if (!(parent instanceof GrReferenceElement)) { + return; + } + PsiElement qualifier = ((GrReferenceElement)parent).getQualifier(); + if (!(qualifier instanceof GrReferenceElement) || + ((GrReferenceElement)qualifier).getQualifier() != null) { + return; + } + PsiElement target = ((GrReferenceElement)qualifier).resolve(); + if (target != null && !(target instanceof PsiPackage)) { + return; + } + + String fullPrefix = position.getContainingFile().getText().substring(parent.getTextRange().getStartOffset(), parameters.getOffset()); + final CompletionResultSet qualifiedCollector = result.withPrefixMatcher(fullPrefix); + InheritorsHolder inheritors = new InheritorsHolder(position, result); + for (final LookupElement base : suggestQualifierItems(parameters, (GrReferenceElement)qualifier, inheritors)) { + final PsiType type = JavaCompletionUtil.getLookupElementType(base); + if (type != null && !PsiType.VOID.equals(type)) { + GrReferenceElement ref = createMockReference(position, type, base); + PsiElement refName = ref.getReferenceNameElement(); + assert refName != null; + CompletionParameters newParams = parameters.withPosition(refName, refName.getTextRange().getStartOffset()); + GroovyCompletionContributor.completeReference(newParams, ref, inheritors, result.getPrefixMatcher(), new Consumer() { + @Override + public void consume(LookupElement element) { + qualifiedCollector.addElement(new JavaChainLookupElement(base, element) { + @Override + protected boolean shouldParenthesizeQualifier(PsiFile file, int startOffset, int endOffset) { + return false; + } + }); + } + }); + } + } + } + + private static GrReferenceElement createMockReference(final PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) { + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(place.getProject()); + if (qualifierItem.getObject() instanceof PsiClass) { + return factory + .createReferenceExpressionFromText(((PsiClass)qualifierItem.getObject()).getQualifiedName() + ".xxx", place); + } + + return factory.createReferenceExpressionFromText("xxx.xxx", + JavaCompletionUtil + .createContextWithXxxVariable(place, qualifierType)); + } + + + private static Set suggestQualifierItems(CompletionParameters _parameters, + GrReferenceElement qualifier, + InheritorsHolder inheritors) { + CompletionParameters parameters = + _parameters.withPosition(qualifier.getReferenceNameElement(), qualifier.getTextRange().getEndOffset()); + String referenceName = qualifier.getReferenceName(); + if (referenceName == null) { + return Collections.emptySet(); + } + + final PrefixMatcher qMatcher = new CamelHumpMatcher(referenceName); + final Set variants = new LinkedHashSet(); + GroovyCompletionContributor.completeReference(parameters, qualifier, inheritors, qMatcher, new Consumer() { + @Override + public void consume(LookupElement element) { + if (qMatcher.prefixMatches(element)) { + variants.add(element); + } + } + }); + + for (PsiClass aClass : PsiShortNamesCache.getInstance(qualifier.getProject()).getClassesByName(referenceName, qualifier.getResolveScope())) { + variants.add(GroovyCompletionUtil.createClassLookupItem(aClass)); + } + + + if (variants.isEmpty()) { + GroovyCompletionContributor.addAllClasses(parameters, new Consumer() { + @Override + public void consume(LookupElement element) { + if (qMatcher.prefixMatches(element)) { + variants.add(element); + } + } + }, inheritors, qMatcher); + } + return variants; + } + + +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy index 4b2c458e6d94..1effb9ba323f 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy @@ -1399,21 +1399,19 @@ def bar(){}''') } public void testPreferApplicableAnnotations() { - configure(''' + myFixture.addClass ''' import java.lang.annotation.ElementType; import java.lang.annotation.Target; -@Z -@interface Foo { -} +@Target({ElementType.ANNOTATION_TYPE}) +@interface TMetaAnno {} -@Target(ElementType.ANNOTATION_TYPE) -@interface ZMetaAnno {} +@Target({ElementType.LOCAL_VARIABLE}) +@interface TLocalAnno {}''' -@Target(ElementType.LOCAL_VARIABLE) -@interface ZLocalAnno {}''') + configure('@T @interface Foo {}') myFixture.completeBasic() - myFixture.assertPreferredCompletionItems 0, 'ZMetaAnno', 'ZLocalAnno' + myFixture.assertPreferredCompletionItems 0, 'TMetaAnno', 'Target', 'TreeSelectionMode', 'TLocalAnno' } } \ No newline at end of file