mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
711 lines
30 KiB
Java
711 lines
30 KiB
Java
/*
|
|
* 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<PsiElement,?> 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<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(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).withParent(not(psiElement(PsiParameter.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<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()));
|
|
|
|
public 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(){
|
|
{
|
|
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<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 (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<LookupElement>() {
|
|
@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<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;
|
|
}
|
|
}
|
|
}
|