mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-06 14:58:06 +07:00
699 lines
34 KiB
Java
699 lines
34 KiB
Java
/*
|
|
* Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
|
|
* Use is subject to license terms.
|
|
*/
|
|
package com.intellij.codeInsight.completion;
|
|
|
|
import com.intellij.codeInsight.*;
|
|
import com.intellij.codeInsight.lookup.*;
|
|
import com.intellij.codeInsight.template.Template;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.editor.Editor;
|
|
import com.intellij.openapi.util.*;
|
|
import com.intellij.patterns.ElementPattern;
|
|
import static com.intellij.patterns.PlatformPatterns.psiElement;
|
|
import com.intellij.patterns.PsiJavaPatterns;
|
|
import static com.intellij.patterns.PsiJavaPatterns.psiMethod;
|
|
import static com.intellij.patterns.StandardPatterns.*;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.filters.ElementExtractorFilter;
|
|
import com.intellij.psi.filters.ElementFilter;
|
|
import com.intellij.psi.filters.GeneratorFilter;
|
|
import com.intellij.psi.filters.OrFilter;
|
|
import com.intellij.psi.filters.getters.CastTypeGetter;
|
|
import com.intellij.psi.filters.getters.ExpectedTypesGetter;
|
|
import com.intellij.psi.filters.getters.InstanceOfLeftPartTypeGetter;
|
|
import com.intellij.psi.filters.getters.ThrowsListGetter;
|
|
import com.intellij.psi.filters.types.AssignableFromFilter;
|
|
import com.intellij.psi.filters.types.AssignableGroupFilter;
|
|
import com.intellij.psi.filters.types.AssignableToFilter;
|
|
import com.intellij.psi.filters.types.ReturnTypeFilter;
|
|
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
|
|
import com.intellij.psi.infos.CandidateInfo;
|
|
import com.intellij.psi.javadoc.PsiDocTag;
|
|
import com.intellij.psi.statistics.JavaStatisticsManager;
|
|
import com.intellij.psi.statistics.StatisticsInfo;
|
|
import com.intellij.psi.statistics.StatisticsManager;
|
|
import com.intellij.psi.util.*;
|
|
import com.intellij.util.*;
|
|
import com.intellij.util.containers.ContainerUtil;
|
|
import gnu.trove.THashSet;
|
|
import gnu.trove.TObjectHashingStrategy;
|
|
import org.jetbrains.annotations.NonNls;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* @author peter
|
|
*/
|
|
public class JavaSmartCompletionContributor extends CompletionContributor {
|
|
private static final TObjectHashingStrategy<ExpectedTypeInfo> EXPECTED_TYPE_INFO_STRATEGY = new TObjectHashingStrategy<ExpectedTypeInfo>() {
|
|
public int computeHashCode(final ExpectedTypeInfo object) {
|
|
return object.getType().hashCode();
|
|
}
|
|
|
|
public boolean equals(final ExpectedTypeInfo o1, final ExpectedTypeInfo o2) {
|
|
return o1.getType().equals(o2.getType());
|
|
}
|
|
};
|
|
|
|
private static final ElementExtractorFilter THROWABLES_FILTER = new ElementExtractorFilter(new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE));
|
|
@NonNls private static final String EXCEPTION_TAG = "exception";
|
|
static final ElementPattern<PsiElement> AFTER_NEW =
|
|
psiElement().afterLeaf(
|
|
psiElement().withText(PsiKeyword.NEW).andNot(
|
|
psiElement().afterLeaf(
|
|
psiElement().withText(PsiKeyword.THROW))));
|
|
static final ElementPattern<PsiElement> AFTER_THROW_NEW =
|
|
psiElement().afterLeaf(
|
|
psiElement().withText(PsiKeyword.NEW).afterLeaf(
|
|
psiElement().withText(PsiKeyword.THROW)));
|
|
private static final OrFilter THROWABLE_TYPE_FILTER = new OrFilter(
|
|
new GeneratorFilter(AssignableGroupFilter.class, new ThrowsListGetter()),
|
|
new AssignableFromFilter("java.lang.RuntimeException"));
|
|
public static final ElementPattern<PsiElement> INSIDE_EXPRESSION = or(
|
|
psiElement().withParent(PsiExpression.class).andNot(psiElement().withParent(PsiLiteralExpression.class)),
|
|
psiElement().inside(PsiClassObjectAccessExpression.class),
|
|
psiElement().inside(PsiThisExpression.class),
|
|
psiElement().inside(PsiSuperExpression.class)
|
|
);
|
|
public static final Key<Boolean> TYPE_CAST = Key.create("TYPE_CAST");
|
|
private static final ElementPattern<PsiElement> INSIDE_TYPECAST = psiElement().withParent(
|
|
psiElement(PsiReferenceExpression.class).afterLeaf(
|
|
psiElement().withText(")").withParent(PsiTypeCastExpression.class)));
|
|
|
|
@Nullable
|
|
private static ElementFilter getReferenceFilter(PsiElement element) {
|
|
//throw new foo
|
|
if (AFTER_THROW_NEW.accepts(element)) {
|
|
return new ElementExtractorFilter(THROWABLE_TYPE_FILTER);
|
|
}
|
|
|
|
//new xxx.yyy
|
|
if (psiElement().afterLeaf(psiElement().withText(".")).withSuperParent(2, psiElement(PsiNewExpression.class)).accepts(element)) {
|
|
if (((PsiNewExpression)element.getParent().getParent()).getClassReference() == element.getParent()) {
|
|
return new GeneratorFilter(AssignableGroupFilter.class, new ExpectedTypesGetter());
|
|
}
|
|
}
|
|
|
|
//method throws clause
|
|
if (psiElement().inside(
|
|
psiElement(PsiReferenceList.class).save("refList").withParent(
|
|
psiMethod().withThrowsList(get("refList")))).accepts(element)) {
|
|
return THROWABLES_FILTER;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
|
|
public JavaSmartCompletionContributor() {
|
|
extend(CompletionType.SMART, psiElement().afterLeaf(psiElement().withText("(").withParent(PsiTypeCastExpression.class)), new CompletionProvider<CompletionParameters>() {
|
|
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
|
|
for (final ExpectedTypeInfo type : getExpectedTypes(parameters)) {
|
|
if (type.getType() == PsiType.VOID) {
|
|
continue;
|
|
}
|
|
|
|
final LookupElement item = new LookupElementDecorator<LookupItem>(PsiTypeLookupItem.createLookupItem(type.getDefaultType())) {
|
|
@Override
|
|
public AutoCompletionPolicy getAutoCompletionPolicy() {
|
|
return AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE;
|
|
}
|
|
|
|
@Override
|
|
public void handleInsert(InsertionContext context) {
|
|
final PsiElement position = SmartCompletionDecorator.getPosition(context, this);
|
|
final Editor editor = context.getEditor();
|
|
if (or(psiElement(PsiParenthesizedExpression.class).withLastChild(psiElement(JavaTokenType.RPARENTH)),
|
|
psiElement().afterLeaf(psiElement().withText("(").withParent(PsiTypeCastExpression.class))).accepts(position)) {
|
|
editor.getDocument().deleteString(context.getSelectionEndOffset(),
|
|
context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET));
|
|
}
|
|
|
|
TailTypes.CAST_RPARENTH.processTail(editor, context.getTailOffset());
|
|
DefaultInsertHandler.addImportForItem(context.getFile(), context.getStartOffset(), getDelegate());
|
|
}
|
|
};
|
|
item.putUserData(TYPE_CAST, Boolean.TRUE);
|
|
result.addElement(item);
|
|
}
|
|
}
|
|
});
|
|
|
|
extend(CompletionType.SMART,
|
|
psiElement().beforeLeaf(psiElement(JavaTokenType.RPARENTH)).afterLeaf("(").withParent(
|
|
psiElement(PsiReferenceExpression.class).withParent(
|
|
psiElement(PsiExpressionList.class).withParent(PsiMethodCallExpression.class))), new SameSignatureCallParametersProvider());
|
|
|
|
extend(CompletionType.SMART, psiElement().afterLeaf(PsiKeyword.INSTANCEOF), new CompletionProvider<CompletionParameters>(false) {
|
|
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
|
|
final PsiElement position = parameters.getPosition();
|
|
final PsiType[] leftTypes = ApplicationManager.getApplication().runReadAction(new Computable<PsiType[]>() {
|
|
public PsiType[] compute() {
|
|
return InstanceOfLeftPartTypeGetter.getLeftTypes(position);
|
|
}
|
|
});
|
|
final Set<PsiClassType> expectedClassTypes = new LinkedHashSet<PsiClassType>();
|
|
final Set<PsiClass> parameterizedTypes = new THashSet<PsiClass>();
|
|
for (final PsiType type : leftTypes) {
|
|
if (type instanceof PsiClassType) {
|
|
final PsiClassType classType = (PsiClassType)type;
|
|
if (!classType.isRaw()) {
|
|
ContainerUtil.addIfNotNull(classType.resolve(), parameterizedTypes);
|
|
}
|
|
|
|
expectedClassTypes.add(classType.rawType());
|
|
}
|
|
}
|
|
|
|
processInheritors(parameters, position, position.getContainingFile(), expectedClassTypes, new Consumer<PsiType>() {
|
|
public void consume(PsiType type) {
|
|
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
|
if (psiClass == null) return;
|
|
|
|
if (expectedClassTypes.contains(type)) return;
|
|
|
|
result.addElement(createInstanceofLookupElement(psiClass, parameterizedTypes));
|
|
}
|
|
}, result.getPrefixMatcher());
|
|
}
|
|
});
|
|
|
|
extend(CompletionType.SMART, psiElement(), new CompletionProvider<CompletionParameters>() {
|
|
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
|
|
final PsiElement element = parameters.getPosition();
|
|
final PsiReference reference = element.getContainingFile().findReferenceAt(parameters.getOffset());
|
|
if (reference != null) {
|
|
final ElementFilter filter = getReferenceFilter(element);
|
|
if (filter != null) {
|
|
final List<ExpectedTypeInfo> infos = Arrays.asList(getExpectedTypes(parameters));
|
|
for (final LookupElement item : completeReference(element, reference, filter, true, parameters)) {
|
|
if (AFTER_THROW_NEW.accepts(element)) {
|
|
((LookupItem)item).setAttribute(LookupItem.DONT_CHECK_FOR_INNERS, "");
|
|
if (item.getObject() instanceof PsiClass) {
|
|
JavaCompletionUtil.setShowFQN((LookupItem)item);
|
|
}
|
|
} else {
|
|
((LookupItem)item).setAttribute(LookupItem.NEW_OBJECT_ATTR, "");
|
|
}
|
|
result.addElement(decorate(item, infos));
|
|
}
|
|
}
|
|
else if (INSIDE_TYPECAST.accepts(element)) {
|
|
final ReturnTypeFilter rfilter = new ReturnTypeFilter(new GeneratorFilter(AssignableToFilter.class, new CastTypeGetter()));
|
|
for (final LookupElement item : completeReference(element, reference, rfilter, false, parameters)) {
|
|
result.addElement(item);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
});
|
|
|
|
extend(CompletionType.SMART, INSIDE_EXPRESSION, new ExpectedTypeBasedCompletionProvider() {
|
|
protected void addCompletions(final CompletionParameters params, final CompletionResultSet result, final Collection<ExpectedTypeInfo> _infos) {
|
|
final PsiElement position = params.getPosition();
|
|
final Set<ExpectedTypeInfo> infos = ApplicationManager.getApplication().runReadAction(new Computable<Set<ExpectedTypeInfo>>() {
|
|
public Set<ExpectedTypeInfo> compute() {
|
|
return new THashSet<ExpectedTypeInfo>(_infos, EXPECTED_TYPE_INFO_STRATEGY);
|
|
}
|
|
});
|
|
for (final ExpectedTypeInfo info : infos) {
|
|
final JavaSmartCompletionParameters parameters = new JavaSmartCompletionParameters(params, info);
|
|
final PsiType type = info.getType();
|
|
final boolean isVoid = PsiType.VOID.equals(type);
|
|
final AssignableFromFilter assignableFromFilter = new AssignableFromFilter(type);
|
|
final ElementFilter filter = new ReturnTypeFilter(new ElementFilter() {
|
|
public boolean isAcceptable(Object element, PsiElement context) {
|
|
if (isVoid) {
|
|
return element instanceof PsiMethod;
|
|
}
|
|
return assignableFromFilter.isAcceptable(element, context);
|
|
}
|
|
|
|
public boolean isClassAcceptable(Class hintClass) {
|
|
if (isVoid && !ReflectionCache.isAssignable(PsiMethod.class, hintClass)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
|
|
final Consumer<LookupElement> consumer = new Consumer<LookupElement>() {
|
|
public void consume(final LookupElement lookupElement) {
|
|
final TypedLookupItem typed = lookupElement.as(TypedLookupItem.class);
|
|
if (typed != null) {
|
|
final PsiType psiType = typed.getType();
|
|
if (psiType != null && type.isAssignableFrom(psiType)) {
|
|
result.addElement(decorate(lookupElement, _infos));
|
|
}
|
|
return;
|
|
}
|
|
|
|
final Object object = lookupElement.getObject();
|
|
if (!filter.isClassAcceptable(object.getClass())) return;
|
|
|
|
final PsiSubstitutor substitutor;
|
|
final LookupItem item = lookupElement.as(LookupItem.class);
|
|
if (item != null) {
|
|
substitutor = (PsiSubstitutor)item.getAttribute(LookupItem.SUBSTITUTOR);
|
|
}
|
|
else {
|
|
substitutor = null;
|
|
}
|
|
if (filter.isAcceptable(object, position) ||
|
|
substitutor != null && object instanceof PsiElement && filter.isAcceptable(new CandidateInfo((PsiElement)object, substitutor), position)) {
|
|
result.addElement(decorate(lookupElement, _infos));
|
|
}
|
|
}
|
|
};
|
|
for (ExpressionSmartCompletionContributor contributor : ExpressionSmartCompletionContributor.CONTRIBUTORS) {
|
|
contributor.fillCompletionVariants(parameters, CompletionService.getCompletionService().createResultSet(parameters, consumer, JavaSmartCompletionContributor.this));
|
|
}
|
|
|
|
}
|
|
}
|
|
});
|
|
|
|
extend(CompletionType.SMART, or(
|
|
PsiJavaPatterns.psiElement().withParent(PsiNameValuePair.class),
|
|
PsiJavaPatterns.psiElement().withSuperParent(2, PsiNameValuePair.class)), new CompletionProvider<CompletionParameters>() {
|
|
public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
|
|
final PsiElement element = parameters.getPosition();
|
|
final ElementPattern<? extends PsiElement> leftNeighbor = PsiJavaPatterns.psiElement().afterLeaf(PsiJavaPatterns.psiElement().withText("."));
|
|
final boolean needQualify = leftNeighbor.accepts(element);
|
|
|
|
for (final PsiType type : ExpectedTypesGetter.getExpectedTypes(element, false)) {
|
|
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
|
if (psiClass != null && psiClass.isAnnotationType()) {
|
|
final LookupItem item = new JavaPsiClassReferenceElement(psiClass).setTailType(TailType.NONE);
|
|
if (needQualify) JavaCompletionUtil.qualify(item);
|
|
result.addElement(item);
|
|
}
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
extend(CompletionType.SMART, psiElement().inside(
|
|
psiElement(PsiDocTag.class).withName(
|
|
string().oneOf(PsiKeyword.THROWS, EXCEPTION_TAG))), new CompletionProvider<CompletionParameters>() {
|
|
public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
|
|
final PsiElement element = parameters.getPosition();
|
|
final Set<PsiClass> throwsSet = new HashSet<PsiClass>();
|
|
final PsiMethod method = PsiTreeUtil.getContextOfType(element, PsiMethod.class, true);
|
|
if(method != null){
|
|
for (PsiClassType ref : method.getThrowsList().getReferencedTypes()) {
|
|
final PsiClass exception = ref.resolve();
|
|
if (exception != null && throwsSet.add(exception)) {
|
|
result.addElement(TailTypeDecorator.createDecorator(new JavaPsiClassReferenceElement(exception).setInsertHandler(new DefaultInsertHandler()), TailType.SPACE));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
final Key<PsiTryStatement> tryKey = Key.create("try");
|
|
extend(CompletionType.SMART, psiElement().afterLeaf(
|
|
psiElement().withText("("))
|
|
.withSuperParent(3, psiElement(PsiCatchSection.class).withParent(
|
|
psiElement(PsiTryStatement.class).save(tryKey))), new CompletionProvider<CompletionParameters>() {
|
|
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
|
|
final PsiCodeBlock tryBlock = context.get(tryKey).getTryBlock();
|
|
if (tryBlock == null) return;
|
|
|
|
for (final PsiClassType type : ExceptionUtil.getThrownExceptions(tryBlock.getStatements())) {
|
|
result.addElement(TailTypeDecorator.createDecorator(PsiTypeLookupItem.createLookupItem(type).setInsertHandler(new DefaultInsertHandler()), TailType.SPACE));
|
|
}
|
|
}
|
|
});
|
|
|
|
extend(CompletionType.SMART, psiElement().inside(psiElement(PsiReferenceParameterList.class)),
|
|
new CompletionProvider<CompletionParameters>() {
|
|
|
|
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext processingContext, @NotNull final CompletionResultSet resultSet) {
|
|
final PsiElement context = parameters.getPosition();
|
|
|
|
final Pair<PsiClass, Integer> pair = getTypeParameterInfo(context);
|
|
if (pair == null) return;
|
|
|
|
final PsiClass referencedClass = pair.first;
|
|
final int parameterIndex = pair.second.intValue();
|
|
final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters();
|
|
final PsiTypeParameter targetParameter = typeParameters[parameterIndex];
|
|
|
|
boolean isLast = parameterIndex == typeParameters.length - 1;
|
|
final TailType tail = isLast ? new CharTailType('>') : TailType.COMMA;
|
|
|
|
boolean hasExpected = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
|
|
public Boolean compute() {
|
|
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper();
|
|
final PsiType[] psiTypes = ExpectedTypesGetter.getExpectedTypes(context, false);
|
|
if (psiTypes.length == 0) return false;
|
|
|
|
for (PsiType type : psiTypes) {
|
|
if (!(type instanceof PsiClassType)) continue;
|
|
final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics();
|
|
final PsiClass typeClass = result.getElement();
|
|
final PsiSubstitutor substitutor = result.getSubstitutor();
|
|
|
|
if (!InheritanceUtil.isInheritorOrSelf(referencedClass, typeClass, true)) continue;
|
|
|
|
final PsiSubstitutor currentSubstitutor =
|
|
TypeConversionUtil.getClassSubstitutor(typeClass, referencedClass, PsiSubstitutor.EMPTY);
|
|
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(typeClass)) {
|
|
final PsiType argSubstitution = substitutor.substitute(parameter);
|
|
final PsiType paramSubstitution = currentSubstitutor.substitute(parameter);
|
|
final PsiType substitution = resolveHelper
|
|
.getSubstitutionForTypeParameter(targetParameter, paramSubstitution, argSubstitution, false,
|
|
PsiUtil.getLanguageLevel(context));
|
|
if (substitution != null && substitution != PsiType.NULL) {
|
|
final LookupItem item = PsiTypeLookupItem.createLookupItem(substitution);
|
|
resultSet.addElement(TailTypeDecorator.createDecorator(item.setInsertHandler(new DefaultInsertHandler()), tail));
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}).booleanValue();
|
|
|
|
|
|
if (!hasExpected) {
|
|
final List<PsiClassType> typeList = Collections.singletonList((PsiClassType)TypeConversionUtil.typeParameterErasure(targetParameter));
|
|
processInheritors(parameters, context, parameters.getOriginalFile(), typeList, new Consumer<PsiType>() {
|
|
public void consume(final PsiType type) {
|
|
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
|
if (psiClass == null) return;
|
|
|
|
resultSet.addElement(TailTypeDecorator.createDecorator(new JavaPsiClassReferenceElement(psiClass).setInsertHandler(new DefaultInsertHandler()), tail));
|
|
}
|
|
}, resultSet.getPrefixMatcher());
|
|
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
extend(CompletionType.SMART, AFTER_NEW, new CompletionProvider<CompletionParameters>(false) {
|
|
public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext matchingContext, @NotNull final CompletionResultSet result) {
|
|
final PsiElement identifierCopy = parameters.getPosition();
|
|
final PsiFile file = parameters.getOriginalFile();
|
|
|
|
final List<PsiClassType> expectedClassTypes = new SmartList<PsiClassType>();
|
|
final List<PsiArrayType> expectedArrayTypes = new SmartList<PsiArrayType>();
|
|
final List<ExpectedTypeInfo> infos = new SmartList<ExpectedTypeInfo>();
|
|
|
|
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
|
public void run() {
|
|
infos.addAll(Arrays.asList(getExpectedTypes(parameters)));
|
|
for (PsiType type : ExpectedTypesGetter.getExpectedTypes(identifierCopy, true)) {
|
|
if (type instanceof PsiClassType) {
|
|
final PsiClassType classType = (PsiClassType)type;
|
|
if (classType.resolve() != null) {
|
|
expectedClassTypes.add(classType);
|
|
}
|
|
}
|
|
else if (type instanceof PsiArrayType) {
|
|
expectedArrayTypes.add((PsiArrayType)type);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
for (final PsiArrayType type : expectedArrayTypes) {
|
|
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
|
public void run() {
|
|
final LookupItem item = PsiTypeLookupItem.createLookupItem(JavaCompletionUtil.eliminateWildcards(type));
|
|
item.setAttribute(LookupItem.DONT_CHECK_FOR_INNERS, "");
|
|
if (item.getObject() instanceof PsiClass) {
|
|
JavaCompletionUtil.setShowFQN(item);
|
|
}
|
|
item.setInsertHandler(new DefaultInsertHandler()); //braces & shortening
|
|
result.addElement(decorate(item, infos));
|
|
}
|
|
});
|
|
}
|
|
|
|
processInheritors(parameters, identifierCopy, file, expectedClassTypes, new Consumer<PsiType>() {
|
|
public void consume(final PsiType type) {
|
|
addExpectedType(result, type, parameters, infos);
|
|
}
|
|
}, result.getPrefixMatcher());
|
|
}
|
|
});
|
|
}
|
|
|
|
public static SmartCompletionDecorator decorate(LookupElement lookupElement, Collection<ExpectedTypeInfo> infos) {
|
|
if (lookupElement instanceof LookupItem) {
|
|
final LookupItem lookupItem = (LookupItem)lookupElement;
|
|
if (lookupItem.getInsertHandler() == null) {
|
|
lookupItem.setInsertHandler(new DefaultInsertHandler(){
|
|
@Override
|
|
protected TailType getTailType(char completionChar) {
|
|
return TailType.NONE;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return new SmartCompletionDecorator(lookupElement, infos);
|
|
}
|
|
|
|
private static LookupElement createInstanceofLookupElement(PsiClass psiClass, Set<PsiClass> toWildcardInheritors) {
|
|
final PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
|
|
if (typeParameters.length > 0) {
|
|
for (final PsiClass parameterizedType : toWildcardInheritors) {
|
|
if (psiClass.isInheritor(parameterizedType, true)) {
|
|
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
|
|
final PsiWildcardType wildcard = PsiWildcardType.createUnbounded(psiClass.getManager());
|
|
for (final PsiTypeParameter typeParameter : typeParameters) {
|
|
substitutor = substitutor.put(typeParameter, wildcard);
|
|
}
|
|
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
|
|
return PsiTypeLookupItem.createLookupItem(factory.createType(psiClass, substitutor));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return new JavaPsiClassReferenceElement(psiClass);
|
|
}
|
|
|
|
@Nullable
|
|
public static Pair<PsiClass, Integer> getTypeParameterInfo(PsiElement context) {
|
|
final PsiReferenceParameterList parameterList = PsiTreeUtil.getContextOfType(context, PsiReferenceParameterList.class, true);
|
|
if (parameterList == null) return null;
|
|
|
|
final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)parameterList.getParent();
|
|
final int parameterIndex;
|
|
|
|
int index = 0;
|
|
final PsiTypeElement typeElement = PsiTreeUtil.getContextOfType(context, PsiTypeElement.class, true);
|
|
if(typeElement != null){
|
|
final PsiTypeElement[] elements = referenceElement.getParameterList().getTypeParameterElements();
|
|
while (index < elements.length) {
|
|
final PsiTypeElement element = elements[index++];
|
|
if(element == typeElement) break;
|
|
}
|
|
}
|
|
parameterIndex = index - 1;
|
|
|
|
if(parameterIndex < 0) return null;
|
|
final PsiElement target = referenceElement.resolve();
|
|
if(!(target instanceof PsiClass)) return null;
|
|
|
|
final PsiClass referencedClass = (PsiClass)target;
|
|
final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters();
|
|
if(typeParameters.length <= parameterIndex) return null;
|
|
|
|
return Pair.create(referencedClass, parameterIndex);
|
|
}
|
|
|
|
|
|
public static void processInheritors(final CompletionParameters parameters, final PsiElement identifierCopy, final PsiFile file, final Collection<PsiClassType> expectedClassTypes,
|
|
final Consumer<PsiType> consumer, final PrefixMatcher matcher) {
|
|
//quick
|
|
for (final PsiClassType type : expectedClassTypes) {
|
|
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
|
public void run() {
|
|
consumer.consume(type);
|
|
|
|
final PsiClassType.ClassResolveResult baseResult = JavaCompletionUtil.originalize(type).resolveGenerics();
|
|
final PsiClass baseClass = baseResult.getElement();
|
|
if (baseClass == null) return;
|
|
|
|
final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor();
|
|
|
|
final THashSet<PsiType> statVariants = new THashSet<PsiType>();
|
|
final Processor<PsiClass> processor = CodeInsightUtil
|
|
.createInheritorsProcessor(parameters.getPosition(), type, 0, false, statVariants, baseClass, baseSubstitutor);
|
|
final StatisticsInfo[] statisticsInfos =
|
|
StatisticsManager.getInstance().getAllValues(JavaStatisticsManager.getAfterNewKey(type));
|
|
for (final StatisticsInfo statisticsInfo : statisticsInfos) {
|
|
final String value = statisticsInfo.getValue();
|
|
if (value.startsWith(JavaStatisticsManager.CLASS_PREFIX)) {
|
|
final String qname = value.substring(JavaStatisticsManager.CLASS_PREFIX.length());
|
|
final PsiClass psiClass = JavaPsiFacade.getInstance(file.getProject()).findClass(qname, file.getResolveScope());
|
|
if (psiClass != null && !PsiTreeUtil.isAncestor(file, psiClass, true) && !processor.process(psiClass)) break;
|
|
}
|
|
}
|
|
|
|
for (final PsiType variant : statVariants) {
|
|
consumer.consume(variant);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
//long
|
|
final Condition<String> shortNameCondition = new Condition<String>() {
|
|
public boolean value(String s) {
|
|
return matcher.prefixMatches(s);
|
|
}
|
|
};
|
|
for (final PsiClassType type : expectedClassTypes) {
|
|
final boolean shouldSearchForInheritors = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
|
|
public Boolean compute() {
|
|
final PsiClass psiClass = type.resolve();
|
|
return psiClass != null && psiClass.isValid() && !psiClass.hasModifierProperty(PsiModifier.FINAL);
|
|
}
|
|
}).booleanValue();
|
|
if (shouldSearchForInheritors) {
|
|
final Set<PsiType> psiTypes = CodeInsightUtil.addSubtypes(type, identifierCopy, false, shortNameCondition);
|
|
for (final PsiType psiType : psiTypes) {
|
|
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
|
public void run() {
|
|
if (psiType.isValid()) {
|
|
consumer.consume(psiType);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static ExpectedTypeInfo[] getExpectedTypes(final CompletionParameters parameters) {
|
|
final PsiElement position = parameters.getPosition();
|
|
if (psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent(PsiThrowStatement.class)).accepts(position)) {
|
|
final PsiElementFactory factory = JavaPsiFacade.getInstance(position.getProject()).getElementFactory();
|
|
final PsiClassType classType = factory
|
|
.createTypeByFQClassName(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, position.getResolveScope());
|
|
final List<ExpectedTypeInfo> result = new SmartList<ExpectedTypeInfo>();
|
|
result.add(new ExpectedTypeInfoImpl(classType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, 0, classType, TailType.SEMICOLON));
|
|
final PsiMethod method = PsiTreeUtil.getContextOfType(position, PsiMethod.class, true);
|
|
if (method != null) {
|
|
for (final PsiClassType type : method.getThrowsList().getReferencedTypes()) {
|
|
result.add(new ExpectedTypeInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, 0, type, TailType.SEMICOLON));
|
|
}
|
|
}
|
|
return result.toArray(new ExpectedTypeInfo[result.size()]);
|
|
}
|
|
|
|
PsiExpression expression = PsiTreeUtil.getContextOfType(position, PsiExpression.class, true);
|
|
if (expression == null) return ExpectedTypeInfo.EMPTY_ARRAY;
|
|
|
|
return ExpectedTypesProvider.getInstance(position.getProject()).getExpectedTypes(expression, true, parameters.getCompletionType() == CompletionType.SMART);
|
|
}
|
|
|
|
private static void addExpectedType(final CompletionResultSet result, final PsiType type, final CompletionParameters parameters, Collection<ExpectedTypeInfo> infos) {
|
|
if (!JavaCompletionUtil.hasAccessibleConstructor(type)) return;
|
|
|
|
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
|
|
if (psiClass == null) return;
|
|
|
|
final PsiClass parentClass = psiClass.getContainingClass();
|
|
if (parentClass != null && !psiClass.hasModifierProperty(PsiModifier.STATIC) &&
|
|
!PsiTreeUtil.isAncestor(parentClass, parameters.getPosition(), false) &&
|
|
!(parentClass.getContainingFile().equals(parameters.getOriginalFile()) &&
|
|
parentClass.getTextRange().contains(parameters.getOffset()))) {
|
|
return;
|
|
}
|
|
|
|
final LookupItem item = PsiTypeLookupItem.createLookupItem(JavaCompletionUtil.eliminateWildcards(type));
|
|
item.setAttribute(LookupItem.DONT_CHECK_FOR_INNERS, "");
|
|
JavaCompletionUtil.setShowFQN(item);
|
|
item.setAttribute(LookupItem.NEW_OBJECT_ATTR, "");
|
|
|
|
if (psiClass.isInterface() || psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
|
item.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
|
|
item.setAttribute(LookupItem.INDICATE_ANONYMOUS, "");
|
|
}
|
|
result.addElement(decorate(item, infos));
|
|
}
|
|
|
|
static Set<LookupElement> completeReference(final PsiElement element, PsiReference reference, final ElementFilter filter, final boolean acceptClasses, CompletionParameters parameters) {
|
|
if (reference instanceof PsiMultiReference) {
|
|
reference = ContainerUtil.findInstance(((PsiMultiReference) reference).getReferences(), PsiJavaReference.class);
|
|
}
|
|
|
|
if (reference instanceof PsiJavaReference) {
|
|
final PsiJavaReference javaReference = (PsiJavaReference)reference;
|
|
|
|
return JavaCompletionUtil.processJavaReference(element, javaReference, new ElementFilter() {
|
|
public boolean isAcceptable(Object element, PsiElement context) {
|
|
return filter.isAcceptable(element, context);
|
|
}
|
|
|
|
public boolean isClassAcceptable(Class hintClass) {
|
|
if (acceptClasses) {
|
|
return ReflectionCache.isAssignable(PsiClass.class, hintClass);
|
|
}
|
|
|
|
return ReflectionCache.isAssignable(PsiVariable.class, hintClass) ||
|
|
ReflectionCache.isAssignable(PsiMethod.class, hintClass) ||
|
|
ReflectionCache.isAssignable(PsiExpression.class, hintClass) ||
|
|
ReflectionCache.isAssignable(Template.class, hintClass) ||
|
|
ReflectionCache.isAssignable(CandidateInfo.class, hintClass) ||
|
|
ReflectionCache.isAssignable(PsiKeyword.class, hintClass);
|
|
}
|
|
}, true, null, parameters);
|
|
}
|
|
|
|
return Collections.emptySet();
|
|
}
|
|
|
|
@Override
|
|
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
|
|
if (context.getCompletionType() != CompletionType.SMART) {
|
|
return;
|
|
}
|
|
|
|
if (!context.getEditor().getSelectionModel().hasSelection()) {
|
|
final PsiFile file = context.getFile();
|
|
PsiElement element = file.findElementAt(context.getStartOffset());
|
|
if (element instanceof PsiIdentifier) {
|
|
element = element.getParent();
|
|
while (element instanceof PsiJavaCodeReferenceElement || element instanceof PsiMethodCallExpression ||
|
|
element instanceof PsiThisExpression || element instanceof PsiSuperExpression ||
|
|
element instanceof PsiTypeElement ||
|
|
element instanceof PsiClassObjectAccessExpression) {
|
|
context.getOffsetMap().addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, element.getTextRange().getEndOffset());
|
|
element = element.getParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
PsiElement lastElement = context.getFile().findElementAt(context.getStartOffset() - 1);
|
|
if (lastElement != null && lastElement.getText().equals("(")) {
|
|
final PsiElement parent = lastElement.getParent();
|
|
if (parent instanceof PsiTypeCastExpression) {
|
|
context.setFileCopyPatcher(new DummyIdentifierPatcher(""));
|
|
return;
|
|
}
|
|
if (parent instanceof PsiParenthesizedExpression) {
|
|
context.setFileCopyPatcher(new DummyIdentifierPatcher("xxx)yyy ")); // to handle type cast
|
|
return;
|
|
}
|
|
}
|
|
context.setFileCopyPatcher(new DummyIdentifierPatcher("xxx"));
|
|
}
|
|
|
|
}
|