| /* |
| * Copyright 2000-2013 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.PsiTypeLookupItem; |
| import com.intellij.codeInsight.lookup.TailTypeDecorator; |
| import com.intellij.patterns.ElementPattern; |
| 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.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.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"}; |
| |
| public static final ElementPattern<PsiElement> AFTER_DOT = psiElement().afterLeaf("."); |
| |
| public static final PsiJavaElementPattern.Capture<PsiElement> 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<PsiElement> 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<PsiElement> START_SWITCH = |
| psiElement().afterLeaf(psiElement().withText("{").withParents(PsiCodeBlock.class, PsiSwitchStatement.class)); |
| |
| private static final ElementPattern<PsiElement> SUPER_OR_THIS_PATTERN = |
| and(JavaSmartCompletionContributor.INSIDE_EXPRESSION, |
| not(psiElement().afterLeaf(PsiKeyword.CASE)), |
| not(psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.THIS, PsiKeyword.SUPER))), |
| not(psiElement().inside(PsiAnnotation.class)), |
| not(START_SWITCH), |
| not(JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN)); |
| |
| 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<PsiElement> START_FOR = |
| psiElement().afterLeaf(psiElement().withText("(").afterLeaf("for")).withParents(PsiJavaCodeReferenceElement.class, |
| PsiExpressionStatement.class, PsiForStatement.class); |
| private static final PsiJavaElementPattern.Capture<PsiElement> CLASS_REFERENCE = |
| psiElement().withParent(psiReferenceExpression().referencing(psiClass())); |
| |
| private static final ElementPattern<PsiElement> 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<PsiElement> 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(){ |
| } |
| |
| /** |
| * 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); |
| } |
| |
| { |
| // 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; |
| } |
| if (scope instanceof PsiLambdaExpression) { |
| final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)scope)); |
| if (PsiType.VOID.equals(returnType)) { |
| 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), TailTypes.DO_LBRACE)); |
| 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), TailTypes.TRY_LBRACE)); |
| 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); |
| } |
| PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(position); |
| if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) { |
| if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).afterLeaf("{").accepts(statement)) { |
| PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(prevLeaf, PsiTryStatement.class); |
| if (tryStatement == null || tryStatement.getCatchSections().length > 0 || tryStatement.getFinallyBlock() != null) { |
| 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)) { |
| final boolean afterDot = AFTER_DOT.accepts(position); |
| final boolean insideQualifierClass = isInsideQualifierClass(position); |
| final boolean insideInheritorClass = PsiUtil.isLanguageLevel8OrHigher(position) && isInsideInheritorClass(position); |
| if (!afterDot || insideQualifierClass || insideInheritorClass) { |
| if (!afterDot || insideQualifierClass) { |
| 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<LookupElement>() { |
| @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 (isExpressionPosition(position)) { |
| if (PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) == null) { |
| result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); |
| result.addElement(createKeyword(position, PsiKeyword.NULL)); |
| } |
| if (mayExpectBoolean(parameters)) { |
| result.addElement(createKeyword(position, PsiKeyword.TRUE)); |
| result.addElement(createKeyword(position, PsiKeyword.FALSE)); |
| } |
| } |
| |
| PsiFile file = position.getContainingFile(); |
| if (!(file instanceof PsiExpressionCodeFragment) && |
| !(file instanceof PsiJavaCodeReferenceCodeFragment) && |
| !(file instanceof PsiTypeCodeFragment)) { |
| if (prevLeaf == 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) || isAtResourceVariableStart(position) || isAtCatchVariableStart(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 (isInstanceofPlace(position)) { |
| result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.INSTANCEOF), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| |
| if (isSuitableForClass(position)) { |
| for (String s : ModifierChooser.getKeywords(position)) { |
| result.addElement(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| if (PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) == null) { |
| result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_5)) { |
| result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.ENUM), TailType.INSERT_SPACE)); |
| } |
| } |
| } |
| |
| addPrimitiveTypes(result, position); |
| |
| if (isAfterTypeDot(position)) { |
| result.addElement(createKeyword(position, PsiKeyword.CLASS)); |
| } |
| |
| addUnfinishedMethodTypeParameters(position, result); |
| |
| if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) && |
| !AFTER_DOT.accepts(position)) { |
| addExpectedTypeMembers(parameters, result); |
| if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) { |
| new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result); |
| } |
| } |
| |
| if (JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN.accepts(position)) { |
| result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SUPER), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| } |
| |
| private static boolean mayExpectBoolean(CompletionParameters parameters) { |
| for (ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { |
| PsiType type = info.getType(); |
| if (type instanceof PsiClassType || type == PsiType.BOOLEAN) return true; |
| } |
| return false; |
| } |
| |
| private static boolean isExpressionPosition(PsiElement position) { |
| return EXPR_KEYWORDS.accepts(position) || |
| psiElement().insideStarting(psiElement(PsiClassObjectAccessExpression.class)).accepts(position); |
| } |
| |
| public static boolean isInstanceofPlace(PsiElement position) { |
| PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); |
| if (prev == null) return false; |
| |
| PsiElement expr = PsiTreeUtil.getParentOfType(prev, PsiExpression.class); |
| if (expr != null && expr.getTextRange().getEndOffset() == prev.getTextRange().getEndOffset()) { |
| return true; |
| } |
| |
| if (position instanceof PsiIdentifier && position.getParent() instanceof PsiLocalVariable) { |
| PsiType type = ((PsiLocalVariable)position.getParent()).getType(); |
| if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == null) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean isSuitableForClass(PsiElement position) { |
| if (psiElement().afterLeaf("@").accepts(position) || |
| PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) { |
| return false; |
| } |
| |
| PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); |
| if (prev == null) { |
| return true; |
| } |
| if (psiElement().withoutText(".").inside( |
| psiElement(PsiModifierList.class).withParent( |
| not(psiElement(PsiParameter.class)).andNot(psiElement(PsiParameterList.class)))).accepts(prev) && |
| !psiElement().inside(PsiAnnotationParameterList.class).accepts(prev)) { |
| return true; |
| } |
| |
| return END_OF_BLOCK.isAcceptable(position, position); |
| } |
| |
| static void addExpectedTypeMembers(CompletionParameters parameters, final CompletionResultSet result) { |
| if (parameters.getInvocationCount() <= 1) { // on second completion, StaticMemberProcessor will suggest those |
| for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { |
| new JavaMembersGetter(info.getDefaultType(), parameters).addMembers(false, 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) { |
| if (AFTER_DOT.accepts(position)) { |
| return; |
| } |
| |
| boolean afterNew = psiElement().afterLeaf( |
| psiElement().withText(PsiKeyword.NEW).andNot(psiElement().afterLeaf(PsiKeyword.THROW, "."))).accepts(position); |
| if (afterNew) { |
| PsiElementFactory factory = JavaPsiFacade.getElementFactory(position.getProject()); |
| for (String primitiveType : PRIMITIVE_TYPES) { |
| result.addElement(PsiTypeLookupItem.createLookupItem(factory.createTypeFromText(primitiveType + "[]", null), null)); |
| } |
| result.addElement(PsiTypeLookupItem.createLookupItem(factory.createTypeFromText("void[]", null), null)); |
| return; |
| } |
| |
| 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); |
| boolean expressionPosition = isExpressionPosition(position); |
| boolean inGenerics = PsiTreeUtil.getParentOfType(position, PsiReferenceParameterList.class) != null; |
| if (START_FOR.accepts(position) || |
| isInsideParameterList(position) || |
| inGenerics || |
| VARIABLE_AFTER_FINAL.accepts(position) || |
| inCast || |
| declaration || |
| typeFragment || |
| expressionPosition || |
| isStatementPosition(position)) { |
| for (String primitiveType : PRIMITIVE_TYPES) { |
| result.addElement(createKeyword(position, primitiveType)); |
| } |
| } |
| 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 boolean isAtResourceVariableStart(PsiElement position) { |
| return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiResourceList.class)).accepts(position); |
| } |
| |
| private static boolean isAtCatchVariableStart(PsiElement position) { |
| return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiCatchSection.class)).accepts(position); |
| } |
| |
| 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 isInsideInheritorClass(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 && ((PsiClass)qualifierClass).isInterface()) { |
| PsiElement parent = position; |
| while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { |
| if (PsiUtil.getEnclosingStaticElement(position, (PsiClass)parent) == null && |
| ((PsiClass)parent).isInheritor((PsiClass)qualifierClass, true)) { |
| 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; |
| } |
| |
| public static class OverrideableSpace extends TailTypeDecorator<LookupElement> { |
| 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; |
| } |
| } |
| } |