blob: fba10a6334322acb2952c087eb2729029b0a8ce0 [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 com.intellij.codeInsight.completion;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix;
import com.intellij.codeInsight.hint.ShowParameterInfoHandler;
import com.intellij.codeInsight.lookup.*;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.lang.LangBundle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PatternCondition;
import com.intellij.patterns.PsiJavaElementPattern;
import com.intellij.patterns.PsiNameValuePairPattern;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.filters.*;
import com.intellij.psi.filters.classes.AnnotationTypeFilter;
import com.intellij.psi.filters.classes.AssignableFromContextFilter;
import com.intellij.psi.filters.element.ExcludeDeclaredFilter;
import com.intellij.psi.filters.element.ModifierFilter;
import com.intellij.psi.filters.getters.ExpectedTypesGetter;
import com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl;
import com.intellij.psi.impl.source.PsiLabelReference;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.scope.ElementClassFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.Consumer;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PairConsumer;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.patterns.PsiJavaPatterns.*;
/**
* @author peter
*/
public class JavaCompletionContributor extends CompletionContributor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionContributor");
private static final Map<LanguageLevel, JavaCompletionData> ourCompletionData;
static {
ourCompletionData = new LinkedHashMap<LanguageLevel, JavaCompletionData>();
ourCompletionData.put(LanguageLevel.JDK_1_8, new Java18CompletionData());
ourCompletionData.put(LanguageLevel.JDK_1_5, new Java15CompletionData());
ourCompletionData.put(LanguageLevel.JDK_1_3, new JavaCompletionData());
}
public static final ElementPattern<PsiElement> ANNOTATION_NAME = psiElement().
withParents(PsiJavaCodeReferenceElement.class, PsiAnnotation.class).afterLeaf("@");
private static final PsiJavaElementPattern.Capture<PsiElement> UNEXPECTED_REFERENCE_AFTER_DOT =
psiElement().afterLeaf(".").insideStarting(psiExpressionStatement());
private static JavaCompletionData getCompletionData(LanguageLevel level) {
final Set<Map.Entry<LanguageLevel, JavaCompletionData>> entries = ourCompletionData.entrySet();
for (Map.Entry<LanguageLevel, JavaCompletionData> entry : entries) {
if (entry.getKey().isAtLeast(level)) return entry.getValue();
}
return ourCompletionData.get(LanguageLevel.JDK_1_3);
}
private static final PsiNameValuePairPattern NAME_VALUE_PAIR =
psiNameValuePair().withSuperParent(2, psiElement(PsiAnnotation.class));
private static final ElementPattern<PsiElement> ANNOTATION_ATTRIBUTE_NAME =
or(psiElement(PsiIdentifier.class).withParent(NAME_VALUE_PAIR),
psiElement().afterLeaf("(").withParent(psiReferenceExpression().withParent(NAME_VALUE_PAIR)));
private static final ElementPattern SWITCH_LABEL =
psiElement().withSuperParent(2, psiElement(PsiSwitchLabelStatement.class).withSuperParent(2,
psiElement(PsiSwitchStatement.class).with(new PatternCondition<PsiSwitchStatement>("enumExpressionType") {
@Override
public boolean accepts(@NotNull PsiSwitchStatement psiSwitchStatement, ProcessingContext context) {
final PsiExpression expression = psiSwitchStatement.getExpression();
if(expression == null) return false;
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
return aClass != null && aClass.isEnum();
}
})));
private static final ElementPattern<PsiElement> AFTER_NUMBER_LITERAL =
psiElement().afterLeaf(psiElement().withElementType(elementType().oneOf(JavaTokenType.DOUBLE_LITERAL, JavaTokenType.LONG_LITERAL,
JavaTokenType.FLOAT_LITERAL, JavaTokenType.INTEGER_LITERAL)));
private static final ElementPattern<PsiElement> IMPORT_REFERENCE =
psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class).withParent(PsiImportStatementBase.class));
@Nullable
public static ElementFilter getReferenceFilter(PsiElement position) {
// Completion after extends in interface, type parameter and implements in class
final PsiClass containingClass = PsiTreeUtil.getParentOfType(position, PsiClass.class, false, PsiCodeBlock.class, PsiMethod.class, PsiExpressionList.class, PsiVariable.class, PsiAnnotation.class);
if (containingClass != null && psiElement().afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS, ",", "&").accepts(position)) {
return new AndFilter(ElementClassFilter.CLASS, new NotFilter(new AssignableFromContextFilter()));
}
if (ANNOTATION_NAME.accepts(position)) {
return new AnnotationTypeFilter();
}
if (JavaCompletionData.DECLARATION_START.accepts(position) ||
JavaCompletionData.isInsideParameterList(position) ||
psiElement().inside(psiElement(PsiJavaCodeReferenceElement.class).withParent(psiAnnotation())).accepts(position)) {
return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER);
}
if (psiElement().afterLeaf(PsiKeyword.INSTANCEOF).accepts(position)) {
return new ElementExtractorFilter(ElementClassFilter.CLASS);
}
if (JavaCompletionData.VARIABLE_AFTER_FINAL.accepts(position)) {
return ElementClassFilter.CLASS;
}
if (JavaCompletionData.AFTER_TRY_BLOCK.isAcceptable(position, position) ||
JavaCompletionData.START_SWITCH.accepts(position) ||
JavaCompletionData.isInstanceofPlace(position) ||
JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
return null;
}
if (JavaCompletionData.START_FOR.accepts(position)) {
return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.VARIABLE);
}
if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) {
return ElementClassFilter.CLASS;
}
if (psiElement().inside(PsiReferenceParameterList.class).accepts(position)) {
return ElementClassFilter.CLASS;
}
if (psiElement().inside(PsiAnnotationParameterList.class).accepts(position)) {
return createAnnotationFilter(position);
}
if (psiElement().afterLeaf("=").inside(PsiVariable.class).accepts(position)) {
return new OrFilter(
new ClassFilter(PsiVariable.class, false),
new ExcludeDeclaredFilter(new ClassFilter(PsiVariable.class)));
}
if (SWITCH_LABEL.accepts(position)) {
return new ClassFilter(PsiField.class) {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return element instanceof PsiEnumConstant;
}
};
}
return TrueFilter.INSTANCE;
}
private static ElementFilter createAnnotationFilter(PsiElement position) {
OrFilter orFilter = new OrFilter(ElementClassFilter.CLASS,
ElementClassFilter.PACKAGE_FILTER,
new AndFilter(new ClassFilter(PsiField.class),
new ModifierFilter(PsiModifier.STATIC, PsiModifier.FINAL)));
if (psiElement().insideStarting(psiNameValuePair()).accepts(position)) {
orFilter.addFilter(new ClassFilter(PsiAnnotationMethod.class) {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return element instanceof PsiAnnotationMethod && PsiUtil.isAnnotationMethod((PsiElement)element);
}
});
}
return orFilter;
}
@Override
public void fillCompletionVariants(@NotNull final CompletionParameters parameters, @NotNull final CompletionResultSet _result) {
if (parameters.getCompletionType() != CompletionType.BASIC) {
return;
}
final PsiElement position = parameters.getPosition();
if (!isInJavaContext(position)) {
return;
}
if (AFTER_NUMBER_LITERAL.accepts(position) || UNEXPECTED_REFERENCE_AFTER_DOT.accepts(position)) {
_result.stopHere();
return;
}
final CompletionResultSet result = JavaCompletionSorting.addJavaSorting(parameters, _result);
if (ANNOTATION_ATTRIBUTE_NAME.accepts(position) && !JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
JavaCompletionData.addExpectedTypeMembers(parameters, result);
completeAnnotationAttributeName(result, position, parameters);
result.stopHere();
return;
}
final InheritorsHolder inheritors = new InheritorsHolder(position, result);
if (JavaSmartCompletionContributor.IN_TYPE_ARGS.accepts(position)) {
new TypeArgumentCompletionProvider(false, inheritors).addCompletions(parameters, new ProcessingContext(), result);
}
PrefixMatcher matcher = result.getPrefixMatcher();
if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) {
new JavaInheritorsGetter(ConstructorInsertHandler.BASIC_INSTANCE).generateVariants(parameters, matcher, inheritors);
}
if (IMPORT_REFERENCE.accepts(position)) {
result.addElement(LookupElementBuilder.create("*"));
}
addKeywords(parameters, result);
Set<String> usedWords = addReferenceVariants(parameters, result, inheritors);
if (psiElement().inside(PsiLiteralExpression.class).accepts(position)) {
PsiReference reference = position.getContainingFile().findReferenceAt(parameters.getOffset());
if (reference == null || reference.isSoft()) {
WordCompletionContributor.addWordCompletionVariants(result, parameters, usedWords);
}
}
JavaGenerateMemberCompletionContributor.fillCompletionVariants(parameters, result);
addAllClasses(parameters, result, inheritors);
final PsiElement parent = position.getParent();
if (parent instanceof PsiReferenceExpression &&
!((PsiReferenceExpression)parent).isQualified() &&
parameters.isExtendedCompletion() &&
StringUtil.isNotEmpty(matcher.getPrefix())) {
new JavaStaticMemberProcessor(parameters).processStaticMethodsGlobally(matcher, result);
}
result.stopHere();
}
public static boolean isInJavaContext(PsiElement position) {
return PsiUtilCore.findLanguageFromElement(position).isKindOf(JavaLanguage.INSTANCE);
}
public static void addAllClasses(CompletionParameters parameters,
final CompletionResultSet result,
final InheritorsHolder inheritors) {
if (!isClassNamePossible(parameters) || !mayStartClassName(result)) {
return;
}
if (parameters.getInvocationCount() >= 2) {
JavaClassNameCompletionContributor.addAllClasses(parameters, parameters.getInvocationCount() <= 2, result.getPrefixMatcher(), new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
if (!inheritors.alreadyProcessed(element)) {
result.addElement(element);
}
}
});
} else {
advertiseSecondCompletion(parameters.getPosition().getProject(), result);
}
}
public static void advertiseSecondCompletion(Project project, CompletionResultSet result) {
if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.SECOND_BASIC_COMPLETION, project)) {
result.addLookupAdvertisement("Press " + getActionShortcut(IdeActions.ACTION_CODE_COMPLETION) + " to see non-imported classes");
}
}
private static Set<String> addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, final InheritorsHolder inheritors) {
final Set<String> usedWords = new HashSet<String>();
final PsiElement position = parameters.getPosition();
final boolean first = parameters.getInvocationCount() <= 1;
final boolean isSwitchLabel = SWITCH_LABEL.accepts(position);
final boolean isAfterNew = JavaClassNameCompletionContributor.AFTER_NEW.accepts(position);
final boolean pkgContext = JavaCompletionUtil.inSomePackage(position);
LegacyCompletionContributor.processReferences(parameters, result, new PairConsumer<PsiReference, CompletionResultSet>() {
@Override
public void consume(final PsiReference reference, final CompletionResultSet result) {
if (reference instanceof PsiJavaReference) {
final ElementFilter filter = getReferenceFilter(position);
if (filter != null) {
final PsiFile originalFile = parameters.getOriginalFile();
JavaCompletionProcessor.Options options =
JavaCompletionProcessor.Options.DEFAULT_OPTIONS
.withCheckAccess(first)
.withFilterStaticAfterInstance(first)
.withShowInstanceInStaticContext(!first);
for (LookupElement element : JavaCompletionUtil.processJavaReference(position,
(PsiJavaReference)reference,
new ElementExtractorFilter(filter),
options,
result.getPrefixMatcher(), parameters)) {
if (inheritors.alreadyProcessed(element)) {
continue;
}
if (isSwitchLabel) {
result.addElement(TailTypeDecorator.withTail(element, TailType.createSimpleTailType(':')));
}
else {
final LookupItem item = element.as(LookupItem.CLASS_CONDITION_KEY);
if (originalFile instanceof PsiJavaCodeReferenceCodeFragment &&
!((PsiJavaCodeReferenceCodeFragment)originalFile).isClassesAccepted() && item != null) {
item.setTailType(TailType.NONE);
}
result.addElement(element);
}
}
}
return;
}
if (reference instanceof PsiLabelReference) {
processLabelReference(result, (PsiLabelReference)reference);
return;
}
final Object[] variants = reference.getVariants();
//noinspection ConstantConditions
if (variants == null) {
LOG.error("Reference=" + reference);
}
for (Object completion : variants) {
if (completion == null) {
LOG.error("Position=" + position + "\n;Reference=" + reference + "\n;variants=" + Arrays.toString(variants));
}
if (completion instanceof LookupElement && !inheritors.alreadyProcessed((LookupElement)completion)) {
usedWords.add(((LookupElement)completion).getLookupString());
result.addElement((LookupElement)completion);
}
else if (completion instanceof PsiClass) {
for (JavaPsiClassReferenceElement item : JavaClassNameCompletionContributor.createClassLookupItems((PsiClass)completion, isAfterNew,
JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
return !inheritors.alreadyProcessed(psiClass) && JavaCompletionUtil.isSourceLevelAccessible(position, psiClass, pkgContext);
}
})) {
usedWords.add(item.getLookupString());
result.addElement(item);
}
}
else {
//noinspection deprecation
LookupElement element = LookupItemUtil.objectToLookupItem(completion);
usedWords.add(element.getLookupString());
result.addElement(element);
}
}
}
});
return usedWords;
}
private static void addKeywords(CompletionParameters parameters, CompletionResultSet result) {
PsiElement position = parameters.getPosition();
final Set<LookupElement> lookupSet = new LinkedHashSet<LookupElement>();
final Set<CompletionVariant> keywordVariants = new HashSet<CompletionVariant>();
final JavaCompletionData completionData = getCompletionData(PsiUtil.getLanguageLevel(position));
completionData.addKeywordVariants(keywordVariants, position, parameters.getOriginalFile());
completionData.completeKeywordsBySet(lookupSet, keywordVariants, position, result.getPrefixMatcher(), parameters.getOriginalFile());
completionData.fillCompletions(parameters, result);
for (final LookupElement item : lookupSet) {
result.addElement(item);
}
}
static boolean isClassNamePossible(CompletionParameters parameters) {
boolean isSecondCompletion = parameters.getInvocationCount() >= 2;
PsiElement position = parameters.getPosition();
if (JavaCompletionData.isInstanceofPlace(position)) return false;
final PsiElement parent = position.getParent();
if (!(parent instanceof PsiJavaCodeReferenceElement)) return isSecondCompletion;
if (((PsiJavaCodeReferenceElement)parent).getQualifier() != null) return isSecondCompletion;
if (parent instanceof PsiJavaCodeReferenceElementImpl &&
((PsiJavaCodeReferenceElementImpl)parent).getKind(parent.getContainingFile()) == PsiJavaCodeReferenceElementImpl.PACKAGE_NAME_KIND) {
return false;
}
PsiElement grand = parent.getParent();
if (grand instanceof PsiSwitchLabelStatement) {
return false;
}
if (psiElement().inside(PsiImportStatement.class).accepts(parent)) {
return isSecondCompletion;
}
if (grand instanceof PsiAnonymousClass) {
grand = grand.getParent();
}
if (grand instanceof PsiNewExpression && ((PsiNewExpression)grand).getQualifier() != null) {
return false;
}
if (JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
return false;
}
return true;
}
public static boolean mayStartClassName(CompletionResultSet result) {
return StringUtil.isNotEmpty(result.getPrefixMatcher().getPrefix());
}
private static void completeAnnotationAttributeName(CompletionResultSet result, PsiElement insertedElement,
CompletionParameters parameters) {
PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class);
PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)ObjectUtils.assertNotNull(pair).getParent();
PsiAnnotation anno = (PsiAnnotation)parameterList.getParent();
boolean showClasses = psiElement().afterLeaf("(").accepts(insertedElement);
PsiClass annoClass = null;
final PsiJavaCodeReferenceElement referenceElement = anno.getNameReferenceElement();
if (referenceElement != null) {
final PsiElement element = referenceElement.resolve();
if (element instanceof PsiClass) {
annoClass = (PsiClass)element;
if (annoClass.findMethodsByName("value", false).length == 0) {
showClasses = false;
}
}
}
if (showClasses && insertedElement.getParent() instanceof PsiReferenceExpression) {
final Set<LookupElement> set = JavaCompletionUtil.processJavaReference(
insertedElement, (PsiJavaReference)insertedElement.getParent(), new ElementExtractorFilter(createAnnotationFilter(insertedElement)), JavaCompletionProcessor.Options.DEFAULT_OPTIONS, result.getPrefixMatcher(), parameters);
for (final LookupElement element : set) {
result.addElement(element);
}
addAllClasses(parameters, result, new InheritorsHolder(insertedElement, result));
}
if (annoClass != null) {
final PsiNameValuePair[] existingPairs = parameterList.getAttributes();
methods: for (PsiMethod method : annoClass.getMethods()) {
if (!(method instanceof PsiAnnotationMethod)) continue;
final String attrName = method.getName();
for (PsiNameValuePair existingAttr : existingPairs) {
if (PsiTreeUtil.isAncestor(existingAttr, insertedElement, false)) break;
if (Comparing.equal(existingAttr.getName(), attrName) ||
PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(attrName) && existingAttr.getName() == null) continue methods;
}
LookupElementBuilder element = LookupElementBuilder.createWithIcon(method).withInsertHandler(new InsertHandler<LookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
final Editor editor = context.getEditor();
TailType.EQ.processTail(editor, editor.getCaretModel().getOffset());
context.setAddCompletionChar(false);
context.commitDocument();
PsiAnnotationParameterList paramList =
PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiAnnotationParameterList.class, false);
if (paramList != null && paramList.getAttributes().length > 0 && paramList.getAttributes()[0].getName() == null) {
int valueOffset = paramList.getAttributes()[0].getTextRange().getStartOffset();
context.getDocument().insertString(valueOffset, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
TailType.EQ.processTail(editor, valueOffset + PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.length());
}
}
});
PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)method).getDefaultValue();
if (defaultValue != null) {
element = element.withTailText(" default " + defaultValue.getText(), true);
}
result.addElement(element);
}
}
}
@Override
public String advertise(@NotNull final CompletionParameters parameters) {
if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null;
if (parameters.getCompletionType() == CompletionType.BASIC && parameters.getInvocationCount() > 0) {
PsiElement position = parameters.getPosition();
if (psiElement().withParent(psiReferenceExpression().withFirstChild(psiReferenceExpression().referencing(psiClass()))).accepts(position)) {
if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.GLOBAL_MEMBER_NAME)) {
final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION);
if (StringUtil.isNotEmpty(shortcut)) {
return "Pressing " + shortcut + " twice without a class qualifier would show all accessible static methods";
}
}
}
}
if (parameters.getCompletionType() != CompletionType.SMART && shouldSuggestSmartCompletion(parameters.getPosition())) {
if (CompletionUtil.shouldShowFeature(parameters, CodeCompletionFeatures.EDITING_COMPLETION_SMARTTYPE_GENERAL)) {
final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
if (StringUtil.isNotEmpty(shortcut)) {
return CompletionBundle.message("completion.smart.hint", shortcut);
}
}
}
if (parameters.getCompletionType() == CompletionType.SMART && parameters.getInvocationCount() == 1) {
final PsiType[] psiTypes = ExpectedTypesGetter.getExpectedTypes(parameters.getPosition(), true);
if (psiTypes.length > 0) {
if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_TOAR)) {
final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
if (StringUtil.isNotEmpty(shortcut)) {
for (final PsiType psiType : psiTypes) {
final PsiType type = PsiUtil.extractIterableTypeParameter(psiType, false);
if (type != null) {
return CompletionBundle.message("completion.smart.aslist.hint", shortcut, type.getPresentableText());
}
}
}
}
if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_ASLIST)) {
final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
if (StringUtil.isNotEmpty(shortcut)) {
for (final PsiType psiType : psiTypes) {
if (psiType instanceof PsiArrayType) {
final PsiType componentType = ((PsiArrayType)psiType).getComponentType();
if (!(componentType instanceof PsiPrimitiveType)) {
return CompletionBundle.message("completion.smart.toar.hint", shortcut, componentType.getPresentableText());
}
}
}
}
}
if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_CHAIN)) {
final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
if (StringUtil.isNotEmpty(shortcut)) {
return CompletionBundle.message("completion.smart.chain.hint", shortcut);
}
}
}
}
return null;
}
@Override
public String handleEmptyLookup(@NotNull final CompletionParameters parameters, final Editor editor) {
if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null;
final String ad = advertise(parameters);
final String suffix = ad == null ? "" : "; " + StringUtil.decapitalize(ad);
if (parameters.getCompletionType() == CompletionType.SMART) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
final Project project = parameters.getPosition().getProject();
final PsiFile file = parameters.getOriginalFile();
PsiExpression expression = PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
if (expression != null && expression.getParent() instanceof PsiExpressionList) {
int lbraceOffset = expression.getParent().getTextRange().getStartOffset();
ShowParameterInfoHandler.invoke(project, editor, file, lbraceOffset, null);
}
if (expression instanceof PsiLiteralExpression) {
return LangBundle.message("completion.no.suggestions") + suffix;
}
if (expression instanceof PsiInstanceOfExpression) {
final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression)expression;
if (PsiTreeUtil.isAncestor(instanceOfExpression.getCheckType(), parameters.getPosition(), false)) {
return LangBundle.message("completion.no.suggestions") + suffix;
}
}
}
final Set<PsiType> expectedTypes = JavaCompletionUtil.getExpectedTypes(parameters);
if (expectedTypes != null) {
PsiType type = expectedTypes.size() == 1 ? expectedTypes.iterator().next() : null;
if (type != null) {
final PsiType deepComponentType = type.getDeepComponentType();
if (deepComponentType instanceof PsiClassType) {
if (((PsiClassType)deepComponentType).resolve() != null) {
return CompletionBundle.message("completion.no.suggestions.of.type", type.getPresentableText()) + suffix;
}
return CompletionBundle.message("completion.unknown.type", type.getPresentableText()) + suffix;
}
if (!PsiType.NULL.equals(type)) {
return CompletionBundle.message("completion.no.suggestions.of.type", type.getPresentableText()) + suffix;
}
}
}
}
return LangBundle.message("completion.no.suggestions") + suffix;
}
@Override
public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) {
return typeChar == ':' && JavaTokenType.COLON == position.getNode().getElementType();
}
private static boolean shouldSuggestSmartCompletion(final PsiElement element) {
if (shouldSuggestClassNameCompletion(element)) return false;
final PsiElement parent = element.getParent();
if (parent instanceof PsiReferenceExpression && ((PsiReferenceExpression)parent).getQualifier() != null) return false;
if (parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiReferenceExpression) return true;
return ExpectedTypesGetter.getExpectedTypes(element, false).length > 0;
}
private static boolean shouldSuggestClassNameCompletion(final PsiElement element) {
if (element == null) return false;
final PsiElement parent = element.getParent();
if (parent == null) return false;
return parent.getParent() instanceof PsiTypeElement || parent.getParent() instanceof PsiExpressionStatement || parent.getParent() instanceof PsiReferenceList;
}
@Override
public void beforeCompletion(@NotNull final CompletionInitializationContext context) {
final PsiFile file = context.getFile();
if (file instanceof PsiJavaFile) {
if (context.getInvocationCount() > 0) {
autoImport(file, context.getStartOffset() - 1, context.getEditor());
PsiElement leaf = file.findElementAt(context.getStartOffset() - 1);
if (leaf != null) leaf = PsiTreeUtil.prevVisibleLeaf(leaf);
PsiVariable variable = PsiTreeUtil.getParentOfType(leaf, PsiVariable.class);
if (variable != null) {
PsiTypeElement typeElement = variable.getTypeElement();
if (typeElement != null) {
PsiType type = typeElement.getType();
if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == null) {
autoImportReference(file, context.getEditor(), typeElement.getInnermostComponentReferenceElement());
}
}
}
}
JavaCompletionUtil.initOffsets(file, context.getOffsetMap());
if (context.getCompletionType() == CompletionType.BASIC) {
if (semicolonNeeded(context.getEditor(), file, context.getStartOffset())) {
context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";");
return;
}
final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false);
if (ref != null && !(ref instanceof PsiReferenceExpression)) {
if (ref.getParent() instanceof PsiTypeElement) {
context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";");
}
if (JavaSmartCompletionContributor.AFTER_NEW.accepts(ref)) {
final PsiReferenceParameterList paramList = ref.getParameterList();
if (paramList != null && paramList.getTextLength() > 0) {
context.getOffsetMap().addOffset(ConstructorInsertHandler.PARAM_LIST_START, paramList.getTextRange().getStartOffset());
context.getOffsetMap().addOffset(ConstructorInsertHandler.PARAM_LIST_END, paramList.getTextRange().getEndOffset());
}
}
return;
}
final PsiElement element = file.findElementAt(context.getStartOffset());
if (psiElement().inside(PsiAnnotation.class).accepts(element)) {
return;
}
context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER_TRIMMED);
}
}
}
public static boolean semicolonNeeded(final Editor editor, PsiFile file, final int startOffset) {
final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, startOffset, PsiJavaCodeReferenceElement.class, false);
if (ref != null && !(ref instanceof PsiReferenceExpression)) {
if (ref.getParent() instanceof PsiTypeElement) {
return true;
}
}
HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(startOffset);
if (iterator.atEnd()) return false;
if (iterator.getTokenType() == JavaTokenType.IDENTIFIER) {
iterator.advance();
}
while (!iterator.atEnd() && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(iterator.getTokenType())) {
iterator.advance();
}
if (!iterator.atEnd() && (iterator.getTokenType() == JavaTokenType.LPARENTH)) {
return true;
}
if (!iterator.atEnd()
&& (iterator.getTokenType() == JavaTokenType.COLON)
&& null == PsiTreeUtil.findElementOfClassAtOffset(file, startOffset, PsiConditionalExpression.class, false)) {
return true;
}
while (!iterator.atEnd() && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(iterator.getTokenType())) {
iterator.advance();
}
if (iterator.atEnd() || iterator.getTokenType() != JavaTokenType.IDENTIFIER) return false;
iterator.advance();
while (!iterator.atEnd() && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(iterator.getTokenType())) {
iterator.advance();
}
if (iterator.atEnd()) return false;
return iterator.getTokenType() == JavaTokenType.EQ || iterator.getTokenType() == JavaTokenType.LPARENTH;
}
private static void autoImport(@NotNull final PsiFile file, int offset, @NotNull final Editor editor) {
final CharSequence text = editor.getDocument().getCharsSequence();
while (offset > 0 && Character.isJavaIdentifierPart(text.charAt(offset))) offset--;
if (offset <= 0) return;
while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
if (offset <= 0 || text.charAt(offset) != '.') return;
offset--;
while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
if (offset <= 0) return;
autoImportReference(file, editor, extractReference(PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiExpression.class, false)));
}
private static void autoImportReference(@NotNull PsiFile file, @NotNull Editor editor, @Nullable PsiJavaCodeReferenceElement element) {
if (element == null) return;
while (true) {
final PsiJavaCodeReferenceElement qualifier = extractReference(element.getQualifier());
if (qualifier == null) break;
element = qualifier;
}
if (!(element.getParent() instanceof PsiMethodCallExpression) && element.multiResolve(true).length == 0) {
new ImportClassFix(element).doFix(editor, false, false);
PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument());
}
}
@Nullable
private static PsiJavaCodeReferenceElement extractReference(@Nullable PsiElement expression) {
if (expression instanceof PsiJavaCodeReferenceElement) {
return (PsiJavaCodeReferenceElement)expression;
}
if (expression instanceof PsiMethodCallExpression) {
return ((PsiMethodCallExpression)expression).getMethodExpression();
}
return null;
}
static void processLabelReference(CompletionResultSet result, PsiLabelReference ref) {
for (String s : ref.getVariants()) {
result.addElement(TailTypeDecorator.withTail(LookupElementBuilder.create(s), TailType.SEMICOLON));
}
}
}