Java: Provide completion for nested class names in reflection API (IDEA-181545)

This commit is contained in:
Pavel Dolgov
2017-11-09 13:06:59 +03:00
parent 925add59d2
commit 4dfff46920
7 changed files with 62 additions and 21 deletions
@@ -15,11 +15,13 @@
*/
package com.intellij.psi.impl.source.resolve.reference.impl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PsiJavaElementPattern;
import com.intellij.patterns.PsiJavaPatterns;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceSet;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -73,15 +75,7 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor
}
});
registrar.registerReferenceProvider(CLASS_PATTERN, new JavaReflectionReferenceProvider() {
@Override
protected PsiReference[] getReferencesByMethod(@NotNull PsiLiteralExpression literalArgument,
@NotNull PsiReferenceExpression methodReference,
@NotNull ProcessingContext context) {
return new JavaClassReferenceProvider().getReferencesByElement(literalArgument, context);
}
});
registrar.registerReferenceProvider(CLASS_PATTERN, new ReflectionClassNameReferenceProvider());
registrar.registerReferenceProvider(METHOD_HANDLE_PATTERN, new JavaLangInvokeHandleReference.JavaLangInvokeHandleReferenceProvider());
@@ -101,4 +95,26 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor
registrar.registerReferenceProvider(ATOMIC_NUMERIC_PATTERN, atomicProvider);
registrar.registerReferenceProvider(ATOMIC_REFERENCE_PATTERN, atomicProvider);
}
private static class ReflectionClassNameReferenceProvider extends JavaClassReferenceProvider {
ReflectionClassNameReferenceProvider() {
setOption(JavaClassReferenceProvider.ALLOW_DOLLAR_NAMES, Boolean.TRUE);
setOption(JavaClassReferenceProvider.JVM_FORMAT, Boolean.TRUE);
}
@NotNull
@Override
public PsiReference[] getReferencesByString(String str, @NotNull PsiElement position, int offsetInPosition) {
if (StringUtil.isEmpty(str)) {
return PsiReference.EMPTY_ARRAY;
}
return new JavaClassReferenceSet(str, position, offsetInPosition, true, this) {
@Override
public boolean isAllowDollarInNames() {
return true;
}
}.getAllReferences();
}
}
}
@@ -1,6 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class.forName("foo.bar.PublicClass.<caret>");
Class.forName("foo.bar.PublicClass$<caret>");
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class.forName("foo.bar.PublicClass$Nested<caret>");
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class.forName("foo.bar.PublicClass$NestedClass");
}
}
@@ -1,6 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class.forName("foo.bar.PublicClass.NestedClass");
Class.forName("foo.bar.PublicClass$NestedClass");
}
}
@@ -165,9 +165,17 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
doTest(0, "one", "two");
}
public void testClassForNameNested() {
public void testClassForNameNestedAutocomplete() {
myFixture.addClass("package foo.bar; public class PublicClass { public static class NestedClass {} }");
doTest(0, "NestedClass");
doTest(-1, () -> assertNull("Auto-completed", myFixture.getLookupElementStrings()));
}
public void testClassForNameNested() {
myFixture.addClass("package foo.bar; public class PublicClass {" +
" public static class NestedClass {}" +
" public class InnerClass {}" +
"}");
doTest(1, "InnerClass", "NestedClass");
}
public void testWithClassLoader() {
@@ -31,12 +31,12 @@ class JavaReflectionClassNavigationTest : JavaReflectionClassNavigationTestBase(
fun testPublicInnerClass() {
myFixture.addClass("package foo.bar; public class PublicClass { public class PublicInnerClass {} }")
doTest("foo.bar.PublicClass.PublicInnerClass")
doTest("foo.bar.PublicClass\$PublicInnerClass")
}
fun testPublicInnerClass2() {
myFixture.addClass("package foo.bar; public class PublicClass { public class PublicInnerClass {} }")
doTest("foo.bar.<caret>PublicClass.PublicInnerClass")
doTest("foo.bar.<caret>PublicClass\$PublicInnerClass")
}
fun testPackageLocalClass() {
@@ -46,17 +46,22 @@ class JavaReflectionClassNavigationTest : JavaReflectionClassNavigationTestBase(
fun testPrivateInnerClass() {
myFixture.addClass("package foo.bar; public class PublicClass { private class PrivateInnerClass {} }")
doTest("foo.bar.PublicClass.PrivateInnerClass")
doTest("foo.bar.PublicClass\$PrivateInnerClass")
}
fun testPrivateInnerClass2() {
myFixture.addClass("package foo.bar; public class PublicClass { private class PrivateInnerClass {} }")
doTest("foo.bar.<caret>PublicClass.PrivateInnerClass")
doTest("foo.bar.<caret>PublicClass\$PrivateInnerClass")
}
fun testWithClassLoader() {
myFixture.addClass("package foo.bar; public class PublicClass {}")
doTest("foo.bar.<caret>PublicClass.PrivateInnerClass", { "Thread.currentThread().getContextClassLoader().loadClass(\"$it\")" })
doTest("foo.bar.<caret>PublicClass\$PrivateInnerClass", { "Thread.currentThread().getContextClassLoader().loadClass(\"$it\")" })
}
fun testNestedClassDefaultPackage() {
myFixture.addClass("public class Outer { public static class Inner {} }")
doTest("Outer\$Inner")
}
}
@@ -73,11 +78,11 @@ abstract class JavaReflectionClassNavigationTestBase : LightCodeInsightFixtureTe
protected fun doTest(className: String, usageFormatter: (String) -> String = { "Class.forName(\"$it\")" }) {
val caretPos = className.indexOf("<caret>")
val atCaret: String
var expectedName = className
var expectedName = className.replace('$', '.')
if (caretPos >= 0) {
atCaret = className
val dotPos = className.indexOf(".", caretPos + 1)
if (dotPos >= 0) expectedName = className.substring(0, dotPos).replace("<caret>", "")
val dotPos = expectedName.indexOf(".", caretPos + 1)
if (dotPos >= 0) expectedName = expectedName.substring(0, dotPos).replace("<caret>", "")
}
else {
atCaret = className + "<caret>"