java class reference: search subtypes only on smart completion, work as usual (and fast) on basic (IDEA-92934)

This commit is contained in:
peter
2012-10-16 16:15:58 +02:00
parent c13e9f5e3c
commit 30cdd2b4bc
11 changed files with 116 additions and 71 deletions
@@ -15,11 +15,15 @@
*/
package com.intellij.codeInsight.completion;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,24 +35,33 @@ public class JavaClassReferenceCompletionContributor extends CompletionContribut
public void duringCompletion(@NotNull CompletionInitializationContext context) {
JavaClassReference reference = findJavaClassReference(context.getFile(), context.getStartOffset());
if (reference != null && reference.getExtendClassNames() != null) {
final PsiReference[] references = reference.getJavaClassReferenceSet().getReferences();
final PsiReference last = references[references.length - 1];
context.setReplacementOffset(last.getRangeInElement().getEndOffset() + last.getElement().getTextRange().getStartOffset());
JavaClassReferenceSet set = reference.getJavaClassReferenceSet();
context.setReplacementOffset(set.getRangeInElement().getEndOffset() + set.getElement().getTextRange().getStartOffset());
}
}
@Override
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) {
if (parameters.getCompletionType() == CompletionType.SMART) {
return;
}
PsiElement position = parameters.getPosition();
JavaClassReference reference = findJavaClassReference(position.getContainingFile(), parameters.getOffset());
if (reference == null) {
return;
}
String[] extendClassNames = reference.getExtendClassNames();
PsiElement context = reference.getCompletionContext();
if (extendClassNames != null && context instanceof PsiPackage) {
if (parameters.getCompletionType() == CompletionType.SMART) {
JavaClassReferenceSet set = reference.getJavaClassReferenceSet();
int setStart = set.getRangeInElement().getStartOffset() + set.getElement().getTextRange().getStartOffset();
String fullPrefix = parameters.getPosition().getContainingFile().getText().substring(setStart, parameters.getOffset());
reference.processSubclassVariants((PsiPackage)context, extendClassNames, result.withPrefixMatcher(fullPrefix));
return;
}
result.addLookupAdvertisement("Press " + getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION) + " to see inheritors of " +
StringUtil.join(extendClassNames, ", "));
}
if (parameters.getCompletionType() == CompletionType.CLASS_NAME) {
JavaClassNameCompletionContributor.addAllClasses(parameters, result);
}
@@ -15,6 +15,7 @@
*/
package com.intellij.psi.impl.source.resolve.reference.impl.providers;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.daemon.QuickFixProvider;
@@ -23,6 +24,7 @@ import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl;
import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.LocalQuickFixProvider;
import com.intellij.lang.java.JavaLanguage;
@@ -55,7 +57,10 @@ import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author peter
@@ -225,15 +230,8 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
@Override
@NotNull
public Object[] getVariants() {
PsiElement context = getContext();
if (context == null) {
context = JavaPsiFacade.getInstance(getElement().getProject()).findPackage("");
}
PsiElement context = getCompletionContext();
if (context instanceof PsiPackage) {
final String[] extendClasses = getExtendClassNames();
if (extendClasses != null) {
return getSubclassVariants((PsiPackage)context, extendClasses);
}
return processPackage((PsiPackage)context);
}
if (context instanceof PsiClass) {
@@ -257,6 +255,12 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@NotNull
public PsiElement getCompletionContext() {
PsiElement context = getContext();
return context == null ? JavaPsiFacade.getInstance(getElement().getProject()).findPackage("") : context;
}
public String[] getExtendClassNames() {
return JavaClassReferenceProvider.EXTEND_CLASS_NAMES.getValue(getOptions());
}
@@ -508,8 +512,7 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
}
@NotNull
private Object[] getSubclassVariants(@NotNull PsiPackage context, @NotNull String[] extendClasses) {
HashSet<Object> lookups = new HashSet<Object>();
public void processSubclassVariants(@NotNull PsiPackage context, @NotNull String[] extendClasses, CompletionResultSet result) {
GlobalSearchScope packageScope = PackageScope.packageScope(context, true);
GlobalSearchScope scope = myJavaClassReferenceSet.getProvider().getScope(getElement().getProject());
if (scope != null) {
@@ -529,35 +532,22 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
// add itself
if (packageScope.contains(extendClass.getContainingFile().getVirtualFile())) {
if (isClassAccepted(extendClass, classKind, instantiatable, concrete, notInterface, notEnum)) {
ContainerUtil.addIfNotNull(createSubclassLookupValue(context, extendClass), lookups);
result.addElement(createSubclassLookupValue(extendClass, extendClassName));
}
}
for (final PsiClass clazz : ClassInheritorsSearch.search(extendClass, packageScope, true)) {
if (isClassAccepted(clazz, classKind, instantiatable, concrete, notInterface, notEnum)) {
ContainerUtil.addIfNotNull(createSubclassLookupValue(context, clazz), lookups);
String qname = clazz.getQualifiedName();
if (qname != null && isClassAccepted(clazz, classKind, instantiatable, concrete, notInterface, notEnum)) {
result.addElement(createSubclassLookupValue(clazz, qname));
}
}
}
}
return lookups.toArray();
}
@Nullable
private static Object createSubclassLookupValue(@NotNull final PsiPackage context, @NotNull final PsiClass clazz) {
final String qname = clazz.getQualifiedName();
if (qname == null) return null;
String name = qname;
final String pack = context.getQualifiedName();
if (pack.length() > 0) {
if (name.startsWith(pack)) {
name = name.substring(pack.length() + 1);
}
else {
return null;
}
}
return JavaLookupElementBuilder.forClass(clazz, name, true).withLookupString(qname).withLookupString(clazz.getName());
@NotNull
private static LookupElementBuilder createSubclassLookupValue(@NotNull final PsiClass clazz, @NotNull String qname) {
return JavaLookupElementBuilder.forClass(clazz, qname, true).withPresentableText(StringUtil.getShortName(qname));
}
@Override
@@ -59,6 +59,11 @@ public class JavaClassReferenceSet {
return myProvider;
}
public TextRange getRangeInElement() {
PsiReference[] references = getReferences();
return new TextRange(references[0].getRangeInElement().getStartOffset(), references[references.length - 1].getRangeInElement().getEndOffset());
}
private void reparse(String str, PsiElement element, final boolean isStaticImport, JavaClassReferenceSet context) {
myElement = element;
myContext = context;