/* * Copyright 2000-2017 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.JavaClassNameCompletionContributor; import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody; import org.jetbrains.plugins.groovy.lang.psi.api.types.*; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrCodeReferenceElementImpl; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.lang.resolve.processors.CompletionProcessor; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; import java.util.List; import static org.jetbrains.plugins.groovy.lang.psi.util.PsiTreeUtilKt.treeWalkUp; public class CompleteCodeReferenceElement { private static final Logger LOG = Logger.getInstance(CompleteCodeReferenceElement.class); private final GrCodeReferenceElementImpl myRef; private final Consumer<LookupElement> myConsumer; private final PrefixMatcher myMatcher; private CompleteCodeReferenceElement(@NotNull GrCodeReferenceElementImpl ref, @NotNull Consumer<LookupElement> consumer, @NotNull PrefixMatcher matcher) { myRef = ref; myConsumer = consumer; myMatcher = matcher; } public static void processVariants(@NotNull GrCodeReferenceElementImpl ref, @NotNull Consumer<LookupElement> consumer, @NotNull PrefixMatcher matcher) { new CompleteCodeReferenceElement(ref, consumer, matcher).processVariantsImpl(); } private void feedLookupElements(@NotNull PsiNamedElement psi, boolean afterNew) { GroovyResolveResultImpl candidate = new GroovyResolveResultImpl(psi, true); List<? extends LookupElement> elements = GroovyCompletionUtil.createLookupElements(candidate, afterNew, myMatcher, null); for (LookupElement element : elements) { myConsumer.consume(element); } } public void processVariantsImpl() { boolean afterNew = JavaClassNameCompletionContributor.AFTER_NEW.accepts(myRef); switch (myRef.getKind(true)) { case STATIC_MEMBER_FQ: { final GrCodeReferenceElement qualifier = myRef.getQualifier(); if (qualifier != null) { final PsiElement resolve = qualifier.resolve(); if (resolve instanceof PsiClass) { final PsiClass clazz = (PsiClass)resolve; for (PsiField field : clazz.getFields()) { if (field.hasModifierProperty(PsiModifier.STATIC)) { feedLookupElements(field, afterNew); } } for (PsiMethod method : clazz.getMethods()) { if (method.hasModifierProperty(PsiModifier.STATIC)) { feedLookupElements(method, afterNew); } } for (PsiClass inner : clazz.getInnerClasses()) { if (inner.hasModifierProperty(PsiModifier.STATIC)) { feedLookupElements(inner, afterNew); } } return; } } } // fall through case PACKAGE_FQ: case CLASS_FQ: case CLASS_OR_PACKAGE_FQ: { final String refText = PsiUtil.getQualifiedReferenceText(myRef); LOG.assertTrue(refText != null, myRef.getText()); String parentPackageFQName = StringUtil.getPackageName(refText); final PsiPackage parentPackage = JavaPsiFacade.getInstance(myRef.getProject()).findPackage(parentPackageFQName); if (parentPackage != null) { final GlobalSearchScope scope = myRef.getResolveScope(); if (myRef.getKind(true) == GrCodeReferenceElementImpl.ReferenceKind.PACKAGE_FQ) { for (PsiPackage aPackage : parentPackage.getSubPackages(scope)) { feedLookupElements(aPackage, afterNew); } return; } if (myRef.getKind(true) == GrCodeReferenceElementImpl.ReferenceKind.CLASS_FQ) { for (PsiClass aClass : parentPackage.getClasses(scope)) { feedLookupElements(aClass, afterNew); } return; } for (PsiPackage aPackage : parentPackage.getSubPackages(scope)) { feedLookupElements(aPackage, afterNew); } for (PsiClass aClass : parentPackage.getClasses(scope)) { feedLookupElements(aClass, afterNew); } return; } } case CLASS_OR_PACKAGE: case CLASS: { GrCodeReferenceElement qualifier = myRef.getQualifier(); if (qualifier != null) { PsiElement qualifierResolved = qualifier.resolve(); if (qualifierResolved instanceof PsiPackage) { PsiPackage aPackage = (PsiPackage)qualifierResolved; for (PsiClass aClass : aPackage.getClasses(myRef.getResolveScope())) { feedLookupElements(aClass, afterNew); } if (myRef.getKind(true) == GrCodeReferenceElementImpl.ReferenceKind.CLASS) return; for (PsiPackage subpackage : aPackage.getSubPackages(myRef.getResolveScope())) { feedLookupElements(subpackage, afterNew); } } else if (qualifierResolved instanceof PsiClass) { for (PsiClass aClass : ((PsiClass)qualifierResolved).getInnerClasses()) { feedLookupElements(aClass, afterNew); } } } else { ResolverProcessor classProcessor = CompletionProcessor.createClassCompletionProcessor(myRef); processTypeParametersFromUnfinishedMethodOrField(classProcessor); treeWalkUp(myRef, classProcessor); for (LookupElement o : GroovyCompletionUtil.getCompletionVariants(classProcessor.getCandidates(), afterNew, myMatcher, myRef)) { myConsumer.consume(o); } } } } } private void processTypeParametersFromUnfinishedMethodOrField(@NotNull ResolverProcessor processor) { final PsiElement candidate = findTypeParameterListCandidate(); if (candidate instanceof GrTypeParameterList) { for (GrTypeParameter p : ((GrTypeParameterList)candidate).getTypeParameters()) { ResolveUtil.processElement(processor, p, ResolveState.initial()); } } } @Nullable private PsiElement findTypeParameterListCandidate() { final GrTypeElement typeElement = getRootTypeElement(); if (typeElement == null) return null; if (typeElement.getParent() instanceof GrTypeDefinitionBody) { return PsiUtil.skipWhitespacesAndComments(typeElement.getPrevSibling(), false); } if (typeElement.getParent() instanceof GrVariableDeclaration) { final PsiElement errorElement = PsiUtil.skipWhitespacesAndComments(typeElement.getPrevSibling(), false); if (errorElement instanceof PsiErrorElement) { return errorElement.getFirstChild(); } } return null; } @Nullable private GrTypeElement getRootTypeElement() { PsiElement parent = myRef.getParent(); while (isTypeElementChild(parent)) { if (parent instanceof GrTypeElement && !isTypeElementChild(parent.getParent())) return (GrTypeElement)parent; parent = parent.getParent(); } return null; } private static boolean isTypeElementChild(@Nullable PsiElement element) { return element instanceof GrCodeReferenceElement || element instanceof GrTypeArgumentList || element instanceof GrTypeElement; } }