make entering class literals in method calls easier during smart completion (IDEA-162394)

sort class literals by class proximity
process closest (imported) inheritors first
This commit is contained in:
peter
2018-05-07 10:19:33 +02:00
parent 89da2eca3d
commit 0607536f02
4 changed files with 44 additions and 5 deletions
@@ -26,6 +26,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.tree.IElementType;
@@ -326,10 +327,12 @@ public class CodeInsightUtil {
if (baseClass.hasModifierProperty(PsiModifier.FINAL)) return;
Set<PsiClass> imported = processImportedInheritors(context, baseClass, inheritorsProcessor);
if (matcher.getPrefix().length() > 2) {
JBTreeTraverser<PsiClass> traverser = JBTreeTraverser.of(PsiClass::getInnerClasses);
AllClassesGetter.processJavaClasses(matcher, context.getProject(), scope, psiClass -> {
Iterable<PsiClass> inheritors = traverser.withRoot(psiClass).filter(c -> c.isInheritor(baseClass, true));
Iterable<PsiClass> inheritors = traverser.withRoot(psiClass).filter(c -> c.isInheritor(baseClass, true) && !imported.contains(c));
return ContainerUtil.process(inheritors, inheritorsProcessor);
});
}
@@ -337,11 +340,28 @@ public class CodeInsightUtil {
Query<PsiClass> baseQuery = ClassInheritorsSearch.search(baseClass, scope, true, true, false);
Query<PsiClass> query = new FilteredQuery<>(baseQuery, psiClass ->
!(psiClass instanceof PsiTypeParameter) &&
ContainerUtil.exists(JavaCompletionUtil.getAllLookupStrings(psiClass), matcher::prefixMatches));
ContainerUtil.exists(JavaCompletionUtil.getAllLookupStrings(psiClass), matcher::prefixMatches) &&
!imported.contains(psiClass));
query.forEach(inheritorsProcessor);
}
}
@NotNull
private static Set<PsiClass> processImportedInheritors(PsiElement context, PsiClass baseClass, Processor<PsiClass> inheritorsProcessor) {
Set<PsiClass> visited = new HashSet<>();
context.getContainingFile().getOriginalFile().processDeclarations(new PsiScopeProcessor() {
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
if (element instanceof PsiClass && ((PsiClass)element).isInheritor(baseClass, true) && visited.add((PsiClass)element)) {
return inheritorsProcessor.process((PsiClass)element);
}
return true;
}
}, ResolveState.initial(), null, context);
return visited;
}
private static void addContextTypeArguments(PsiElement context, PsiClassType baseType, Processor<PsiClass> inheritorsProcessor) {
Set<String> usedNames = ContainerUtil.newHashSet();
PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject());
@@ -32,13 +32,17 @@ import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
public class ClassLiteralLookupElement extends LookupElement implements TypedLookupItem {
class ClassLiteralLookupElement extends LookupElement implements TypedLookupItem {
@NonNls private static final String DOT_CLASS = ".class";
@Nullable private final SmartPsiElementPointer<PsiClass> myClass;
private final PsiExpression myExpr;
private final String myPresentableText;
private final String myCanonicalText;
public ClassLiteralLookupElement(PsiClassType type, PsiElement context) {
ClassLiteralLookupElement(PsiClassType type, PsiElement context) {
PsiClass psiClass = PsiUtil.resolveClassInType(type);
myClass = psiClass == null ? null : SmartPointerManager.createPointer(psiClass);
myCanonicalText = type.getCanonicalText();
myPresentableText = type.getPresentableText();
myExpr = JavaPsiFacade.getInstance(context.getProject()).getElementFactory().createExpressionFromText(myCanonicalText + DOT_CLASS, context);
@@ -63,7 +67,7 @@ public class ClassLiteralLookupElement extends LookupElement implements TypedLoo
@Nullable
@Override
public PsiElement getPsiElement() {
return PsiUtil.resolveClassInType(getType());
return myClass == null ? null : myClass.getElement();
}
@NotNull
@@ -0,0 +1,11 @@
class X {
<T> void foo(Class<T> c) {
}
{
foo(Str<caret>)
}
}
@@ -275,4 +275,8 @@ public void testConvertToObjectStream() {
myFixture.assertPreferredCompletionItems(2, "SortedMap", "NavigableMap", "TreeMap", "ConcurrentNavigableMap", "ConcurrentSkipListMap");
}
public void testConsiderClassProximityForClassLiterals() {
configureByTestName();
myFixture.assertPreferredCompletionItems(0, "String.class");
}
}