From a2c362078100447e74f6e9ca609187156440ca0a Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 16 Jul 2012 18:36:03 +0200 Subject: [PATCH] IDEA-88844 Complete enum members in annotations when type is obvious --- .../completion/JavaCompletionContributor.java | 1445 +++++++++-------- .../completion/JavaCompletionData.java | 1416 ++++++++-------- .../SuggestExpectedTypeMembersInAnno.java | 4 + ...uggestExpectedTypeMembersInAnno_after.java | 4 + .../completion/NormalCompletionTest.groovy | 1 + 5 files changed, 1442 insertions(+), 1428 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index ec274c96c7e9..35f3c64af89b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -1,722 +1,723 @@ -/* - * Copyright 2000-2012 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.CodeInsightSettings; -import com.intellij.codeInsight.TailType; -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.PsiNameValuePairPattern; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.*; -import com.intellij.psi.filters.*; -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.filters.types.AssignableFromFilter; -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.impl.source.tree.JavaElementType; -import com.intellij.psi.scope.ElementClassFilter; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.util.Consumer; -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 ourCompletionData; - - static { - ourCompletionData = new LinkedHashMap(); - ourCompletionData.put(LanguageLevel.JDK_1_8, new Java18CompletionData()); - ourCompletionData.put(LanguageLevel.JDK_1_3, new Java15CompletionData()); - ourCompletionData.put(LanguageLevel.JDK_1_3, new JavaCompletionData()); - } - - private static JavaCompletionData getCompletionData(LanguageLevel level) { - final Set> entries = ourCompletionData.entrySet(); - for (Map.Entry 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 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("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 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 IMPORT_REFERENCE = - psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class).withParent(PsiImportStatementBase.class)); - - static final ElementPattern IN_CATCH_TYPE = - psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class). - withParent(psiElement(PsiTypeElement.class). - withParent(or(psiElement(PsiCatchSection.class), - psiElement(PsiVariable.class).withParent(PsiCatchSection.class))))); - static final ElementPattern IN_MULTI_CATCH_TYPE = - or(psiElement().afterLeaf(psiElement().withText("|").withParent(PsiTypeElement.class).withSuperParent(2, PsiCatchSection.class)), - psiElement().afterLeaf(psiElement().withText("|").withParent(PsiTypeElement.class).withSuperParent(2, PsiParameter.class).withSuperParent(3, PsiCatchSection.class))); - static final ElementPattern INSIDE_METHOD_THROWS_CLAUSE = - psiElement().afterLeaf(PsiKeyword.THROWS, ",").inside(psiElement(JavaElementType.THROWS_LIST)); - static final ElementPattern IN_RESOURCE_TYPE = - psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class). - withParent(psiElement(PsiTypeElement.class). - withParent(or(psiElement(PsiResourceVariable.class), psiElement(PsiResourceList.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); - if (containingClass != null && psiElement().afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS, ",", "&").accepts(position)) { - return new AndFilter(ElementClassFilter.CLASS, new NotFilter(new AssignableFromContextFilter())); - } - - if (JavaCompletionData.DECLARATION_START.accepts(position) || - JavaCompletionData.isInsideParameterList(position)) { - return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER); - } - - if (INSIDE_METHOD_THROWS_CLAUSE.accepts(position)) { - return new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE); - } - - 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.INSTANCEOF_PLACE.isAcceptable(position, position) || - JavaCompletionData.isAfterPrimitiveOrArrayType(position)) { - return null; - } - - if (JavaCompletionData.START_FOR.accepts(position)) { - return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.VARIABLE); - } - - if (IN_CATCH_TYPE.accepts(position) || IN_MULTI_CATCH_TYPE.accepts(position)) { - return new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE); - } - - if (IN_RESOURCE_TYPE.accepts(position)) { - return new AssignableFromFilter(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE); - } - - if (JavaSmartCompletionContributor.AFTER_THROW_NEW.accepts(position)) { - return new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE); - } - - 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 new OrFilter(new ClassFilter(PsiAnnotationMethod.class), - ElementClassFilter.CLASS, - ElementClassFilter.PACKAGE_FILTER, - new AndFilter(new ClassFilter(PsiField.class), new ModifierFilter(PsiModifier.STATIC, PsiModifier.FINAL))); - } - - 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; - } - - public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet _result) { - if (parameters.getCompletionType() != CompletionType.BASIC) { - return; - } - - final PsiElement position = parameters.getPosition(); - if (!position.getContainingFile().getLanguage().isKindOf(JavaLanguage.INSTANCE)) { - return; - } - - if (AFTER_NUMBER_LITERAL.accepts(position)) { - _result.stopHere(); - return; - } - - final CompletionResultSet result = JavaCompletionSorting.addJavaSorting(parameters, _result); - - if (ANNOTATION_ATTRIBUTE_NAME.accepts(position) && !JavaCompletionData.isAfterPrimitiveOrArrayType(position)) { - 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("*")); - } - - Set usedWords = addReferenceVariants(parameters, result, inheritors); - - addKeywords(parameters, result); - - if (psiElement().inside(PsiLiteralExpression.class).accepts(position)) { - PsiReference reference = position.getContainingFile().findReferenceAt(parameters.getOffset()); - if (reference == null || reference.isSoft()) { - WordCompletionContributor.addWordCompletionVariants(result, parameters, usedWords); - } - } - - 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, new Consumer() { - @Override - public void consume(LookupElement element) { - result.addElement(element); - } - }); - } - result.stopHere(); - } - - public static void addAllClasses(CompletionParameters parameters, - final CompletionResultSet result, - final InheritorsHolder inheritors) { - if (!isClassNamePossible(parameters.getPosition()) && parameters.getInvocationCount() <= 1 || - !mayStartClassName(result)) { - return; - } - - if (parameters.getInvocationCount() >= 2) { - JavaClassNameCompletionContributor.addAllClasses(parameters, parameters.getInvocationCount() <= 2, result.getPrefixMatcher(), new Consumer() { - @Override - public void consume(LookupElement element) { - if (!inheritors.alreadyProcessed(element)) { - result.addElement(element); - } - } - }); - } else { - advertiseSecondCompletion(parameters.getPosition().getProject()); - } - } - - public static void advertiseSecondCompletion(Project project) { - if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.SECOND_BASIC_COMPLETION, project)) { - CompletionService.getCompletionService().setAdvertisementText("Press " + getActionShortcut(IdeActions.ACTION_CODE_COMPLETION) + " to see non-imported classes"); - } - } - - private static Set addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, final InheritorsHolder inheritors) { - final Set usedWords = new HashSet(); - final PsiElement position = parameters.getPosition(); - final boolean checkAccess = 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() { - 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(); - for (LookupElement element : JavaCompletionUtil.processJavaReference(position, - (PsiJavaReference)reference, - new ElementExtractorFilter(filter), - checkAccess, checkAccess, - 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(); - 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() { - @Override - public boolean value(PsiClass psiClass) { - return !inheritors.alreadyProcessed(psiClass) && JavaCompletionUtil.isSourceLevelAccessible(position, psiClass, pkgContext); - } - })) { - usedWords.add(item.getLookupString()); - result.addElement(item); - } - - } - else { - 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 lookupSet = new LinkedHashSet(); - final Set keywordVariants = new HashSet(); - 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); - } - } - - public static boolean isClassNamePossible(final PsiElement position) { - final PsiElement parent = position.getParent(); - if (!(parent instanceof PsiJavaCodeReferenceElement)) return false; - if (((PsiJavaCodeReferenceElement)parent).getQualifier() != null) return false; - - if (parent instanceof PsiJavaCodeReferenceElementImpl && - ((PsiJavaCodeReferenceElementImpl)parent).getKind() == PsiJavaCodeReferenceElementImpl.PACKAGE_NAME_KIND) { - return false; - } - - PsiElement grand = parent.getParent(); - if (grand instanceof PsiSwitchLabelStatement) { - return false; - } - - if (psiElement().inside(PsiImportStatement.class).accepts(parent)) { - return false; - } - - 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) { - String prefix = result.getPrefixMatcher().getPrefix(); - if (StringUtil.isEmpty(prefix)) { - return false; - } - - return StringUtil.isCapitalized(prefix) || - CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE == CodeInsightSettings.NONE; - } - - private static void completeAnnotationAttributeName(CompletionResultSet result, PsiElement insertedElement, - CompletionParameters parameters) { - PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class); - PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)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 set = JavaCompletionUtil.processJavaReference( - insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, false, result.getPrefixMatcher(), parameters); - for (final LookupElement element : set) { - result.addElement(element); - } - } - - if (annoClass != null) { - final PsiNameValuePair[] existingPairs = parameterList.getAttributes(); - - methods: for (PsiMethod method : annoClass.getMethods()) { - final String attrName = method.getName(); - for (PsiNameValuePair apair : existingPairs) { - if (Comparing.equal(apair.getName(), attrName)) continue methods; - } - result.addElement(new LookupItem(method, attrName).setInsertHandler(new InsertHandler() { - public void handleInsert(InsertionContext context, LookupElement item) { - final Editor editor = context.getEditor(); - TailType.EQ.processTail(editor, editor.getCaretModel().getOffset()); - context.setAddCompletionChar(false); - } - })); - } - } - } - - 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 (shortcut != null) { - 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 (shortcut != null) { - 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 (shortcut != null) { - 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 (shortcut != null) { - 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 (shortcut != null) { - return CompletionBundle.message("completion.smart.chain.hint", shortcut); - } - } - } - } - return null; - } - - 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(); - new 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 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; - } - - 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; - } - - public void beforeCompletion(@NotNull final CompletionInitializationContext context) { - final PsiFile file = context.getFile(); - - if (file instanceof PsiJavaFile) { - JavaCompletionUtil.initOffsets(file, context.getProject(), context.getOffsetMap()); - - autoImport(file, context.getStartOffset() - 1, context.getEditor()); - - if (context.getCompletionType() == CompletionType.BASIC) { - if (semicolonNeeded(context)) { - context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";"); - return; - } - - final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false); - if (ref != null && !(ref instanceof PsiReferenceExpression)) { - 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); - } - } - } - - private static boolean semicolonNeeded(CompletionInitializationContext context) { - HighlighterIterator iterator = ((EditorEx) context.getEditor()).getHighlighter().createIterator(context.getStartOffset()); - 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 || iterator.getTokenType() == JavaTokenType.COLON)) { - 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(final PsiFile file, int offset, 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; - - PsiJavaCodeReferenceElement element = extractReference(PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiExpression.class, false)); - 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); - } - } - - @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)); - } - } -} +/* + * Copyright 2000-2012 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.CodeInsightSettings; +import com.intellij.codeInsight.TailType; +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.PsiNameValuePairPattern; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.filters.*; +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.filters.types.AssignableFromFilter; +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.impl.source.tree.JavaElementType; +import com.intellij.psi.scope.ElementClassFilter; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.Consumer; +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 ourCompletionData; + + static { + ourCompletionData = new LinkedHashMap(); + ourCompletionData.put(LanguageLevel.JDK_1_8, new Java18CompletionData()); + ourCompletionData.put(LanguageLevel.JDK_1_3, new Java15CompletionData()); + ourCompletionData.put(LanguageLevel.JDK_1_3, new JavaCompletionData()); + } + + private static JavaCompletionData getCompletionData(LanguageLevel level) { + final Set> entries = ourCompletionData.entrySet(); + for (Map.Entry 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 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("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 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 IMPORT_REFERENCE = + psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class).withParent(PsiImportStatementBase.class)); + + static final ElementPattern IN_CATCH_TYPE = + psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class). + withParent(psiElement(PsiTypeElement.class). + withParent(or(psiElement(PsiCatchSection.class), + psiElement(PsiVariable.class).withParent(PsiCatchSection.class))))); + static final ElementPattern IN_MULTI_CATCH_TYPE = + or(psiElement().afterLeaf(psiElement().withText("|").withParent(PsiTypeElement.class).withSuperParent(2, PsiCatchSection.class)), + psiElement().afterLeaf(psiElement().withText("|").withParent(PsiTypeElement.class).withSuperParent(2, PsiParameter.class).withSuperParent(3, PsiCatchSection.class))); + static final ElementPattern INSIDE_METHOD_THROWS_CLAUSE = + psiElement().afterLeaf(PsiKeyword.THROWS, ",").inside(psiElement(JavaElementType.THROWS_LIST)); + static final ElementPattern IN_RESOURCE_TYPE = + psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class). + withParent(psiElement(PsiTypeElement.class). + withParent(or(psiElement(PsiResourceVariable.class), psiElement(PsiResourceList.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); + if (containingClass != null && psiElement().afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS, ",", "&").accepts(position)) { + return new AndFilter(ElementClassFilter.CLASS, new NotFilter(new AssignableFromContextFilter())); + } + + if (JavaCompletionData.DECLARATION_START.accepts(position) || + JavaCompletionData.isInsideParameterList(position)) { + return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER); + } + + if (INSIDE_METHOD_THROWS_CLAUSE.accepts(position)) { + return new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE); + } + + 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.INSTANCEOF_PLACE.isAcceptable(position, position) || + JavaCompletionData.isAfterPrimitiveOrArrayType(position)) { + return null; + } + + if (JavaCompletionData.START_FOR.accepts(position)) { + return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.VARIABLE); + } + + if (IN_CATCH_TYPE.accepts(position) || IN_MULTI_CATCH_TYPE.accepts(position)) { + return new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE); + } + + if (IN_RESOURCE_TYPE.accepts(position)) { + return new AssignableFromFilter(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE); + } + + if (JavaSmartCompletionContributor.AFTER_THROW_NEW.accepts(position)) { + return new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE); + } + + 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 new OrFilter(new ClassFilter(PsiAnnotationMethod.class), + ElementClassFilter.CLASS, + ElementClassFilter.PACKAGE_FILTER, + new AndFilter(new ClassFilter(PsiField.class), new ModifierFilter(PsiModifier.STATIC, PsiModifier.FINAL))); + } + + 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; + } + + public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet _result) { + if (parameters.getCompletionType() != CompletionType.BASIC) { + return; + } + + final PsiElement position = parameters.getPosition(); + if (!position.getContainingFile().getLanguage().isKindOf(JavaLanguage.INSTANCE)) { + return; + } + + if (AFTER_NUMBER_LITERAL.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, position); + 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("*")); + } + + Set usedWords = addReferenceVariants(parameters, result, inheritors); + + addKeywords(parameters, result); + + if (psiElement().inside(PsiLiteralExpression.class).accepts(position)) { + PsiReference reference = position.getContainingFile().findReferenceAt(parameters.getOffset()); + if (reference == null || reference.isSoft()) { + WordCompletionContributor.addWordCompletionVariants(result, parameters, usedWords); + } + } + + 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, new Consumer() { + @Override + public void consume(LookupElement element) { + result.addElement(element); + } + }); + } + result.stopHere(); + } + + public static void addAllClasses(CompletionParameters parameters, + final CompletionResultSet result, + final InheritorsHolder inheritors) { + if (!isClassNamePossible(parameters.getPosition()) && parameters.getInvocationCount() <= 1 || + !mayStartClassName(result)) { + return; + } + + if (parameters.getInvocationCount() >= 2) { + JavaClassNameCompletionContributor.addAllClasses(parameters, parameters.getInvocationCount() <= 2, result.getPrefixMatcher(), new Consumer() { + @Override + public void consume(LookupElement element) { + if (!inheritors.alreadyProcessed(element)) { + result.addElement(element); + } + } + }); + } else { + advertiseSecondCompletion(parameters.getPosition().getProject()); + } + } + + public static void advertiseSecondCompletion(Project project) { + if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.SECOND_BASIC_COMPLETION, project)) { + CompletionService.getCompletionService().setAdvertisementText("Press " + getActionShortcut(IdeActions.ACTION_CODE_COMPLETION) + " to see non-imported classes"); + } + } + + private static Set addReferenceVariants(final CompletionParameters parameters, CompletionResultSet result, final InheritorsHolder inheritors) { + final Set usedWords = new HashSet(); + final PsiElement position = parameters.getPosition(); + final boolean checkAccess = 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() { + 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(); + for (LookupElement element : JavaCompletionUtil.processJavaReference(position, + (PsiJavaReference)reference, + new ElementExtractorFilter(filter), + checkAccess, checkAccess, + 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(); + 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() { + @Override + public boolean value(PsiClass psiClass) { + return !inheritors.alreadyProcessed(psiClass) && JavaCompletionUtil.isSourceLevelAccessible(position, psiClass, pkgContext); + } + })) { + usedWords.add(item.getLookupString()); + result.addElement(item); + } + + } + else { + 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 lookupSet = new LinkedHashSet(); + final Set keywordVariants = new HashSet(); + 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); + } + } + + public static boolean isClassNamePossible(final PsiElement position) { + final PsiElement parent = position.getParent(); + if (!(parent instanceof PsiJavaCodeReferenceElement)) return false; + if (((PsiJavaCodeReferenceElement)parent).getQualifier() != null) return false; + + if (parent instanceof PsiJavaCodeReferenceElementImpl && + ((PsiJavaCodeReferenceElementImpl)parent).getKind() == PsiJavaCodeReferenceElementImpl.PACKAGE_NAME_KIND) { + return false; + } + + PsiElement grand = parent.getParent(); + if (grand instanceof PsiSwitchLabelStatement) { + return false; + } + + if (psiElement().inside(PsiImportStatement.class).accepts(parent)) { + return false; + } + + 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) { + String prefix = result.getPrefixMatcher().getPrefix(); + if (StringUtil.isEmpty(prefix)) { + return false; + } + + return StringUtil.isCapitalized(prefix) || + CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE == CodeInsightSettings.NONE; + } + + private static void completeAnnotationAttributeName(CompletionResultSet result, PsiElement insertedElement, + CompletionParameters parameters) { + PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class); + PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)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 set = JavaCompletionUtil.processJavaReference( + insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, false, result.getPrefixMatcher(), parameters); + for (final LookupElement element : set) { + result.addElement(element); + } + } + + if (annoClass != null) { + final PsiNameValuePair[] existingPairs = parameterList.getAttributes(); + + methods: for (PsiMethod method : annoClass.getMethods()) { + final String attrName = method.getName(); + for (PsiNameValuePair apair : existingPairs) { + if (Comparing.equal(apair.getName(), attrName)) continue methods; + } + result.addElement(new LookupItem(method, attrName).setInsertHandler(new InsertHandler() { + public void handleInsert(InsertionContext context, LookupElement item) { + final Editor editor = context.getEditor(); + TailType.EQ.processTail(editor, editor.getCaretModel().getOffset()); + context.setAddCompletionChar(false); + } + })); + } + } + } + + 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 (shortcut != null) { + 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 (shortcut != null) { + 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 (shortcut != null) { + 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 (shortcut != null) { + 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 (shortcut != null) { + return CompletionBundle.message("completion.smart.chain.hint", shortcut); + } + } + } + } + return null; + } + + 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(); + new 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 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; + } + + 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; + } + + public void beforeCompletion(@NotNull final CompletionInitializationContext context) { + final PsiFile file = context.getFile(); + + if (file instanceof PsiJavaFile) { + JavaCompletionUtil.initOffsets(file, context.getProject(), context.getOffsetMap()); + + autoImport(file, context.getStartOffset() - 1, context.getEditor()); + + if (context.getCompletionType() == CompletionType.BASIC) { + if (semicolonNeeded(context)) { + context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";"); + return; + } + + final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false); + if (ref != null && !(ref instanceof PsiReferenceExpression)) { + 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); + } + } + } + + private static boolean semicolonNeeded(CompletionInitializationContext context) { + HighlighterIterator iterator = ((EditorEx) context.getEditor()).getHighlighter().createIterator(context.getStartOffset()); + 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 || iterator.getTokenType() == JavaTokenType.COLON)) { + 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(final PsiFile file, int offset, 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; + + PsiJavaCodeReferenceElement element = extractReference(PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiExpression.class, false)); + 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); + } + } + + @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)); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java index 606662d4b896..1167d982f121 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java @@ -1,706 +1,710 @@ -/* - * Copyright 2000-2012 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.TailType; -import com.intellij.codeInsight.TailTypes; -import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.codeInsight.lookup.TailTypeDecorator; -import com.intellij.patterns.ElementPattern; -import com.intellij.patterns.PsiElementPattern; -import com.intellij.patterns.PsiJavaElementPattern; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.*; -import com.intellij.psi.filters.*; -import com.intellij.psi.filters.classes.EnumOrAnnotationTypeFilter; -import com.intellij.psi.filters.classes.InterfaceFilter; -import com.intellij.psi.filters.element.ReferenceOnFilter; -import com.intellij.psi.filters.getters.JavaMembersGetter; -import com.intellij.psi.filters.position.*; -import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement; -import com.intellij.psi.jsp.JspElementType; -import com.intellij.psi.templateLanguages.OuterLanguageElement; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.util.Consumer; -import com.intellij.util.ProcessingContext; -import org.jetbrains.annotations.NonNls; - -import static com.intellij.patterns.PsiJavaPatterns.*; -import static com.intellij.patterns.StandardPatterns.not; - -public class JavaCompletionData extends JavaAwareCompletionData { - private static final @NonNls String[] BLOCK_FINALIZERS = {"{", "}", ";", ":", "else"}; - - private static final PsiElementPattern AFTER_DOT = psiElement().afterLeaf("."); - - public static final LeftNeighbour INSTANCEOF_PLACE = new LeftNeighbour(new OrFilter( - new ReferenceOnFilter(new ClassFilter(PsiVariable.class)), - new TextFilter(PsiKeyword.THIS), - new AndFilter(new TextFilter(")"), new ParentElementFilter(new AndFilter( - new ClassFilter(PsiTypeCastExpression.class, false), - new OrFilter( - new ParentElementFilter(new ClassFilter(PsiExpression.class)), - new ClassFilter(PsiExpression.class))))), - new AndFilter(new TextFilter("]"), new ParentElementFilter(new ClassFilter(PsiArrayAccessExpression.class))))); - - public static final PsiJavaElementPattern.Capture VARIABLE_AFTER_FINAL = - psiElement().afterLeaf(PsiKeyword.FINAL).inside(PsiDeclarationStatement.class); - - public static final LeftNeighbour AFTER_TRY_BLOCK = new LeftNeighbour(new AndFilter( - new TextFilter("}"), - new ParentElementFilter(new AndFilter( - new LeftNeighbour(new TextFilter(PsiKeyword.TRY)), - new ParentElementFilter(new ClassFilter(PsiTryStatement.class))) - ))); - - private static final PsiJavaElementPattern.Capture INSIDE_PARAMETER_LIST = - psiElement().withParent( - psiElement(PsiJavaCodeReferenceElement.class).insideStarting( - psiElement().withTreeParent( - psiElement(PsiParameterList.class).andNot(psiElement(PsiAnnotationParameterList.class))))); - - public static boolean isInsideParameterList(PsiElement position) { - PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); - PsiModifierList modifierList = PsiTreeUtil.getParentOfType(prev, PsiModifierList.class); - if (modifierList != null) { - if (PsiTreeUtil.isAncestor(modifierList, position, false)) { - return false; - } - PsiElement parent = modifierList.getParent(); - return parent instanceof PsiParameterList || parent instanceof PsiParameter && parent.getParent() instanceof PsiParameterList; - } - return INSIDE_PARAMETER_LIST.accepts(position); - } - - - private static final AndFilter START_OF_CODE_FRAGMENT = new AndFilter( - new ScopeFilter(new AndFilter( - new ClassFilter(JavaCodeFragment.class), - new ClassFilter(PsiExpressionCodeFragment.class, false), - new ClassFilter(PsiJavaCodeReferenceCodeFragment.class, false), - new ClassFilter(PsiTypeCodeFragment.class, false) - )), - new StartElementFilter() - ); - - static final ElementFilter END_OF_BLOCK = new OrFilter( - new AndFilter( - new LeftNeighbour( - new OrFilter( - new AndFilter ( - new TextFilter(BLOCK_FINALIZERS), - new NotFilter ( - new SuperParentFilter(new ClassFilter(PsiAnnotation.class)) - ) - ), - new TextFilter("*/"), - new TokenTypeFilter(JspElementType.HOLDER_TEMPLATE_DATA), - new ClassFilter(OuterLanguageElement.class), - new AndFilter( - new TextFilter(")"), - new NotFilter( - new OrFilter( - new ParentElementFilter(new ClassFilter(PsiExpressionList.class)), - new ParentElementFilter(new ClassFilter(PsiParameterList.class)), - new ParentElementFilter(new ClassFilter(PsiTypeCastExpression.class)) - ) - ) - ))), - new NotFilter(new TextFilter(".")) - ), - START_OF_CODE_FRAGMENT - ); - - static final ElementPattern START_SWITCH = - psiElement().afterLeaf(psiElement().withText("{").withParents(PsiCodeBlock.class, PsiSwitchStatement.class)); - - private static final ElementPattern SUPER_OR_THIS_PATTERN = - and(JavaSmartCompletionContributor.INSIDE_EXPRESSION, - not(psiElement().afterLeaf(PsiKeyword.CASE)), - not(psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.THIS, PsiKeyword.SUPER))), - not(START_SWITCH)); - - public static final AndFilter CLASS_START = new AndFilter( - new OrFilter( - END_OF_BLOCK, - new PatternFilter(psiElement().afterLeaf( - or( - psiElement().withoutText(".").inside(psiElement(PsiModifierList.class)), - psiElement().isNull()))) - ), - new PatternFilter(not(psiElement().afterLeaf("@")))); - - private static final String[] PRIMITIVE_TYPES = new String[]{ - PsiKeyword.SHORT, PsiKeyword.BOOLEAN, - PsiKeyword.DOUBLE, PsiKeyword.LONG, - PsiKeyword.INT, PsiKeyword.FLOAT, - PsiKeyword.CHAR, PsiKeyword.BYTE - }; - - private static final ElementFilter CLASS_BODY = new OrFilter( - new AfterElementFilter(new TextFilter("{")), - new ScopeFilter(new ClassFilter(JspClassLevelDeclarationStatement.class))); - - public static final ElementPattern START_FOR = - psiElement().afterLeaf(psiElement().withText("(").afterLeaf("for")).withParents(PsiJavaCodeReferenceElement.class, - PsiExpressionStatement.class, PsiForStatement.class); - private static final PsiJavaElementPattern.Capture CLASS_REFERENCE = - psiElement().withParent(psiReferenceExpression().referencing(psiClass())); - - public static final ElementPattern EXPR_KEYWORDS = and( - psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent( - not( - or(psiElement(PsiTypeCastExpression.class), - psiElement(PsiSwitchLabelStatement.class), - psiElement(PsiExpressionStatement.class), - psiElement(PsiPrefixExpression.class) - ) - ) - )), - not(psiElement().afterLeaf(".")) - ); - - public JavaCompletionData(){ - declareCompletionSpaces(); - - initVariantsInFileScope(); - initVariantsInClassScope(); - initVariantsInMethodScope(); - - defineScopeEquivalence(PsiMethod.class, PsiClassInitializer.class); - defineScopeEquivalence(PsiMethod.class, JavaCodeFragment.class); - } - - public static final ElementPattern DECLARATION_START = psiElement().andNot(psiElement().afterLeaf("@", ".")). - andOr( - psiElement().and(new FilterPattern(CLASS_BODY)). - andOr( - new FilterPattern(END_OF_BLOCK), - psiElement().afterLeaf(or( - psiElement().inside(PsiModifierList.class), - psiElement().withElementType(JavaTokenType.GT).inside(PsiTypeParameterList.class) - ))), - psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiMember.class), - psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiClassLevelDeclarationStatement.class) - ); - - private void declareCompletionSpaces() { - declareFinalScope(PsiFile.class); - - { - // Class body - final CompletionVariant variant = new CompletionVariant(CLASS_BODY); - variant.includeScopeClass(PsiClass.class, true); - registerVariant(variant); - } - { - // Method body - final CompletionVariant variant = new CompletionVariant(new AndFilter(new InsideElementFilter(new ClassFilter(PsiCodeBlock.class)), - new NotFilter(new InsideElementFilter(new ClassFilter(JspClassLevelDeclarationStatement.class))))); - variant.includeScopeClass(PsiMethod.class, true); - variant.includeScopeClass(PsiClassInitializer.class, true); - registerVariant(variant); - } - - { - // Field initializer - final CompletionVariant variant = new CompletionVariant(new AfterElementFilter(new TextFilter("="))); - variant.includeScopeClass(PsiField.class, true); - registerVariant(variant); - } - - declareFinalScope(PsiLiteralExpression.class); - declareFinalScope(PsiComment.class); - } - - protected void initVariantsInFileScope(){ - { - final CompletionVariant variant = new CompletionVariant(PsiJavaFile.class, CLASS_START); - variant.includeScopeClass(PsiClass.class); - - variant.addCompletion(PsiKeyword.CLASS); - variant.addCompletion(PsiKeyword.INTERFACE); - - registerVariant(variant); - } - - } - - /** - * aClass == null for JspDeclaration scope - */ - protected void initVariantsInClassScope() { -// Completion for extends keyword -// position - { - final ElementFilter position = new AndFilter( - new NotFilter(CLASS_BODY), - new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))), - new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))), - new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))), - new NotFilter(new ScopeFilter(new EnumOrAnnotationTypeFilter())), - new LeftNeighbour(new OrFilter( - new ClassFilter(PsiIdentifier.class), - new TextFilter(">")))); -// completion - final CompletionVariant variant = new CompletionVariant(position); - variant.includeScopeClass(PsiClass.class, true); - variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD); - variant.excludeScopeClass(PsiAnonymousClass.class); - variant.excludeScopeClass(PsiTypeParameter.class); - - registerVariant(variant); - } -// Completion for implements keyword -// position - { - final ElementFilter position = new AndFilter( - new NotFilter(CLASS_BODY), - new NotFilter(new BeforeElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))), - new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))), - new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))), - new LeftNeighbour(new OrFilter( - new ClassFilter(PsiIdentifier.class), - new TextFilter(">"))), - new NotFilter(new ScopeFilter(new InterfaceFilter()))); -// completion - final CompletionVariant variant = new CompletionVariant(position); - variant.includeScopeClass(PsiClass.class, true); - variant.addCompletion(PsiKeyword.IMPLEMENTS, TailType.HUMBLE_SPACE_BEFORE_WORD); - variant.excludeScopeClass(PsiAnonymousClass.class); - - registerVariant(variant); - } - - - { - final CompletionVariant variant = new CompletionVariant(PsiElement.class, psiElement().afterLeaf( - psiElement(PsiIdentifier.class).afterLeaf( - psiElement().withText(string().oneOf(",", "<")).withParent(PsiTypeParameterList.class)))); - //variant.includeScopeClass(PsiClass.class, true); - variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD); - registerVariant(variant); - } - } - - private void initVariantsInMethodScope() { -// Completion for classes in method throws section -// position - { - final ElementFilter position = new LeftNeighbour(new AndFilter( - new TextFilter(")"), - new ParentElementFilter(new ClassFilter(PsiParameterList.class)))); - -// completion - CompletionVariant variant = new CompletionVariant(PsiMethod.class, position); - variant.includeScopeClass(PsiClass.class); // for throws on separate line - variant.addCompletion(PsiKeyword.THROWS); - - registerVariant(variant); - -//in annotation methods - variant = new CompletionVariant(PsiAnnotationMethod.class, position); - variant.addCompletion(PsiKeyword.DEFAULT); - registerVariant(variant); - } - - { -// instanceof keyword - final ElementFilter position = INSTANCEOF_PLACE; - final CompletionVariant variant = new CompletionVariant(position); - variant.includeScopeClass(PsiExpression.class, true); - variant.includeScopeClass(PsiMethod.class); - variant.addCompletion(PsiKeyword.INSTANCEOF); - - registerVariant(variant); - } - - { -// Keyword completion in returns !!!! - final CompletionVariant variant = new CompletionVariant(PsiMethod.class, new LeftNeighbour(new TextFilter(PsiKeyword.RETURN))); - variant.addCompletion(PsiKeyword.TRUE, TailType.NONE); - variant.addCompletion(PsiKeyword.FALSE, TailType.NONE); - registerVariant(variant); - } - - -// Catch/Finally completion - { - final ElementFilter position = AFTER_TRY_BLOCK; - - final CompletionVariant variant = new CompletionVariant(position); - variant.includeScopeClass(PsiCodeBlock.class, true); - variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH); - variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE); - registerVariant(variant); - } - -// Catch/Finally completion - { - final ElementFilter position = new LeftNeighbour(new AndFilter( - new TextFilter("}"), - new ParentElementFilter(new AndFilter( - new LeftNeighbour(new NotFilter(new TextFilter(PsiKeyword.TRY))), - new OrFilter( - new ParentElementFilter(new ClassFilter(PsiTryStatement.class)), - new ParentElementFilter(new ClassFilter(PsiCatchSection.class))) - )))); - - final CompletionVariant variant = new CompletionVariant(position); - variant.includeScopeClass(PsiCodeBlock.class, false); - variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH); - variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE); - registerVariant(variant); - } - -// Completion for else expression -// completion - { - final ElementFilter position = new LeftNeighbour( - new OrFilter( - new AndFilter(new TextFilter("}"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 3)), - new AndFilter(new TextFilter(";"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 2)) - )); - final CompletionVariant variant = new CompletionVariant(PsiMethod.class, position); - variant.addCompletion(PsiKeyword.ELSE); - - registerVariant(variant); - } - - } - - private static TailType getReturnTail(PsiElement position) { - PsiElement scope = position; - while(true){ - if (scope instanceof PsiFile || scope instanceof PsiClassInitializer){ - return TailType.NONE; - } - - if (scope instanceof PsiMethod){ - final PsiMethod method = (PsiMethod)scope; - if(method.isConstructor() || PsiType.VOID.equals(method.getReturnType())) { - return TailType.SEMICOLON; - } - - return TailType.HUMBLE_SPACE_BEFORE_WORD; - } - scope = scope.getParent(); - } - } - - private static void addStatementKeywords(CompletionResultSet variant, PsiElement position) { - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailType.createSimpleTailType('{'))); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FOR), TailTypes.FOR_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IF), TailTypes.IF_LPARENTH)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.TRY), TailType.createSimpleTailType('{'))); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.THROW), TailType.INSERT_SPACE)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH)); - - if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_4)) { - variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.ASSERT), TailType.INSERT_SPACE)); - } - - TailType returnTail = getReturnTail(position); - LookupElement ret = createKeyword(position, PsiKeyword.RETURN); - if (returnTail != TailType.NONE) { - ret = new OverrideableSpace(ret, returnTail); - } - variant.addElement(ret); - } - - @Override - public void fillCompletions(CompletionParameters parameters, final CompletionResultSet result) { - final PsiElement position = parameters.getPosition(); - if (PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null) { - return; - } - - PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class); - if (statement == null) { - statement = PsiTreeUtil.getParentOfType(position, PsiDeclarationStatement.class); - } - if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) { - if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).accepts(statement)) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); - } - } - - if (isStatementPosition(position)) { - if (PsiTreeUtil.getParentOfType(position, PsiSwitchStatement.class, false, PsiMember.class) != null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE)); - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON)); - if (START_SWITCH.accepts(position)) { - return; - } - } - - addBreakContinue(result, position); - addStatementKeywords(result, position); - } - - if (SUPER_OR_THIS_PATTERN.accepts(position)) { - if (!AFTER_DOT.accepts(position) || isInsideQualifierClass(position)) { - result.addElement(createKeyword(position, PsiKeyword.THIS)); - - final LookupItem superItem = (LookupItem)createKeyword(position, PsiKeyword.SUPER); - if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(position)) { - final PsiMethod method = PsiTreeUtil.getParentOfType(position, PsiMethod.class, false, PsiClass.class); - assert method != null; - final boolean hasParams = superConstructorHasParameters(method); - superItem.setInsertHandler(new ParenthesesInsertHandler() { - @Override - protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) { - return hasParams; - } - - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - super.handleInsert(context, item); - TailType.insertChar(context.getEditor(), context.getTailOffset(), ';'); - } - }); - } - - result.addElement(superItem); - } - } - - if (EXPR_KEYWORDS.accepts(position)) { - result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); - result.addElement(createKeyword(position, PsiKeyword.NULL)); - result.addElement(createKeyword(position, PsiKeyword.TRUE)); - result.addElement(createKeyword(position, PsiKeyword.FALSE)); - } - - if (PsiTreeUtil.prevVisibleLeaf(position) == null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD)); - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); - } - else if (END_OF_BLOCK.isAcceptable(position, position) && PsiTreeUtil.getParentOfType(position, PsiMember.class) == null) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); - } - - if (isInsideParameterList(position) && !psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) && !AFTER_DOT.accepts(position)) { - result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); - } - - if (CLASS_START.isAcceptable(position, position) && - PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) == null) { - for (String s : ModifierChooser.getKeywords(position)) { - result.addElement(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD)); - } - } - - addPrimitiveTypes(result, position); - - if (isAfterTypeDot(position)) { - result.addElement(createKeyword(position, PsiKeyword.CLASS)); - } - - addUnfinishedMethodTypeParameters(position, result); - - if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) && - !BasicExpressionCompletionContributor.AFTER_DOT.accepts(position) && - !(position.getParent() instanceof PsiLiteralExpression)) { - for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { - new JavaMembersGetter(info.getDefaultType(), position).addMembers(parameters, parameters.getInvocationCount() > 1, new Consumer() { - @Override - public void consume(LookupElement element) { - result.addElement(element); - } - }); - } - if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) { - new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result); - } - } - } - - private static void addUnfinishedMethodTypeParameters(PsiElement position, CompletionResultSet result) { - final ProcessingContext context = new ProcessingContext(); - if (psiElement().inside( - psiElement(PsiTypeElement.class).afterLeaf( - psiElement().withText(">").withParent( - psiElement(PsiTypeParameterList.class).withParent(PsiErrorElement.class).save("typeParameterList")))).accepts(position, context)) { - final PsiTypeParameterList list = (PsiTypeParameterList)context.get("typeParameterList"); - PsiElement current = list.getParent().getParent(); - if (current instanceof PsiField) { - current = current.getParent(); - } - if (current instanceof PsiClass) { - for (PsiTypeParameter typeParameter : list.getTypeParameters()) { - result.addElement(new JavaPsiClassReferenceElement(typeParameter)); - } - } - } - } - - static boolean isAfterPrimitiveOrArrayType(PsiElement element) { - return psiElement().withParent( - psiReferenceExpression().withFirstChild( - psiElement(PsiClassObjectAccessExpression.class).withLastChild( - not(psiElement().withText(PsiKeyword.CLASS))))).accepts(element); - } - - static boolean isAfterTypeDot(PsiElement position) { - if (isInsideParameterList(position) || position.getContainingFile() instanceof PsiJavaCodeReferenceCodeFragment) { - return false; - } - - return psiElement().afterLeaf(psiElement().withText(".").afterLeaf(CLASS_REFERENCE)).accepts(position) || - isAfterPrimitiveOrArrayType(position); - } - - private static void addPrimitiveTypes(CompletionResultSet result, PsiElement position) { - boolean inCast = psiElement() - .afterLeaf(psiElement().withText("(").withParent(psiElement(PsiParenthesizedExpression.class, PsiTypeCastExpression.class))) - .accepts(position); - - boolean typeFragment = position.getContainingFile() instanceof PsiTypeCodeFragment && PsiTreeUtil.prevVisibleLeaf(position) == null; - boolean declaration = DECLARATION_START.accepts(position); - if (START_FOR.accepts(position) || - isInsideParameterList(position) && !AFTER_DOT.accepts(position) || - VARIABLE_AFTER_FINAL.accepts(position) || - inCast || - declaration || - typeFragment || - isStatementPosition(position)) { - for (String primitiveType : PRIMITIVE_TYPES) { - LookupElement keyword = createKeyword(position, primitiveType); - result.addElement(inCast || typeFragment ? keyword : new OverrideableSpace(keyword, TailType.HUMBLE_SPACE_BEFORE_WORD)); - } - } - if (declaration) { - result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD)); - } else if (typeFragment && ((PsiTypeCodeFragment)position.getContainingFile()).isVoidValid()) { - result.addElement(createKeyword(position, PsiKeyword.VOID)); - } - } - - private static void addBreakContinue(CompletionResultSet result, PsiElement position) { - PsiLoopStatement loop = PsiTreeUtil.getParentOfType(position, PsiLoopStatement.class); - - LookupElement br = createKeyword(position, PsiKeyword.BREAK); - LookupElement cont = createKeyword(position, PsiKeyword.CONTINUE); - TailType tailType; - if (psiElement().insideSequence(true, psiElement(PsiLabeledStatement.class), - or(psiElement(PsiFile.class), psiElement(PsiMethod.class), - psiElement(PsiClassInitializer.class))).accepts(position)) { - tailType = TailType.HUMBLE_SPACE_BEFORE_WORD; - } - else { - tailType = TailType.SEMICOLON; - } - br = TailTypeDecorator.withTail(br, tailType); - cont = TailTypeDecorator.withTail(cont, tailType); - - if (loop != null && new InsideElementFilter(new ClassFilter(PsiStatement.class)).isAcceptable(position, loop)) { - result.addElement(br); - result.addElement(cont); - } - if (psiElement().inside(PsiSwitchStatement.class).accepts(position)) { - result.addElement(br); - } - } - - private static boolean isStatementPosition(PsiElement position) { - if (PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) { - return false; - } - - if (psiElement().withSuperParent(2, PsiConditionalExpression.class).andNot(psiElement().insideStarting(psiElement(PsiConditionalExpression.class))).accepts(position)) { - return false; - } - - if (END_OF_BLOCK.isAcceptable(position, position) && - PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) != null) { - return true; - } - - if (psiElement().withParents(PsiReferenceExpression.class, PsiExpressionStatement.class, PsiIfStatement.class).andNot( - psiElement().afterLeaf(".")).accepts(position)) { - PsiElement stmt = position.getParent().getParent(); - PsiIfStatement ifStatement = (PsiIfStatement)stmt.getParent(); - if (ifStatement.getElseBranch() == stmt || ifStatement.getThenBranch() == stmt) { - return true; - } - } - - return false; - } - - protected static LookupElement createKeyword(PsiElement position, String keyword) { - return BasicExpressionCompletionContributor.createKeywordLookupItem(position, keyword); - } - - private static boolean isInsideQualifierClass(PsiElement position) { - if (position.getParent() instanceof PsiJavaCodeReferenceElement) { - final PsiElement qualifier = ((PsiJavaCodeReferenceElement)position.getParent()).getQualifier(); - if (qualifier instanceof PsiJavaCodeReferenceElement) { - final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve(); - if (qualifierClass instanceof PsiClass) { - PsiElement parent = position; - final PsiManager psiManager = position.getManager(); - while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { - if (psiManager.areElementsEquivalent(parent, qualifierClass)) { - return true; - } - } - } - } - } - return false; - } - - private static boolean superConstructorHasParameters(PsiMethod method) { - final PsiClass psiClass = method.getContainingClass(); - if (psiClass == null) { - return false; - } - - final PsiClass superClass = psiClass.getSuperClass(); - if (superClass != null) { - for (final PsiMethod psiMethod : superClass.getConstructors()) { - final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper(); - if (resolveHelper.isAccessible(psiMethod, method, null) && psiMethod.getParameterList().getParameters().length > 0) { - return true; - } - } - } - return false; - } - - protected static class OverrideableSpace extends TailTypeDecorator { - private final TailType myTail; - - public OverrideableSpace(LookupElement keyword, TailType tail) { - super(keyword); - myTail = tail; - } - - @Override - protected TailType computeTailType(InsertionContext context) { - return context.shouldAddCompletionChar() ? TailType.NONE : myTail; - } - } -} +/* + * Copyright 2000-2012 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.TailType; +import com.intellij.codeInsight.TailTypes; +import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupItem; +import com.intellij.codeInsight.lookup.TailTypeDecorator; +import com.intellij.patterns.ElementPattern; +import com.intellij.patterns.PsiElementPattern; +import com.intellij.patterns.PsiJavaElementPattern; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.filters.*; +import com.intellij.psi.filters.classes.EnumOrAnnotationTypeFilter; +import com.intellij.psi.filters.classes.InterfaceFilter; +import com.intellij.psi.filters.element.ReferenceOnFilter; +import com.intellij.psi.filters.getters.JavaMembersGetter; +import com.intellij.psi.filters.position.*; +import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement; +import com.intellij.psi.jsp.JspElementType; +import com.intellij.psi.templateLanguages.OuterLanguageElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.Consumer; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NonNls; + +import static com.intellij.patterns.PsiJavaPatterns.*; +import static com.intellij.patterns.StandardPatterns.not; + +public class JavaCompletionData extends JavaAwareCompletionData { + private static final @NonNls String[] BLOCK_FINALIZERS = {"{", "}", ";", ":", "else"}; + + private static final PsiElementPattern AFTER_DOT = psiElement().afterLeaf("."); + + public static final LeftNeighbour INSTANCEOF_PLACE = new LeftNeighbour(new OrFilter( + new ReferenceOnFilter(new ClassFilter(PsiVariable.class)), + new TextFilter(PsiKeyword.THIS), + new AndFilter(new TextFilter(")"), new ParentElementFilter(new AndFilter( + new ClassFilter(PsiTypeCastExpression.class, false), + new OrFilter( + new ParentElementFilter(new ClassFilter(PsiExpression.class)), + new ClassFilter(PsiExpression.class))))), + new AndFilter(new TextFilter("]"), new ParentElementFilter(new ClassFilter(PsiArrayAccessExpression.class))))); + + public static final PsiJavaElementPattern.Capture VARIABLE_AFTER_FINAL = + psiElement().afterLeaf(PsiKeyword.FINAL).inside(PsiDeclarationStatement.class); + + public static final LeftNeighbour AFTER_TRY_BLOCK = new LeftNeighbour(new AndFilter( + new TextFilter("}"), + new ParentElementFilter(new AndFilter( + new LeftNeighbour(new TextFilter(PsiKeyword.TRY)), + new ParentElementFilter(new ClassFilter(PsiTryStatement.class))) + ))); + + private static final PsiJavaElementPattern.Capture INSIDE_PARAMETER_LIST = + psiElement().withParent( + psiElement(PsiJavaCodeReferenceElement.class).insideStarting( + psiElement().withTreeParent( + psiElement(PsiParameterList.class).andNot(psiElement(PsiAnnotationParameterList.class))))); + + public static boolean isInsideParameterList(PsiElement position) { + PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); + PsiModifierList modifierList = PsiTreeUtil.getParentOfType(prev, PsiModifierList.class); + if (modifierList != null) { + if (PsiTreeUtil.isAncestor(modifierList, position, false)) { + return false; + } + PsiElement parent = modifierList.getParent(); + return parent instanceof PsiParameterList || parent instanceof PsiParameter && parent.getParent() instanceof PsiParameterList; + } + return INSIDE_PARAMETER_LIST.accepts(position); + } + + + private static final AndFilter START_OF_CODE_FRAGMENT = new AndFilter( + new ScopeFilter(new AndFilter( + new ClassFilter(JavaCodeFragment.class), + new ClassFilter(PsiExpressionCodeFragment.class, false), + new ClassFilter(PsiJavaCodeReferenceCodeFragment.class, false), + new ClassFilter(PsiTypeCodeFragment.class, false) + )), + new StartElementFilter() + ); + + static final ElementFilter END_OF_BLOCK = new OrFilter( + new AndFilter( + new LeftNeighbour( + new OrFilter( + new AndFilter ( + new TextFilter(BLOCK_FINALIZERS), + new NotFilter ( + new SuperParentFilter(new ClassFilter(PsiAnnotation.class)) + ) + ), + new TextFilter("*/"), + new TokenTypeFilter(JspElementType.HOLDER_TEMPLATE_DATA), + new ClassFilter(OuterLanguageElement.class), + new AndFilter( + new TextFilter(")"), + new NotFilter( + new OrFilter( + new ParentElementFilter(new ClassFilter(PsiExpressionList.class)), + new ParentElementFilter(new ClassFilter(PsiParameterList.class)), + new ParentElementFilter(new ClassFilter(PsiTypeCastExpression.class)) + ) + ) + ))), + new NotFilter(new TextFilter(".")) + ), + START_OF_CODE_FRAGMENT + ); + + static final ElementPattern START_SWITCH = + psiElement().afterLeaf(psiElement().withText("{").withParents(PsiCodeBlock.class, PsiSwitchStatement.class)); + + private static final ElementPattern SUPER_OR_THIS_PATTERN = + and(JavaSmartCompletionContributor.INSIDE_EXPRESSION, + not(psiElement().afterLeaf(PsiKeyword.CASE)), + not(psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.THIS, PsiKeyword.SUPER))), + not(START_SWITCH)); + + public static final AndFilter CLASS_START = new AndFilter( + new OrFilter( + END_OF_BLOCK, + new PatternFilter(psiElement().afterLeaf( + or( + psiElement().withoutText(".").inside(psiElement(PsiModifierList.class)), + psiElement().isNull()))) + ), + new PatternFilter(not(psiElement().afterLeaf("@")))); + + private static final String[] PRIMITIVE_TYPES = new String[]{ + PsiKeyword.SHORT, PsiKeyword.BOOLEAN, + PsiKeyword.DOUBLE, PsiKeyword.LONG, + PsiKeyword.INT, PsiKeyword.FLOAT, + PsiKeyword.CHAR, PsiKeyword.BYTE + }; + + private static final ElementFilter CLASS_BODY = new OrFilter( + new AfterElementFilter(new TextFilter("{")), + new ScopeFilter(new ClassFilter(JspClassLevelDeclarationStatement.class))); + + public static final ElementPattern START_FOR = + psiElement().afterLeaf(psiElement().withText("(").afterLeaf("for")).withParents(PsiJavaCodeReferenceElement.class, + PsiExpressionStatement.class, PsiForStatement.class); + private static final PsiJavaElementPattern.Capture CLASS_REFERENCE = + psiElement().withParent(psiReferenceExpression().referencing(psiClass())); + + public static final ElementPattern EXPR_KEYWORDS = and( + psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent( + not( + or(psiElement(PsiTypeCastExpression.class), + psiElement(PsiSwitchLabelStatement.class), + psiElement(PsiExpressionStatement.class), + psiElement(PsiPrefixExpression.class) + ) + ) + )), + not(psiElement().afterLeaf(".")) + ); + + public JavaCompletionData(){ + declareCompletionSpaces(); + + initVariantsInFileScope(); + initVariantsInClassScope(); + initVariantsInMethodScope(); + + defineScopeEquivalence(PsiMethod.class, PsiClassInitializer.class); + defineScopeEquivalence(PsiMethod.class, JavaCodeFragment.class); + } + + public static final ElementPattern DECLARATION_START = psiElement().andNot(psiElement().afterLeaf("@", ".")). + andOr( + psiElement().and(new FilterPattern(CLASS_BODY)). + andOr( + new FilterPattern(END_OF_BLOCK), + psiElement().afterLeaf(or( + psiElement().inside(PsiModifierList.class), + psiElement().withElementType(JavaTokenType.GT).inside(PsiTypeParameterList.class) + ))), + psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiMember.class), + psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiClassLevelDeclarationStatement.class) + ); + + private void declareCompletionSpaces() { + declareFinalScope(PsiFile.class); + + { + // Class body + final CompletionVariant variant = new CompletionVariant(CLASS_BODY); + variant.includeScopeClass(PsiClass.class, true); + registerVariant(variant); + } + { + // Method body + final CompletionVariant variant = new CompletionVariant(new AndFilter(new InsideElementFilter(new ClassFilter(PsiCodeBlock.class)), + new NotFilter(new InsideElementFilter(new ClassFilter(JspClassLevelDeclarationStatement.class))))); + variant.includeScopeClass(PsiMethod.class, true); + variant.includeScopeClass(PsiClassInitializer.class, true); + registerVariant(variant); + } + + { + // Field initializer + final CompletionVariant variant = new CompletionVariant(new AfterElementFilter(new TextFilter("="))); + variant.includeScopeClass(PsiField.class, true); + registerVariant(variant); + } + + declareFinalScope(PsiLiteralExpression.class); + declareFinalScope(PsiComment.class); + } + + protected void initVariantsInFileScope(){ + { + final CompletionVariant variant = new CompletionVariant(PsiJavaFile.class, CLASS_START); + variant.includeScopeClass(PsiClass.class); + + variant.addCompletion(PsiKeyword.CLASS); + variant.addCompletion(PsiKeyword.INTERFACE); + + registerVariant(variant); + } + + } + + /** + * aClass == null for JspDeclaration scope + */ + protected void initVariantsInClassScope() { +// Completion for extends keyword +// position + { + final ElementFilter position = new AndFilter( + new NotFilter(CLASS_BODY), + new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))), + new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))), + new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))), + new NotFilter(new ScopeFilter(new EnumOrAnnotationTypeFilter())), + new LeftNeighbour(new OrFilter( + new ClassFilter(PsiIdentifier.class), + new TextFilter(">")))); +// completion + final CompletionVariant variant = new CompletionVariant(position); + variant.includeScopeClass(PsiClass.class, true); + variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD); + variant.excludeScopeClass(PsiAnonymousClass.class); + variant.excludeScopeClass(PsiTypeParameter.class); + + registerVariant(variant); + } +// Completion for implements keyword +// position + { + final ElementFilter position = new AndFilter( + new NotFilter(CLASS_BODY), + new NotFilter(new BeforeElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))), + new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))), + new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))), + new LeftNeighbour(new OrFilter( + new ClassFilter(PsiIdentifier.class), + new TextFilter(">"))), + new NotFilter(new ScopeFilter(new InterfaceFilter()))); +// completion + final CompletionVariant variant = new CompletionVariant(position); + variant.includeScopeClass(PsiClass.class, true); + variant.addCompletion(PsiKeyword.IMPLEMENTS, TailType.HUMBLE_SPACE_BEFORE_WORD); + variant.excludeScopeClass(PsiAnonymousClass.class); + + registerVariant(variant); + } + + + { + final CompletionVariant variant = new CompletionVariant(PsiElement.class, psiElement().afterLeaf( + psiElement(PsiIdentifier.class).afterLeaf( + psiElement().withText(string().oneOf(",", "<")).withParent(PsiTypeParameterList.class)))); + //variant.includeScopeClass(PsiClass.class, true); + variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD); + registerVariant(variant); + } + } + + private void initVariantsInMethodScope() { +// Completion for classes in method throws section +// position + { + final ElementFilter position = new LeftNeighbour(new AndFilter( + new TextFilter(")"), + new ParentElementFilter(new ClassFilter(PsiParameterList.class)))); + +// completion + CompletionVariant variant = new CompletionVariant(PsiMethod.class, position); + variant.includeScopeClass(PsiClass.class); // for throws on separate line + variant.addCompletion(PsiKeyword.THROWS); + + registerVariant(variant); + +//in annotation methods + variant = new CompletionVariant(PsiAnnotationMethod.class, position); + variant.addCompletion(PsiKeyword.DEFAULT); + registerVariant(variant); + } + + { +// instanceof keyword + final ElementFilter position = INSTANCEOF_PLACE; + final CompletionVariant variant = new CompletionVariant(position); + variant.includeScopeClass(PsiExpression.class, true); + variant.includeScopeClass(PsiMethod.class); + variant.addCompletion(PsiKeyword.INSTANCEOF); + + registerVariant(variant); + } + + { +// Keyword completion in returns !!!! + final CompletionVariant variant = new CompletionVariant(PsiMethod.class, new LeftNeighbour(new TextFilter(PsiKeyword.RETURN))); + variant.addCompletion(PsiKeyword.TRUE, TailType.NONE); + variant.addCompletion(PsiKeyword.FALSE, TailType.NONE); + registerVariant(variant); + } + + +// Catch/Finally completion + { + final ElementFilter position = AFTER_TRY_BLOCK; + + final CompletionVariant variant = new CompletionVariant(position); + variant.includeScopeClass(PsiCodeBlock.class, true); + variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH); + variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE); + registerVariant(variant); + } + +// Catch/Finally completion + { + final ElementFilter position = new LeftNeighbour(new AndFilter( + new TextFilter("}"), + new ParentElementFilter(new AndFilter( + new LeftNeighbour(new NotFilter(new TextFilter(PsiKeyword.TRY))), + new OrFilter( + new ParentElementFilter(new ClassFilter(PsiTryStatement.class)), + new ParentElementFilter(new ClassFilter(PsiCatchSection.class))) + )))); + + final CompletionVariant variant = new CompletionVariant(position); + variant.includeScopeClass(PsiCodeBlock.class, false); + variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH); + variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE); + registerVariant(variant); + } + +// Completion for else expression +// completion + { + final ElementFilter position = new LeftNeighbour( + new OrFilter( + new AndFilter(new TextFilter("}"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 3)), + new AndFilter(new TextFilter(";"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 2)) + )); + final CompletionVariant variant = new CompletionVariant(PsiMethod.class, position); + variant.addCompletion(PsiKeyword.ELSE); + + registerVariant(variant); + } + + } + + private static TailType getReturnTail(PsiElement position) { + PsiElement scope = position; + while(true){ + if (scope instanceof PsiFile || scope instanceof PsiClassInitializer){ + return TailType.NONE; + } + + if (scope instanceof PsiMethod){ + final PsiMethod method = (PsiMethod)scope; + if(method.isConstructor() || PsiType.VOID.equals(method.getReturnType())) { + return TailType.SEMICOLON; + } + + return TailType.HUMBLE_SPACE_BEFORE_WORD; + } + scope = scope.getParent(); + } + } + + private static void addStatementKeywords(CompletionResultSet variant, PsiElement position) { + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH)); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH)); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailType.createSimpleTailType('{'))); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FOR), TailTypes.FOR_LPARENTH)); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IF), TailTypes.IF_LPARENTH)); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.TRY), TailType.createSimpleTailType('{'))); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.THROW), TailType.INSERT_SPACE)); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH)); + + if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_4)) { + variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.ASSERT), TailType.INSERT_SPACE)); + } + + TailType returnTail = getReturnTail(position); + LookupElement ret = createKeyword(position, PsiKeyword.RETURN); + if (returnTail != TailType.NONE) { + ret = new OverrideableSpace(ret, returnTail); + } + variant.addElement(ret); + } + + @Override + public void fillCompletions(CompletionParameters parameters, final CompletionResultSet result) { + final PsiElement position = parameters.getPosition(); + if (PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null) { + return; + } + + PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class); + if (statement == null) { + statement = PsiTreeUtil.getParentOfType(position, PsiDeclarationStatement.class); + } + if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) { + if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).accepts(statement)) { + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + } + + if (isStatementPosition(position)) { + if (PsiTreeUtil.getParentOfType(position, PsiSwitchStatement.class, false, PsiMember.class) != null) { + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE)); + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON)); + if (START_SWITCH.accepts(position)) { + return; + } + } + + addBreakContinue(result, position); + addStatementKeywords(result, position); + } + + if (SUPER_OR_THIS_PATTERN.accepts(position)) { + if (!AFTER_DOT.accepts(position) || isInsideQualifierClass(position)) { + result.addElement(createKeyword(position, PsiKeyword.THIS)); + + final LookupItem superItem = (LookupItem)createKeyword(position, PsiKeyword.SUPER); + if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(position)) { + final PsiMethod method = PsiTreeUtil.getParentOfType(position, PsiMethod.class, false, PsiClass.class); + assert method != null; + final boolean hasParams = superConstructorHasParameters(method); + superItem.setInsertHandler(new ParenthesesInsertHandler() { + @Override + protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) { + return hasParams; + } + + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + super.handleInsert(context, item); + TailType.insertChar(context.getEditor(), context.getTailOffset(), ';'); + } + }); + } + + result.addElement(superItem); + } + } + + if (EXPR_KEYWORDS.accepts(position)) { + result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); + result.addElement(createKeyword(position, PsiKeyword.NULL)); + result.addElement(createKeyword(position, PsiKeyword.TRUE)); + result.addElement(createKeyword(position, PsiKeyword.FALSE)); + } + + if (PsiTreeUtil.prevVisibleLeaf(position) == null) { + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + else if (END_OF_BLOCK.isAcceptable(position, position) && PsiTreeUtil.getParentOfType(position, PsiMember.class) == null) { + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + + if (isInsideParameterList(position) && !psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) && !AFTER_DOT.accepts(position)) { + result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + + if (CLASS_START.isAcceptable(position, position) && + PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) == null) { + for (String s : ModifierChooser.getKeywords(position)) { + result.addElement(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + } + + addPrimitiveTypes(result, position); + + if (isAfterTypeDot(position)) { + result.addElement(createKeyword(position, PsiKeyword.CLASS)); + } + + addUnfinishedMethodTypeParameters(position, result); + + if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) && + !BasicExpressionCompletionContributor.AFTER_DOT.accepts(position) && + !(position.getParent() instanceof PsiLiteralExpression)) { + addExpectedTypeMembers(parameters, result, position); + if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) { + new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result); + } + } + } + + static void addExpectedTypeMembers(CompletionParameters parameters, final CompletionResultSet result, PsiElement position) { + for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { + new JavaMembersGetter(info.getDefaultType(), position).addMembers(parameters, parameters.getInvocationCount() > 1, new Consumer() { + @Override + public void consume(LookupElement element) { + result.addElement(element); + } + }); + } + } + + private static void addUnfinishedMethodTypeParameters(PsiElement position, CompletionResultSet result) { + final ProcessingContext context = new ProcessingContext(); + if (psiElement().inside( + psiElement(PsiTypeElement.class).afterLeaf( + psiElement().withText(">").withParent( + psiElement(PsiTypeParameterList.class).withParent(PsiErrorElement.class).save("typeParameterList")))).accepts(position, context)) { + final PsiTypeParameterList list = (PsiTypeParameterList)context.get("typeParameterList"); + PsiElement current = list.getParent().getParent(); + if (current instanceof PsiField) { + current = current.getParent(); + } + if (current instanceof PsiClass) { + for (PsiTypeParameter typeParameter : list.getTypeParameters()) { + result.addElement(new JavaPsiClassReferenceElement(typeParameter)); + } + } + } + } + + static boolean isAfterPrimitiveOrArrayType(PsiElement element) { + return psiElement().withParent( + psiReferenceExpression().withFirstChild( + psiElement(PsiClassObjectAccessExpression.class).withLastChild( + not(psiElement().withText(PsiKeyword.CLASS))))).accepts(element); + } + + static boolean isAfterTypeDot(PsiElement position) { + if (isInsideParameterList(position) || position.getContainingFile() instanceof PsiJavaCodeReferenceCodeFragment) { + return false; + } + + return psiElement().afterLeaf(psiElement().withText(".").afterLeaf(CLASS_REFERENCE)).accepts(position) || + isAfterPrimitiveOrArrayType(position); + } + + private static void addPrimitiveTypes(CompletionResultSet result, PsiElement position) { + boolean inCast = psiElement() + .afterLeaf(psiElement().withText("(").withParent(psiElement(PsiParenthesizedExpression.class, PsiTypeCastExpression.class))) + .accepts(position); + + boolean typeFragment = position.getContainingFile() instanceof PsiTypeCodeFragment && PsiTreeUtil.prevVisibleLeaf(position) == null; + boolean declaration = DECLARATION_START.accepts(position); + if (START_FOR.accepts(position) || + isInsideParameterList(position) && !AFTER_DOT.accepts(position) || + VARIABLE_AFTER_FINAL.accepts(position) || + inCast || + declaration || + typeFragment || + isStatementPosition(position)) { + for (String primitiveType : PRIMITIVE_TYPES) { + LookupElement keyword = createKeyword(position, primitiveType); + result.addElement(inCast || typeFragment ? keyword : new OverrideableSpace(keyword, TailType.HUMBLE_SPACE_BEFORE_WORD)); + } + } + if (declaration) { + result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } else if (typeFragment && ((PsiTypeCodeFragment)position.getContainingFile()).isVoidValid()) { + result.addElement(createKeyword(position, PsiKeyword.VOID)); + } + } + + private static void addBreakContinue(CompletionResultSet result, PsiElement position) { + PsiLoopStatement loop = PsiTreeUtil.getParentOfType(position, PsiLoopStatement.class); + + LookupElement br = createKeyword(position, PsiKeyword.BREAK); + LookupElement cont = createKeyword(position, PsiKeyword.CONTINUE); + TailType tailType; + if (psiElement().insideSequence(true, psiElement(PsiLabeledStatement.class), + or(psiElement(PsiFile.class), psiElement(PsiMethod.class), + psiElement(PsiClassInitializer.class))).accepts(position)) { + tailType = TailType.HUMBLE_SPACE_BEFORE_WORD; + } + else { + tailType = TailType.SEMICOLON; + } + br = TailTypeDecorator.withTail(br, tailType); + cont = TailTypeDecorator.withTail(cont, tailType); + + if (loop != null && new InsideElementFilter(new ClassFilter(PsiStatement.class)).isAcceptable(position, loop)) { + result.addElement(br); + result.addElement(cont); + } + if (psiElement().inside(PsiSwitchStatement.class).accepts(position)) { + result.addElement(br); + } + } + + private static boolean isStatementPosition(PsiElement position) { + if (PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) { + return false; + } + + if (psiElement().withSuperParent(2, PsiConditionalExpression.class).andNot(psiElement().insideStarting(psiElement(PsiConditionalExpression.class))).accepts(position)) { + return false; + } + + if (END_OF_BLOCK.isAcceptable(position, position) && + PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) != null) { + return true; + } + + if (psiElement().withParents(PsiReferenceExpression.class, PsiExpressionStatement.class, PsiIfStatement.class).andNot( + psiElement().afterLeaf(".")).accepts(position)) { + PsiElement stmt = position.getParent().getParent(); + PsiIfStatement ifStatement = (PsiIfStatement)stmt.getParent(); + if (ifStatement.getElseBranch() == stmt || ifStatement.getThenBranch() == stmt) { + return true; + } + } + + return false; + } + + protected static LookupElement createKeyword(PsiElement position, String keyword) { + return BasicExpressionCompletionContributor.createKeywordLookupItem(position, keyword); + } + + private static boolean isInsideQualifierClass(PsiElement position) { + if (position.getParent() instanceof PsiJavaCodeReferenceElement) { + final PsiElement qualifier = ((PsiJavaCodeReferenceElement)position.getParent()).getQualifier(); + if (qualifier instanceof PsiJavaCodeReferenceElement) { + final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve(); + if (qualifierClass instanceof PsiClass) { + PsiElement parent = position; + final PsiManager psiManager = position.getManager(); + while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { + if (psiManager.areElementsEquivalent(parent, qualifierClass)) { + return true; + } + } + } + } + } + return false; + } + + private static boolean superConstructorHasParameters(PsiMethod method) { + final PsiClass psiClass = method.getContainingClass(); + if (psiClass == null) { + return false; + } + + final PsiClass superClass = psiClass.getSuperClass(); + if (superClass != null) { + for (final PsiMethod psiMethod : superClass.getConstructors()) { + final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper(); + if (resolveHelper.isAccessible(psiMethod, method, null) && psiMethod.getParameterList().getParameters().length > 0) { + return true; + } + } + } + return false; + } + + protected static class OverrideableSpace extends TailTypeDecorator { + private final TailType myTail; + + public OverrideableSpace(LookupElement keyword, TailType tail) { + super(keyword); + myTail = tail; + } + + @Override + protected TailType computeTailType(InsertionContext context) { + return context.shouldAddCompletionChar() ? TailType.NONE : myTail; + } + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno.java b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno.java new file mode 100644 index 000000000000..5eee9cf46544 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno.java @@ -0,0 +1,4 @@ +enum TestEnum { TEXT1, TEXT2 } +public @interface TestAnnotation { TestEnum value(); } + +@TestAnnotation(Tex) \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno_after.java b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno_after.java new file mode 100644 index 000000000000..4c0f47856d0c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersInAnno_after.java @@ -0,0 +1,4 @@ +enum TestEnum { TEXT1, TEXT2 } +public @interface TestAnnotation { TestEnum value(); } + +@TestAnnotation(TestEnum.TEXT1) \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index c23f41246078..d6278ab25f97 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -993,6 +993,7 @@ public class ListUtils { public void testSuggestExpectedTypeMembers() throws Throwable { doTest('\n') } public void testSuggestExpectedTypeMembersInCall() throws Throwable { doTest('\n') } + public void testSuggestExpectedTypeMembersInAnno() throws Throwable { doTest('\n') } public void testExpectedTypesDotSelectsItem() throws Throwable { doTest('.') } public void testExpectedTypeMembersVersusStaticImports() throws Throwable {