IDEA-58766 Recognize dot as completion prefix part for chained call suggestions

This commit is contained in:
peter
2012-02-08 19:22:39 +01:00
parent 4c7e3a9fb0
commit 979191114e
7 changed files with 131 additions and 37 deletions
@@ -57,7 +57,8 @@ public class JavaClassNameCompletionContributor extends CompletionContributor {
}
CompletionResultSet result = _result.withPrefixMatcher(CompletionUtil.findReferenceOrAlphanumericPrefix(parameters));
addAllClasses(parameters, JavaCompletionSorting.addJavaSorting(parameters, result), parameters.getInvocationCount() <= 1, new Consumer<LookupElement>() {
addAllClasses(parameters, parameters.getInvocationCount() <= 1,
JavaCompletionSorting.addJavaSorting(parameters, result).getPrefixMatcher(), new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
_result.addElement(element);
@@ -68,8 +69,8 @@ public class JavaClassNameCompletionContributor extends CompletionContributor {
}
public static void addAllClasses(CompletionParameters parameters,
final CompletionResultSet result,
final boolean filterByScope,
@NotNull final PrefixMatcher matcher,
@NotNull final Consumer<LookupElement> consumer) {
final PsiElement insertedElement = parameters.getPosition();
@@ -110,7 +111,7 @@ public class JavaClassNameCompletionContributor extends CompletionContributor {
}
final boolean lookingForAnnotations = PsiJavaPatterns.psiElement().afterLeaf("@").accepts(insertedElement);
AllClassesGetter.processJavaClasses(parameters, result.getPrefixMatcher(), filterByScope, new Consumer<PsiClass>() {
AllClassesGetter.processJavaClasses(parameters, matcher, filterByScope, new Consumer<PsiClass>() {
@Override
public void consume(PsiClass psiClass) {
if (lookingForAnnotations && !psiClass.isAnnotationType()) return;
@@ -106,7 +106,7 @@ public class JavaCompletionContributor extends CompletionContributor {
withParent(or(psiElement(PsiResourceVariable.class), psiElement(PsiResourceList.class)))));
@Nullable
private static ElementFilter getReferenceFilter(PsiElement position) {
public static ElementFilter getReferenceFilter(PsiElement position) {
// Completion after extends in interface, type parameter and implements in class
final PsiClass containingClass = PsiTreeUtil.getParentOfType(position, PsiClass.class, false, PsiCodeBlock.class, PsiMethod.class, PsiExpressionList.class, PsiVariable.class);
if (containingClass != null && psiElement().afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS, ",", "&").accepts(position)) {
@@ -239,7 +239,7 @@ public class JavaCompletionContributor extends CompletionContributor {
if (!isClassNamePossible(parameters.getPosition()) || !mayStartClassName(result, parameters.isRelaxedMatching())) return;
if (mayShowAllClasses(parameters)) {
JavaClassNameCompletionContributor.addAllClasses(parameters, result, parameters.getInvocationCount() <= 2, new Consumer<LookupElement>() {
JavaClassNameCompletionContributor.addAllClasses(parameters, parameters.getInvocationCount() <= 2, result.getPrefixMatcher(), new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
if (!inheritors.alreadyProcessed(element)) {
@@ -15,10 +15,21 @@
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.util.CollectConsumer;
import com.intellij.util.Consumer;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author peter
*/
@@ -26,29 +37,80 @@ public class JavaNoVariantsDelegator extends NoVariantsDelegator {
@Override
protected void delegate(CompletionParameters parameters, CompletionResultSet result, Consumer<CompletionResult> passResult) {
if (parameters.getCompletionType() == CompletionType.BASIC &&
parameters.getInvocationCount() <= 1 &&
JavaCompletionContributor.mayStartClassName(result, false) &&
JavaCompletionContributor.isClassNamePossible(parameters.getPosition())) {
final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result);
if (parameters.getCompletionType() == CompletionType.BASIC) {
PsiElement position = parameters.getPosition();
if (parameters.getInvocationCount() <= 1 &&
JavaCompletionContributor.mayStartClassName(result, false) &&
JavaCompletionContributor.isClassNamePossible(position)) {
suggestNonImportedClasses(parameters, result);
return;
}
JavaClassNameCompletionContributor.addAllClasses(parameters, JavaCompletionSorting.addJavaSorting(parameters, result),
true, new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
JavaPsiClassReferenceElement classElement = element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY);
if (classElement != null) {
classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
PsiElement parent = position.getParent();
if (parent instanceof PsiJavaCodeReferenceElement) {
PsiElement qualifier = ((PsiJavaCodeReferenceElement)parent).getQualifier();
if (qualifier instanceof PsiJavaCodeReferenceElement &&
!((PsiJavaCodeReferenceElement)qualifier).isQualified() &&
((PsiJavaCodeReferenceElement)qualifier).resolve() == null) {
CompletionResultSet qualifiedCollector = result.withPrefixMatcher(position.getContainingFile().getText().substring(parent.getTextRange().getStartOffset(), parameters.getOffset()));
ElementFilter filter = JavaCompletionContributor.getReferenceFilter(position);
for (LookupElement base : suggestQualifierItems(parameters, result, (PsiJavaCodeReferenceElement)qualifier, filter)) {
PsiType type = JavaCompletionUtil.getLookupElementType(base);
if (type != null) {
PsiReferenceExpression ref = ReferenceExpressionCompletionContributor.createMockReference(position, type, base);
for (final LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters,
result.getPrefixMatcher())) {
qualifiedCollector.addElement(new JavaChainLookupElement(base, item));
}
}
}
merger.consume(classElement);
}
});
}
}
merger.finishedClassProcessing();
} else if (parameters.getCompletionType() == CompletionType.SMART && parameters.getInvocationCount() == 2) {
if (parameters.getCompletionType() == CompletionType.SMART && parameters.getInvocationCount() == 2) {
result.runRemainingContributors(parameters.withInvocationCount(3), passResult);
}
}
private static Set<LookupElement> suggestQualifierItems(CompletionParameters parameters,
CompletionResultSet result,
PsiJavaCodeReferenceElement qualifier,
ElementFilter filter) {
String referenceName = qualifier.getReferenceName();
if (referenceName == null) {
return Collections.emptySet();
}
PrefixMatcher qMatcher = new CamelHumpMatcher(referenceName);
Set<LookupElement> plainVariants =
JavaSmartCompletionContributor.completeReference(qualifier, qualifier, filter, true, true, parameters, qMatcher);
if (!plainVariants.isEmpty()) {
return plainVariants;
}
final Set<LookupElement> allClasses = new LinkedHashSet<LookupElement>();
JavaClassNameCompletionContributor.addAllClasses(parameters.withPosition(qualifier.getReferenceNameElement(), qualifier.getTextRange().getEndOffset()),
true, qMatcher, new CollectConsumer<LookupElement>(allClasses));
return allClasses;
}
private static void suggestNonImportedClasses(CompletionParameters parameters, CompletionResultSet result) {
final ClassByNameMerger merger = new ClassByNameMerger(parameters.getInvocationCount() == 0, result);
JavaClassNameCompletionContributor.addAllClasses(parameters,
true, JavaCompletionSorting.addJavaSorting(parameters, result).getPrefixMatcher(), new Consumer<LookupElement>() {
@Override
public void consume(LookupElement element) {
JavaPsiClassReferenceElement classElement = element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY);
if (classElement != null) {
classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
}
merger.consume(classElement);
}
});
merger.finishedClassProcessing();
}
}
@@ -155,14 +155,15 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
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)) {
for (final LookupElement item : completeReference(element, reference, filter, true, false, parameters, result.getPrefixMatcher())) {
if (item.getObject() instanceof PsiClass) {
result.addElement(decorate(LookupElementDecorator.withInsertHandler((LookupItem)item, ConstructorInsertHandler.SMART_INSTANCE), infos));
}
}
}
else if (INSIDE_TYPECAST_EXPRESSION.accepts(element)) {
for (final LookupElement item : completeReference(element, reference, new GeneratorFilter(AssignableToFilter.class, new CastTypeGetter()), false, parameters)) {
for (final LookupElement item : completeReference(element, reference, new GeneratorFilter(AssignableToFilter.class, new CastTypeGetter()), false, true, parameters,
result.getPrefixMatcher())) {
result.addElement(item);
}
}
@@ -182,7 +183,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
final PsiElement element = parameters.getPosition();
final PsiReference reference = element.getContainingFile().findReferenceAt(parameters.getOffset());
assert reference != null;
for (final LookupElement item : completeReference(element, reference, THROWABLES_FILTER, true, parameters)) {
for (final LookupElement item : completeReference(element, reference, THROWABLES_FILTER, true, false, parameters, result.getPrefixMatcher())) {
result.addElement(item);
}
}
@@ -456,7 +457,12 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
return ExpectedTypesProvider.getExpectedTypes(expression, true, parameters.getCompletionType() == CompletionType.SMART, false);
}
static Set<LookupElement> completeReference(final PsiElement element, PsiReference reference, final ElementFilter filter, final boolean acceptClasses, CompletionParameters parameters) {
static Set<LookupElement> completeReference(final PsiElement element,
PsiReference reference,
final ElementFilter filter,
final boolean acceptClasses,
final boolean acceptMembers,
CompletionParameters parameters, @Nullable final PrefixMatcher matcher) {
if (reference instanceof PsiMultiReference) {
reference = ContainerUtil.findInstance(((PsiMultiReference) reference).getReferences(), PsiJavaReference.class);
}
@@ -470,15 +476,18 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
}
public boolean isClassAcceptable(Class hintClass) {
if (acceptClasses) {
return ReflectionCache.isAssignable(PsiClass.class, hintClass);
if (ReflectionCache.isAssignable(PsiClass.class, hintClass)) {
return acceptClasses;
}
return ReflectionCache.isAssignable(PsiVariable.class, hintClass) ||
ReflectionCache.isAssignable(PsiMethod.class, hintClass) ||
ReflectionCache.isAssignable(CandidateInfo.class, hintClass);
if (ReflectionCache.isAssignable(PsiVariable.class, hintClass) ||
ReflectionCache.isAssignable(PsiMethod.class, hintClass) ||
ReflectionCache.isAssignable(CandidateInfo.class, hintClass)) {
return acceptMembers;
}
return false;
}
}, true, parameters.getInvocationCount() <= 1, null, parameters);
}, true, parameters.getInvocationCount() <= 1, matcher, parameters);
}
return Collections.emptySet();
@@ -122,7 +122,7 @@ public class ReferenceExpressionCompletionContributor {
final boolean secondTime = parameters.getParameters().getInvocationCount() >= 2;
final Set<LookupElement> base =
JavaSmartCompletionContributor.completeReference(element, reference, filter, false, parameters.getParameters());
JavaSmartCompletionContributor.completeReference(element, reference, filter, false, true, parameters.getParameters(), null);
for (final LookupElement item : base) {
addSingleArrayElementAccess(element, item, parameters, result);
}
@@ -177,7 +177,7 @@ public class ReferenceExpressionCompletionContributor {
public boolean isClassAcceptable(Class hintClass) {
return true;
}
}), false, parameters.getParameters());
}), false, true, parameters.getParameters(), null);
for (LookupElement lookupElement : elements) {
if (lookupElement.getObject() instanceof PsiMethod) {
final JavaMethodCallElement item = lookupElement.as(JavaMethodCallElement.CLASS_CONDITION_KEY);
@@ -419,7 +419,7 @@ public class ReferenceExpressionCompletionContributor {
final Consumer<LookupElement> result,
PsiType qualifierType,
final PsiType expectedType, JavaSmartCompletionParameters parameters) throws IncorrectOperationException {
final PsiReferenceExpression mockRef = createMockReference(place, qualifierType);
final PsiReferenceExpression mockRef = createMockReference(place, qualifierType, qualifierItem);
final ElementFilter filter = getReferenceFilter(place, true);
for (final LookupElement item : completeFinalReference(place, mockRef, filter, parameters)) {
@@ -429,7 +429,13 @@ public class ReferenceExpressionCompletionContributor {
}
}
private static PsiReferenceExpression createMockReference(PsiElement place, PsiType qualifierType) {
public static PsiReferenceExpression createMockReference(PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) {
if (qualifierItem.getObject() instanceof PsiClass) {
return (PsiReferenceExpression)JavaPsiFacade.getElementFactory(place.getProject())
.createExpressionFromText(((PsiClass)qualifierItem.getObject()).getQualifiedName() + ".xxx", place);
}
final JavaCodeFragmentFactory factory = JavaCodeFragmentFactory.getInstance(place.getProject());
PsiType varType = qualifierType;
if (varType instanceof PsiEllipsisType) {