mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
Java: Added priorities in the completion list for arguments of getAnnotation() and getConstructor() (IDEA-167250)
This commit is contained in:
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.codeInsight.lookup.LookupValueWithPriority;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.patterns.PatternCondition;
|
||||
@@ -27,11 +28,11 @@ import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static com.intellij.codeInsight.completion.JavaCompletionContributor.isInJavaContext;
|
||||
import static com.intellij.patterns.PsiJavaPatterns.*;
|
||||
@@ -42,18 +43,27 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class JavaReflectionCompletionContributor extends CompletionContributor {
|
||||
private static final String CONSTRUCTOR = "getConstructor";
|
||||
private static final String DECLARED_CONSTRUCTOR = "getDeclaredConstructor";
|
||||
private static final String ANNOTATION = "getAnnotation";
|
||||
private static final String DECLARED_ANNOTATION = "getDeclaredAnnotation";
|
||||
private static final String ANNOTATIONS_BY_TYPE = "getAnnotationsByType";
|
||||
private static final String DECLARED_ANNOTATIONS_BY_TYPE = "getDeclaredAnnotationsByType";
|
||||
private static final String ANNOTATED_ELEMENT = "java.lang.reflect.AnnotatedElement";
|
||||
|
||||
private static final Set<String> DECLARED_NAMES =
|
||||
ContainerUtil.immutableSet(DECLARED_CONSTRUCTOR, DECLARED_ANNOTATION, DECLARED_ANNOTATIONS_BY_TYPE);
|
||||
private static final ElementPattern<? extends PsiElement> CONSTRUCTOR_ARGUMENTS = psiElement(PsiExpressionList.class)
|
||||
.withParent(psiExpression().methodCall(
|
||||
psiMethod()
|
||||
.withName("getConstructor", "getDeclaredConstructor")
|
||||
.withName(CONSTRUCTOR, DECLARED_CONSTRUCTOR)
|
||||
.definedInClass(CommonClassNames.JAVA_LANG_CLASS)));
|
||||
|
||||
private static final ElementPattern<? extends PsiElement> ANNOTATION_ARGUMENTS = psiElement(PsiExpressionList.class)
|
||||
.withParent(psiExpression().methodCall(
|
||||
psiMethod()
|
||||
.withName("getAnnotation", "getDeclaredAnnotation", "getAnnotationsByType", "getDeclaredAnnotationsByType")
|
||||
.with(new MethodDefinedInInterfacePatternCondition("java.lang.reflect.AnnotatedElement"))));
|
||||
.withName(ANNOTATION, DECLARED_ANNOTATION, ANNOTATIONS_BY_TYPE, DECLARED_ANNOTATIONS_BY_TYPE)
|
||||
.with(new MethodDefinedInInterfacePatternCondition(ANNOTATED_ELEMENT))));
|
||||
|
||||
private static final ElementPattern<PsiElement> BEGINNING_OF_CONSTRUCTOR_ARGUMENTS = beginningOfArguments(CONSTRUCTOR_ARGUMENTS);
|
||||
|
||||
@@ -79,28 +89,32 @@ public class JavaReflectionCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
|
||||
if (BEGINNING_OF_ANNOTATION_ARGUMENTS.accepts(position)) {
|
||||
PsiClass psiClass = getQualifierClass(position);
|
||||
if (psiClass != null) {
|
||||
addAnnotationClasses(psiClass, result);
|
||||
}
|
||||
addVariants(position, (psiClass, isDeclared) -> addAnnotationClasses(psiClass, isDeclared, result));
|
||||
//TODO handle annotations on fields and methods
|
||||
}
|
||||
if (BEGINNING_OF_CONSTRUCTOR_ARGUMENTS.accepts(position)) {
|
||||
PsiClass psiClass = getQualifierClass(position);
|
||||
else if (BEGINNING_OF_CONSTRUCTOR_ARGUMENTS.accepts(position)) {
|
||||
addVariants(position, (psiClass, isDeclared) -> addConstructorParameterTypes(psiClass, isDeclared, result));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addVariants(PsiElement position, BiConsumer<PsiClass, Boolean> variantAdder) {
|
||||
PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
|
||||
if (methodCall != null) {
|
||||
PsiClass psiClass = getReflectiveClass(methodCall.getMethodExpression().getQualifierExpression());
|
||||
if (psiClass != null) {
|
||||
addConstructorParameterTypes(psiClass, result);
|
||||
String methodName = methodCall.getMethodExpression().getReferenceName();
|
||||
if (methodName != null) {
|
||||
variantAdder.accept(psiClass, DECLARED_NAMES.contains(methodName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getQualifierClass(@Nullable PsiElement position) {
|
||||
PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
|
||||
return methodCall != null ? getReflectiveClass(methodCall.getMethodExpression().getQualifierExpression()) : null;
|
||||
}
|
||||
private static void addAnnotationClasses(@NotNull PsiClass psiClass, boolean isDeclared, @NotNull CompletionResultSet result) {
|
||||
Set<PsiAnnotation> declaredAnnotations =
|
||||
isDeclared ? ContainerUtil.set(AnnotationUtil.getAllAnnotations(psiClass, false, null, false)) : null;
|
||||
|
||||
private static void addAnnotationClasses(@NotNull PsiModifierListOwner annotationsOwner, @NotNull CompletionResultSet result) {
|
||||
PsiAnnotation[] annotations = AnnotationUtil.getAllAnnotations(annotationsOwner, true, null);
|
||||
PsiAnnotation[] annotations = AnnotationUtil.getAllAnnotations(psiClass, true, null, false);
|
||||
for (PsiAnnotation annotation : annotations) {
|
||||
PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
|
||||
if (referenceElement != null) {
|
||||
@@ -112,6 +126,9 @@ public class JavaReflectionCompletionContributor extends CompletionContributor {
|
||||
LookupElement lookupElement = LookupElementBuilder.createWithIcon(annotationClass)
|
||||
.withPresentableText(className + ".class")
|
||||
.withInsertHandler(JavaReflectionCompletionContributor::handleAnnotationClassInsertion);
|
||||
if (isDeclared) {
|
||||
lookupElement = withPriority(lookupElement, declaredAnnotations.contains(annotation));
|
||||
}
|
||||
result.addElement(lookupElement);
|
||||
}
|
||||
}
|
||||
@@ -119,26 +136,22 @@ public class JavaReflectionCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addConstructorParameterTypes(@NotNull PsiClass psiClass, @NotNull CompletionResultSet result) {
|
||||
private static void addConstructorParameterTypes(@NotNull PsiClass psiClass, boolean isDeclared, @NotNull CompletionResultSet result) {
|
||||
PsiMethod[] constructors = psiClass.getConstructors();
|
||||
if (constructors.length != 0) {
|
||||
for (PsiMethod constructor : constructors) {
|
||||
String parameterTypesText = Arrays.stream(constructor.getParameterList().getParameters())
|
||||
.map(p -> p.getType().getCanonicalText())
|
||||
.collect(Collectors.joining(",", constructor.getName() + "(", ")"));
|
||||
|
||||
LookupElement lookupElement = LookupElementBuilder.createWithIcon(constructor)
|
||||
.withPresentableText(parameterTypesText)
|
||||
.withInsertHandler(JavaReflectionCompletionContributor::handleConstructorSignatureInsertion);
|
||||
result.addElement(lookupElement);
|
||||
for (PsiMethod constructor : constructors) {
|
||||
LookupElement lookupElement = JavaLookupElementBuilder.forMethod(constructor, PsiSubstitutor.EMPTY)
|
||||
.withInsertHandler(JavaReflectionCompletionContributor::handleConstructorSignatureInsertion);
|
||||
if (isDeclared) {
|
||||
lookupElement = withPriority(lookupElement, isPublic(constructor));
|
||||
}
|
||||
result.addElement(lookupElement);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleAnnotationClassInsertion(@NotNull InsertionContext context, @NotNull LookupElement item) {
|
||||
Object object = item.getObject();
|
||||
if (object instanceof PsiClass) {
|
||||
String className = ((PsiClass)object).getName();
|
||||
String className = ((PsiClass)object).getQualifiedName();
|
||||
if (className != null) {
|
||||
handleParametersInsertion(context, className + ".class");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.intellij.psi.impl.source.resolve.reference.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.RecursionGuard;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
@@ -175,4 +177,9 @@ class JavaReflectionReferenceUtil {
|
||||
JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(methodCall.getArgumentList());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static LookupElement withPriority(LookupElement lookupElement, boolean hasPriority) {
|
||||
return PrioritizedLookupElement.withPriority(lookupElement, hasPriority ? 0 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(Bar.class);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotation(Bar.class);
|
||||
Test.class.getDeclaredAnnotation(Foo.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
More.class.getAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import foo.bar.*;
|
||||
import foo.baz.Baz;
|
||||
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
More.class.getAnnotation(Baz.class);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(Foo.class);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotation(Foo.class);
|
||||
Test.class.getDeclaredAnnotation(Bar.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import foo.bar.*;
|
||||
import foo.baz.Baz;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import foo.bar.*;
|
||||
import foo.baz.Baz;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(aType);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,9 @@
|
||||
package com.intellij.codeInsight.completion
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
|
||||
/**
|
||||
@@ -42,26 +44,36 @@ class JavaReflectionParametersCompletionTest : LightFixtureCompletionTestCase()
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase.JAVA_8
|
||||
|
||||
fun testAnnotation() = doTest(0, "Bar.class", "Foo.class", "aType")
|
||||
fun testAnnotation() = doTest(0, "Bar.class", "Foo.class")
|
||||
|
||||
fun testInheritedAnnotation() = doTest(1, "Bar.class", "Foo.class", "aType")
|
||||
fun testInheritedAnnotation() = doTest(1, "Bar.class", "Foo.class")
|
||||
|
||||
fun testDeclaredAnnotation() = doTest(0, "Bar.class", "Foo.class")
|
||||
fun testDeclaredAnnotation() = doTest(0, "Foo.class", "Bar.class")
|
||||
|
||||
fun testInheritedDeclaredAnnotation() = doTest(1, "Bar.class", "Foo.class")
|
||||
fun testInheritedDeclaredAnnotation() = doTest(1, "Foo.class", "Bar.class")
|
||||
|
||||
fun testAnnotationsByType() = doTest(0, "Bar.class", "Foo.class")
|
||||
|
||||
fun testDeclaredAnnotationsByType() = doTest(1, "Bar.class", "Foo.class")
|
||||
fun testDeclaredAnnotationsByType() = doTest(0, "Foo.class", "Bar.class")
|
||||
|
||||
fun testConstructor() {
|
||||
addConstructors()
|
||||
doTest(2, "Construct()", "Construct(int)", "Construct(int,java.lang.String)", "Construct(java.lang.String)")
|
||||
doTest(3, "Construct()", "Construct(int n)", "Construct(java.lang.String s)", "Construct(int n,java.lang.String s)")
|
||||
}
|
||||
|
||||
fun testDeclaredConstructor() {
|
||||
addConstructors()
|
||||
doTest(0, "Construct()", "Construct(int)", "Construct(int,java.lang.String)", "Construct(java.lang.String)")
|
||||
doTest(0, "Construct()", "Construct(int n,java.lang.String s)", "Construct(int n)", "Construct(java.lang.String s)")
|
||||
}
|
||||
|
||||
fun testImports() {
|
||||
addMoreClasses()
|
||||
doTest(1, "Bar.class", "Baz.class")
|
||||
}
|
||||
|
||||
fun testVariable() {
|
||||
addMoreClasses()
|
||||
doTest(2, "Bar.class", "Foo.class", "aType")
|
||||
}
|
||||
|
||||
private fun doTest(index: Int, vararg expected: String) {
|
||||
@@ -69,11 +81,7 @@ class JavaReflectionParametersCompletionTest : LightFixtureCompletionTestCase()
|
||||
configureByFile(getTestName(false) + ".java")
|
||||
|
||||
val lookupItems = lookup.items
|
||||
val texts = lookupItems.subList(0, Math.min(lookupItems.size, expected.size)).map {
|
||||
val presentation = LookupElementPresentation()
|
||||
it?.renderElement(presentation)
|
||||
presentation.itemText ?: ""
|
||||
}
|
||||
val texts = lookupItemTexts(lookupItems, expected.size)
|
||||
assertOrderedEquals(texts, *expected)
|
||||
selectItem(lookupItems[index])
|
||||
myFixture.checkResultByFile(getTestName(false) + "_after.java")
|
||||
@@ -82,9 +90,13 @@ class JavaReflectionParametersCompletionTest : LightFixtureCompletionTestCase()
|
||||
private fun addClasses() {
|
||||
myFixture.addClass("package foo.bar; public @interface Foo {}")
|
||||
myFixture.addClass("package foo.bar; public @interface Bar {}")
|
||||
myFixture.addClass("package foo.bar; public @interface Baz {}")
|
||||
myFixture.addClass("package foo.bar; @Foo class Parent {}")
|
||||
myFixture.addClass("package foo.bar; @Bar class Test extends Parent {}")
|
||||
myFixture.addClass("package foo.bar; @Bar class Parent {}")
|
||||
myFixture.addClass("package foo.bar; @Foo class Test extends Parent {}")
|
||||
}
|
||||
|
||||
private fun addMoreClasses() {
|
||||
myFixture.addClass("package foo.baz; public @interface Baz {}")
|
||||
myFixture.addClass("package foo.bar; @foo.baz.Baz class More extends Parent {}")
|
||||
}
|
||||
|
||||
private fun addConstructors() {
|
||||
@@ -96,4 +108,20 @@ public class Construct {
|
||||
public Construct() {}
|
||||
}""")
|
||||
}
|
||||
|
||||
}
|
||||
fun lookupItemTexts(lookupItems: List<LookupElement?>, maxSize: Int): List<String> =
|
||||
lookupItems.subList(0, Math.min(lookupItems.size, maxSize)).map {
|
||||
val obj = it?.`object`
|
||||
when (obj) {
|
||||
is PsiMethod -> {
|
||||
obj.name + obj.parameterList.parameters.map { it.type.canonicalText + " " + it.name }
|
||||
.joinToString(",", prefix = "(", postfix = ")")
|
||||
}
|
||||
else -> {
|
||||
val presentation = LookupElementPresentation()
|
||||
it?.renderElement(presentation)
|
||||
presentation.itemText ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user