blob: 26a2d9cf294de467d77122b6bb8660c5d1df8c77 [file] [log] [blame]
/*
* Copyright 2000-2014 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;
/**
* Created by Max Medvedev on 25/04/14
*/
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_IN_QUALIFIED_NEW:
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);
ResolveUtil.treeWalkUp(myRef, classProcessor, false);
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;
}
}