blob: 2fc06a2dabd5fd39e478d40b5dde90eb73887c7e [file] [log] [blame]
/*
* Copyright 2000-2009 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 com.intellij.codeInsight.completion;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypesProvider;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.lang.LangBundle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.impl.CustomSyntaxTableFileType;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.PsiJavaElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.filters.ClassFilter;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.TrueFilter;
import com.intellij.psi.filters.classes.AnnotationTypeFilter;
import com.intellij.psi.filters.element.ExcludeDeclaredFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Consumer;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
/**
* @author peter
*/
public class JavaClassNameCompletionContributor extends CompletionContributor {
public static final PsiJavaElementPattern.Capture<PsiElement> AFTER_NEW = psiElement().afterLeaf(PsiKeyword.NEW);
private static final PsiJavaElementPattern.Capture<PsiElement> IN_TYPE_PARAMETER =
psiElement().afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.SUPER, "&").withParent(
psiElement(PsiReferenceList.class).withParent(PsiTypeParameter.class));
@Override
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull final CompletionResultSet _result) {
if (parameters.getCompletionType() == CompletionType.CLASS_NAME ||
parameters.isExtendedCompletion() && mayContainClassName(parameters)) {
addAllClasses(parameters, _result);
}
}
static void addAllClasses(CompletionParameters parameters, final CompletionResultSet _result) {
CompletionResultSet result = _result.withPrefixMatcher(CompletionUtil.findReferenceOrAlphanumericPrefix(parameters));
addAllClasses(parameters, parameters.getInvocationCount() <= 1, result.getPrefixMatcher(), _result);
}
private static boolean mayContainClassName(CompletionParameters parameters) {
PsiElement position = parameters.getPosition();
PsiFile file = position.getContainingFile();
if (file instanceof PsiPlainTextFile || file.getFileType() instanceof CustomSyntaxTableFileType) {
return true;
}
if (SkipAutopopupInStrings.isInStringLiteral(position)) {
return true;
}
if (PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null) {
return true;
}
return false;
}
public static void addAllClasses(@NotNull CompletionParameters parameters,
final boolean filterByScope,
@NotNull final PrefixMatcher matcher,
@NotNull final Consumer<LookupElement> consumer) {
final PsiElement insertedElement = parameters.getPosition();
final ElementFilter filter =
IN_TYPE_PARAMETER.accepts(insertedElement) ? new ExcludeDeclaredFilter(new ClassFilter(PsiTypeParameter.class)) :
JavaCompletionContributor.ANNOTATION_NAME.accepts(insertedElement) ? new AnnotationTypeFilter() :
TrueFilter.INSTANCE;
final boolean inJavaContext = parameters.getPosition() instanceof PsiIdentifier;
final boolean afterNew = AFTER_NEW.accepts(insertedElement);
if (afterNew) {
final PsiExpression expr = PsiTreeUtil.getContextOfType(insertedElement, PsiExpression.class, true);
for (final ExpectedTypeInfo info : ExpectedTypesProvider.getExpectedTypes(expr, true)) {
final PsiType type = info.getType();
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
if (psiClass != null) {
consumer.consume(createClassLookupItem(psiClass, inJavaContext));
}
final PsiType defaultType = info.getDefaultType();
if (!defaultType.equals(type)) {
final PsiClass defClass = PsiUtil.resolveClassInType(defaultType);
if (defClass != null) {
consumer.consume(createClassLookupItem(defClass, true));
}
}
}
}
final boolean pkgContext = JavaCompletionUtil.inSomePackage(insertedElement);
AllClassesGetter.processJavaClasses(parameters, matcher, filterByScope, new Consumer<PsiClass>() {
@Override
public void consume(PsiClass psiClass) {
if (filter.isAcceptable(psiClass, insertedElement)) {
if (!inJavaContext) {
consumer.consume(AllClassesGetter.createLookupItem(psiClass, AllClassesGetter.TRY_SHORTENING));
} else {
for (JavaPsiClassReferenceElement element : createClassLookupItems(psiClass, afterNew,
JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
return filter.isAcceptable(psiClass, insertedElement) &&
AllClassesGetter.isAcceptableInContext(insertedElement, psiClass, filterByScope, pkgContext);
}
})) {
consumer.consume(element);
}
}
}
}
});
}
public static JavaPsiClassReferenceElement createClassLookupItem(final PsiClass psiClass, final boolean inJavaContext) {
return AllClassesGetter.createLookupItem(psiClass, inJavaContext ? JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER
: AllClassesGetter.TRY_SHORTENING);
}
public static List<JavaPsiClassReferenceElement> createClassLookupItems(final PsiClass psiClass,
boolean withInners,
InsertHandler<JavaPsiClassReferenceElement> insertHandler,
Condition<PsiClass> condition) {
List<JavaPsiClassReferenceElement> result = new SmartList<JavaPsiClassReferenceElement>();
if (condition.value(psiClass)) {
result.add(AllClassesGetter.createLookupItem(psiClass, insertHandler));
}
String name = psiClass.getName();
if (withInners && name != null) {
for (PsiClass inner : psiClass.getInnerClasses()) {
if (inner.hasModifierProperty(PsiModifier.STATIC)) {
for (JavaPsiClassReferenceElement lookupInner : createClassLookupItems(inner, true, insertHandler, condition)) {
String forced = lookupInner.getForcedPresentableName();
String qualifiedName = name + "." + (forced != null ? forced : inner.getName());
lookupInner.setForcedPresentableName(qualifiedName);
lookupInner.setLookupString(qualifiedName);
result.add(lookupInner);
}
}
}
}
return result;
}
@Override
public String handleEmptyLookup(@NotNull final CompletionParameters parameters, final Editor editor) {
if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null;
if (shouldShowSecondSmartCompletionHint(parameters)) {
return LangBundle.message("completion.no.suggestions") +
"; " +
StringUtil.decapitalize(
CompletionBundle.message("completion.class.name.hint.2", getActionShortcut(IdeActions.ACTION_CODE_COMPLETION)));
}
return null;
}
private static boolean shouldShowSecondSmartCompletionHint(final CompletionParameters parameters) {
return parameters.getCompletionType() == CompletionType.BASIC &&
parameters.getInvocationCount() == 2 &&
parameters.getOriginalFile().getLanguage().isKindOf(JavaLanguage.INSTANCE);
}
}