mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: Implemented completion assistance for Var/Method Handle - suggest field/method name, autocomplete the signature (IDEA-167319)
This commit is contained in:
+14
-27
@@ -21,11 +21,10 @@ import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -58,10 +57,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
Object value = myElement.getValue();
|
||||
final Object value = myElement.getValue();
|
||||
if (value instanceof String) {
|
||||
final String name = (String)value;
|
||||
final String type = getMemberType();
|
||||
final String type = getMemberType(myElement);
|
||||
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getPsiClass();
|
||||
@@ -73,7 +72,7 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
}
|
||||
|
||||
case DECLARED_FIELD: {
|
||||
PsiField field = psiClass.findFieldByName(name, false);
|
||||
final PsiField field = psiClass.findFieldByName(name, false);
|
||||
return isPotentiallyAccessible(field, psiClass) ? field : null;
|
||||
}
|
||||
|
||||
@@ -102,16 +101,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
return getReflectiveClass(myContext);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getMemberType() {
|
||||
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class);
|
||||
return methodCall != null ? methodCall.getMethodExpression().getReferenceName() : null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final String type = getMemberType();
|
||||
final String type = getMemberType(myElement);
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getPsiClass();
|
||||
if (psiClass != null) {
|
||||
@@ -155,13 +148,13 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
private static int getMethodSortOrder(PsiMethod method) {
|
||||
return isJavaLangObject(method.getContainingClass()) ? 1 : isPublic(method) ? -1 : 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LookupElement lookupField(PsiField field) {
|
||||
return JavaLookupElementBuilder.forField(field);
|
||||
/**
|
||||
* Non-public members of superclass/superinterface can't be obtained via reflection, they need to be filtered out.
|
||||
*/
|
||||
@Contract("null, _ -> false")
|
||||
private static boolean isPotentiallyAccessible(PsiMember member, PsiClass psiClass) {
|
||||
return member != null && (member.getContainingClass() == psiClass || isPublic(member));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -173,15 +166,9 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
public void handleInsert(InsertionContext context, LookupElement item) {
|
||||
final Object object = item.getObject();
|
||||
if (object instanceof PsiMethod) {
|
||||
final PsiElement newElement = PsiUtilCore.getElementAtOffset(context.getFile(), context.getStartOffset());
|
||||
final int start = newElement.getTextRange().getEndOffset();
|
||||
final PsiElement params = newElement.getParent().getParent();
|
||||
final int end = params.getTextRange().getEndOffset() - 1;
|
||||
String types = getParameterTypesText((PsiMethod)object);
|
||||
if (!types.isEmpty()) types = ", " + types;
|
||||
context.getDocument().replaceString(start, end, types);
|
||||
context.commitDocument();
|
||||
shortenArgumentsClassReferences(context);
|
||||
String text = getParameterTypesText((PsiMethod)object);
|
||||
|
||||
replaceText(context, text.isEmpty() ? "" : ", " + text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.psi.impl.source.resolve.reference.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertHandler;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*;
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralExpression> implements InsertHandler<LookupElement> {
|
||||
static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup";
|
||||
static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType";
|
||||
|
||||
static final String FIND_VIRTUAL = "findVirtual";
|
||||
static final String FIND_STATIC = "findStatic";
|
||||
static final String FIND_SPECIAL = "findSpecial";
|
||||
|
||||
static final String FIND_GETTER = "findGetter";
|
||||
static final String FIND_SETTER = "findSetter";
|
||||
static final String FIND_STATIC_GETTER = "findStaticGetter";
|
||||
static final String FIND_STATIC_SETTER = "findStaticSetter";
|
||||
|
||||
static final String FIND_VAR_HANDLE = "findVarHandle";
|
||||
static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle";
|
||||
|
||||
private final PsiExpression myContext;
|
||||
|
||||
public JavaLangInvokeHandleReference(@NotNull PsiLiteralExpression literal, @NotNull PsiExpression context) {
|
||||
super(literal);
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
final Object value = myElement.getValue();
|
||||
if (value instanceof String) {
|
||||
final String name = (String)value;
|
||||
final String type = getMemberType(myElement);
|
||||
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getReflectiveClass(myContext);
|
||||
if (psiClass != null) {
|
||||
switch (type) {
|
||||
case FIND_GETTER:
|
||||
case FIND_SETTER:
|
||||
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isNonStaticField);
|
||||
|
||||
case FIND_STATIC_GETTER:
|
||||
case FIND_STATIC_SETTER:
|
||||
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isStaticField);
|
||||
|
||||
case FIND_VIRTUAL:
|
||||
return resolveMethod(name, psiClass, JavaLangInvokeHandleReference::isNonStaticMethod);
|
||||
case FIND_STATIC:
|
||||
return resolveMethod(name, psiClass, JavaLangInvokeHandleReference::isStaticMethod);
|
||||
|
||||
case FIND_VAR_HANDLE:
|
||||
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isNonStaticField);
|
||||
|
||||
case FIND_STATIC_VAR_HANDLE:
|
||||
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isStaticField);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiElement resolveField(@NotNull String name, @NotNull PsiClass psiClass, Condition<? super PsiField> filter) {
|
||||
final PsiField field = psiClass.findFieldByName(name, true);
|
||||
return field != null && filter.value(field) ? field : null;
|
||||
}
|
||||
|
||||
private static PsiElement resolveMethod(@NotNull String name, @NotNull PsiClass psiClass, Condition<? super PsiMethod> filter) {
|
||||
final PsiMethod[] methods = psiClass.findMethodsByName(name, true);
|
||||
return ContainerUtil.find(methods, filter);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final Object value = myElement.getValue();
|
||||
if (value instanceof String) {
|
||||
final String type = getMemberType(myElement);
|
||||
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getReflectiveClass(myContext);
|
||||
if (psiClass != null) {
|
||||
switch (type) {
|
||||
case FIND_GETTER:
|
||||
case FIND_SETTER:
|
||||
return lookupFields(psiClass, JavaLangInvokeHandleReference::isNonStaticField);
|
||||
case FIND_STATIC_GETTER:
|
||||
case FIND_STATIC_SETTER:
|
||||
return lookupFields(psiClass, JavaLangInvokeHandleReference::isStaticField);
|
||||
|
||||
case FIND_VIRTUAL:
|
||||
return lookupMethods(psiClass, JavaLangInvokeHandleReference::isNonStaticMethod);
|
||||
case FIND_STATIC:
|
||||
return lookupMethods(psiClass, JavaLangInvokeHandleReference::isStaticMethod);
|
||||
|
||||
case FIND_VAR_HANDLE:
|
||||
return lookupFields(psiClass, JavaLangInvokeHandleReference::isNonStaticField);
|
||||
case FIND_STATIC_VAR_HANDLE:
|
||||
return lookupFields(psiClass, JavaLangInvokeHandleReference::isStaticField);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
private Object[] lookupMethods(@NotNull PsiClass psiClass, Predicate<? super PsiMethod> filter) {
|
||||
return psiClass.getVisibleSignatures()
|
||||
.stream()
|
||||
.map(MethodSignatureBackedByPsiMethod::getMethod)
|
||||
.filter(filter)
|
||||
.sorted(Comparator.comparingInt((PsiMethod method) -> getMethodSortOrder(method)).thenComparing(PsiMethod::getName))
|
||||
.map(method -> withPriority(JavaLookupElementBuilder.forMethod(method, PsiSubstitutor.EMPTY)
|
||||
.withInsertHandler(this),
|
||||
-getMethodSortOrder(method)))
|
||||
.toArray();
|
||||
}
|
||||
|
||||
private Object[] lookupFields(@NotNull PsiClass psiClass, Predicate<? super PsiField> filter) {
|
||||
final Set<String> uniqueNames = new THashSet<>();
|
||||
return Arrays.stream(psiClass.getAllFields())
|
||||
.filter(field -> field != null &&
|
||||
(field.getContainingClass() == psiClass || !field.hasModifierProperty(PsiModifier.PRIVATE)) &&
|
||||
field.getName() != null && uniqueNames.add(field.getName()))
|
||||
.filter(filter)
|
||||
.sorted(Comparator.comparing((PsiField field) -> isPublic(field) ? 0 : 1).thenComparing(PsiField::getName))
|
||||
.map(field -> withPriority(JavaLookupElementBuilder.forField(field).withInsertHandler(this), isPublic(field)))
|
||||
.toArray();
|
||||
}
|
||||
|
||||
private static boolean isNonStaticField(PsiField field) {
|
||||
return field != null && !field.hasModifierProperty(PsiModifier.STATIC);
|
||||
}
|
||||
|
||||
private static boolean isStaticField(PsiField field) {
|
||||
return field != null && field.hasModifierProperty(PsiModifier.STATIC);
|
||||
}
|
||||
|
||||
private static boolean isNonStaticMethod(@Nullable PsiMethod method) {
|
||||
return isRegularMethod(method) && !method.hasModifierProperty(PsiModifier.STATIC);
|
||||
}
|
||||
|
||||
private static boolean isStaticMethod(@Nullable PsiMethod method) {
|
||||
return isRegularMethod(method) && method.hasModifierProperty(PsiModifier.STATIC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) {
|
||||
final Object object = item.getObject();
|
||||
|
||||
if (object instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)object;
|
||||
final Stream<PsiType> returnType = Stream.of(method.getReturnType())
|
||||
.map(type -> type != null ? type : PsiType.VOID);
|
||||
final Stream<PsiType> parametersTypes = Arrays.stream(method.getParameterList().getParameters())
|
||||
.map(parameter -> parameter.getType());
|
||||
|
||||
final String types = Stream.concat(returnType, parametersTypes)
|
||||
.map(type -> TypeConversionUtil.erasure(type))
|
||||
.map(type -> (type instanceof PsiEllipsisType) ? new PsiArrayType(((PsiEllipsisType)type).getComponentType()) : type)
|
||||
.map(type -> type.getPresentableText() + ".class")
|
||||
.collect(Collectors.joining(", "));
|
||||
final String text = ", " + JAVA_LANG_INVOKE_METHOD_TYPE + ".methodType(" + types + ")";
|
||||
|
||||
replaceText(context, text);
|
||||
}
|
||||
else if (object instanceof PsiField) {
|
||||
final PsiField field = (PsiField)object;
|
||||
final PsiType type = TypeConversionUtil.erasure(field.getType());
|
||||
final String text = ", " + type.getCanonicalText() + ".class";
|
||||
|
||||
replaceText(context, text);
|
||||
}
|
||||
}
|
||||
|
||||
static class JavaLangInvokeHandleReferenceProvider extends PsiReferenceProvider {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
|
||||
if (element instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression literal = (PsiLiteralExpression)element;
|
||||
if (literal.getValue() instanceof String) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final PsiExpression[] expressions = ((PsiExpressionList)parent).getExpressions();
|
||||
final PsiExpression qualifier = expressions.length != 0 ? expressions[0] : null;
|
||||
if (qualifier != null) {
|
||||
return new PsiReference[]{new JavaLangInvokeHandleReference(literal, qualifier)};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return PsiReference.EMPTY_ARRAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.source.resolve.reference.impl;
|
||||
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.patterns.PsiJavaElementPattern;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
|
||||
@@ -27,6 +28,7 @@ import static com.intellij.patterns.PsiJavaPatterns.psiMethod;
|
||||
import static com.intellij.patterns.StandardPatterns.or;
|
||||
import static com.intellij.patterns.StandardPatterns.string;
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_CLASS;
|
||||
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaLangInvokeHandleReference.*;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
@@ -44,6 +46,14 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor
|
||||
psiMethod().withName(string().equalTo("forName")).definedInClass(JAVA_LANG_CLASS),
|
||||
psiMethod().withName(string().equalTo("loadClass")).definedInClass("java.lang.ClassLoader")));
|
||||
|
||||
private static final ElementPattern<? extends PsiElement> METHOD_HANDLE_PATTERN = psiLiteral()
|
||||
.methodCallParameter(1, psiMethod()
|
||||
.withName(FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL,
|
||||
FIND_GETTER, FIND_SETTER,
|
||||
FIND_STATIC_GETTER, FIND_STATIC_SETTER,
|
||||
FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE)
|
||||
.definedInClass(JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP));
|
||||
|
||||
@Override
|
||||
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
|
||||
registrar.registerReferenceProvider(PATTERN, new JavaReflectionReferenceProvider() {
|
||||
@@ -72,5 +82,7 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
registrar.registerReferenceProvider(METHOD_HANDLE_PATTERN, new JavaLangInvokeHandleReferenceProvider());
|
||||
}
|
||||
}
|
||||
|
||||
+36
-14
@@ -16,6 +16,7 @@
|
||||
package com.intellij.psi.impl.source.resolve.reference.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
|
||||
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -45,8 +46,11 @@ class JavaReflectionReferenceUtil {
|
||||
private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference");
|
||||
|
||||
@Nullable
|
||||
static PsiClass getReflectiveClass(PsiExpression context) {
|
||||
static PsiClass getReflectiveClass(@Nullable PsiExpression context) {
|
||||
context = ParenthesesUtils.stripParentheses(context);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
if (context instanceof PsiClassObjectAccessExpression) { // special case for JDK 1.4
|
||||
PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
|
||||
return PsiTypesUtil.getPsiClass(operand.getType());
|
||||
@@ -136,27 +140,19 @@ class JavaReflectionReferenceUtil {
|
||||
return DeclarationSearchUtils.findDefinition(referenceExpression, variable);
|
||||
}
|
||||
|
||||
static boolean isJavaLangClass(PsiClass aClass) {
|
||||
static boolean isJavaLangClass(@Nullable PsiClass aClass) {
|
||||
return aClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(aClass.getQualifiedName());
|
||||
}
|
||||
|
||||
static boolean isJavaLangObject(PsiClass aClass) {
|
||||
static boolean isJavaLangObject(@Nullable PsiClass aClass) {
|
||||
return aClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName());
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
static boolean isRegularMethod(PsiMethod method) {
|
||||
static boolean isRegularMethod(@Nullable PsiMethod method) {
|
||||
return method != null && !method.isConstructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-public members of superclass/superinterface can't be obtained via reflection, they need to be filtered out.
|
||||
*/
|
||||
@Contract("null, _ -> false")
|
||||
static boolean isPotentiallyAccessible(PsiMember member, PsiClass psiClass) {
|
||||
return member != null && (member.getContainingClass() == psiClass || isPublic(member));
|
||||
}
|
||||
|
||||
static boolean isPublic(@NotNull PsiMember member) {
|
||||
return member.hasModifierProperty(PsiModifier.PUBLIC);
|
||||
}
|
||||
@@ -179,12 +175,38 @@ class JavaReflectionReferenceUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static LookupElement withPriority(LookupElement lookupElement, boolean hasPriority) {
|
||||
static LookupElement withPriority(@NotNull LookupElement lookupElement, boolean hasPriority) {
|
||||
return hasPriority ? lookupElement : PrioritizedLookupElement.withPriority(lookupElement, -1);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static LookupElement withPriority(LookupElement lookupElement, int priority) {
|
||||
static LookupElement withPriority(@NotNull LookupElement lookupElement, int priority) {
|
||||
return priority == 0 ? lookupElement : PrioritizedLookupElement.withPriority(lookupElement, priority);
|
||||
}
|
||||
|
||||
static int getMethodSortOrder(@NotNull PsiMethod method) {
|
||||
return isJavaLangObject(method.getContainingClass()) ? 1 : isPublic(method) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getMemberType(@Nullable PsiElement element) {
|
||||
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
return methodCall != null ? methodCall.getMethodExpression().getReferenceName() : null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static LookupElement lookupField(@NotNull PsiField field) {
|
||||
return JavaLookupElementBuilder.forField(field);
|
||||
}
|
||||
|
||||
static void replaceText(@NotNull InsertionContext context, @NotNull String text) {
|
||||
final PsiElement newElement = PsiUtilCore.getElementAtOffset(context.getFile(), context.getStartOffset());
|
||||
final int start = newElement.getTextRange().getEndOffset();
|
||||
final PsiElement params = newElement.getParent().getParent();
|
||||
final int end = params.getTextRange().getEndOffset() - 1;
|
||||
|
||||
context.getDocument().replaceString(start, end, text);
|
||||
context.commitDocument();
|
||||
shortenArgumentsClassReferences(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findGetter(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findGetter(Test.class, "f1", int.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findSetter(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findSetter(Test.class, "f2", float.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStatic(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticGetter(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticGetter(Test.class, "psf1", char.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticSetter(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticSetter(Test.class, "sf2", short.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticVarHandle(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticVarHandle(Test.class, "psf1");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStatic(Test.class, "psm1", MethodType.methodType(void.class, char.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVarHandle(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVarHandle(Test.class, "f1");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Test.class, "<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Test.class, "m<caret>");
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Test.class, "m2", MethodType.methodType(void.class, float.class, double.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Test.class, "pm1", MethodType.methodType(void.class, int.class));
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.completion
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
|
||||
/**
|
||||
* @author Pavel Dolgov
|
||||
*/
|
||||
class JavaLangInvokeHandleCompletionTest : LightFixtureCompletionTestCase() {
|
||||
|
||||
fun testVirtual() = doTestFirst(1, "m1", "pm1", "m2")
|
||||
fun testVirtualPrefixed() = doTest(1, "m1", "m2", "pm1")
|
||||
|
||||
fun testStatic() = doTest(0, "psm1", "sm1", "sm2")
|
||||
|
||||
fun testGetter() = doTest(0, "f1", "pf1", "f2")
|
||||
fun testSetter() = doTest(2, "f1", "pf1", "f2")
|
||||
|
||||
fun testStaticGetter() = doTest(0, "psf1", "sf1", "sf2")
|
||||
fun testStaticSetter() = doTest(2, "psf1", "sf1", "sf2")
|
||||
|
||||
// TODO enable when the mock for jdk9 is available
|
||||
fun _testVarHandle() = doTest(0, "f1", "pf1", "f2")
|
||||
fun _testStaticVarHandle() = doTest(0, "psf1", "sf1", "sf2")
|
||||
|
||||
override fun getBasePath(): String {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/invokeHandle/"
|
||||
}
|
||||
|
||||
|
||||
private fun doTest(index: Int, vararg expected: String) {
|
||||
doTest(index, { assertStringItems(*expected) })
|
||||
}
|
||||
|
||||
private fun doTestFirst(index: Int, vararg expected: String) {
|
||||
doTest(index, { assertFirstStringItems(*expected, "clone") })
|
||||
}
|
||||
|
||||
private fun doTest(index: Int, assertion: () -> Unit) {
|
||||
myFixture.addClass("""
|
||||
public class Parent {
|
||||
public int pf1;
|
||||
private float pf2;
|
||||
public static char psf1;
|
||||
private static short psf2;
|
||||
|
||||
public void pm1(int n) {}
|
||||
private void pm2(float n, double m) {}
|
||||
public static void psm1(char n) {}
|
||||
private static void psm2(short n) {}
|
||||
}""")
|
||||
myFixture.addClass("""
|
||||
public class Test extends Parent {
|
||||
public int f1;
|
||||
private float f2;
|
||||
public static char sf1;
|
||||
private static short sf2;
|
||||
|
||||
public void m1(int n) {}
|
||||
private void m2(float n, double m) {}
|
||||
public static void sm1(char n) {}
|
||||
private static void sm2(short n) {}
|
||||
}""")
|
||||
|
||||
configureByFile(getTestName(false) + ".java")
|
||||
assertion()
|
||||
if (index >= 0) selectItem(lookup.items[index])
|
||||
myFixture.checkResultByFile(getTestName(false) + "_after.java")
|
||||
}
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.navigation
|
||||
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import junit.framework.TestCase
|
||||
import org.intellij.lang.annotations.MagicConstant
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() {
|
||||
|
||||
fun testVirtual1() = doTest("m1", VIRTUAL)
|
||||
fun testVirtual2() = doTest("m2", VIRTUAL)
|
||||
fun testVirtual3() = doTest("pm1", VIRTUAL)
|
||||
fun testVirtual4() = doTest("pm2", VIRTUAL)
|
||||
fun testVirtual5() = doNegativeTest("sm1", VIRTUAL)
|
||||
fun testVirtual6() = doNegativeTest("psm1", VIRTUAL)
|
||||
fun testVirtual7() = doNegativeTest("f1", VIRTUAL)
|
||||
|
||||
fun testStatic1() = doTest("sm1", STATIC)
|
||||
fun testStatic2() = doTest("sm2", STATIC)
|
||||
fun testStatic3() = doTest("psm1", STATIC)
|
||||
fun testStatic4() = doTest("psm2", STATIC)
|
||||
fun testStatic5() = doNegativeTest("m1", STATIC)
|
||||
fun testStatic6() = doNegativeTest("pm1", STATIC)
|
||||
fun testStatic7() = doNegativeTest("f1", STATIC)
|
||||
|
||||
fun testGetter1() = doTest("f1", GETTER)
|
||||
fun testGetter2() = doTest("f2", GETTER)
|
||||
fun testGetter3() = doTest("pf1", GETTER)
|
||||
fun testGetter4() = doTest("pf2", GETTER)
|
||||
fun testGetter5() = doNegativeTest("sf1", GETTER)
|
||||
fun testGetter6() = doNegativeTest("psf1", GETTER)
|
||||
fun testGetter7() = doNegativeTest("m1", GETTER)
|
||||
|
||||
fun testSetter1() = doTest("f1", SETTER)
|
||||
fun testSetter2() = doTest("f2", SETTER)
|
||||
fun testSetter3() = doTest("pf1", SETTER)
|
||||
fun testSetter4() = doTest("pf2", SETTER)
|
||||
fun testSetter5() = doNegativeTest("sf1", SETTER)
|
||||
fun testSetter6() = doNegativeTest("psf1", SETTER)
|
||||
fun testSetter7() = doNegativeTest("m1", SETTER)
|
||||
|
||||
fun testStaticGetter1() = doTest("sf1", STATIC_GETTER)
|
||||
fun testStaticGetter2() = doTest("sf2", STATIC_GETTER)
|
||||
fun testStaticGetter3() = doTest("psf1", STATIC_GETTER)
|
||||
fun testStaticGetter4() = doTest("psf2", STATIC_GETTER)
|
||||
fun testStaticGetter5() = doNegativeTest("f1", STATIC_GETTER)
|
||||
fun testStaticGetter6() = doNegativeTest("pf1", STATIC_GETTER)
|
||||
fun testStaticGetter7() = doNegativeTest("m1", STATIC_GETTER)
|
||||
|
||||
fun testStaticSetter1() = doTest("sf1", STATIC_SETTER)
|
||||
fun testStaticSetter2() = doTest("sf2", STATIC_SETTER)
|
||||
fun testStaticSetter3() = doTest("psf1", STATIC_SETTER)
|
||||
fun testStaticSetter4() = doTest("psf2", STATIC_SETTER)
|
||||
fun testStaticSetter5() = doNegativeTest("f1", STATIC_SETTER)
|
||||
fun testStaticSetter6() = doNegativeTest("pf1", STATIC_SETTER)
|
||||
fun testStaticSetter7() = doNegativeTest("m1", STATIC_SETTER)
|
||||
|
||||
|
||||
private fun doTest(name: String,
|
||||
@MagicConstant(stringValues = arrayOf(VIRTUAL, STATIC, SPECIAL,
|
||||
GETTER, SETTER,
|
||||
STATIC_GETTER, STATIC_SETTER,
|
||||
VAR_HANDLE, STATIC_VAR_HANDLE)) function: String) {
|
||||
doTestImpl(name, getMainClassText(name, function))
|
||||
}
|
||||
|
||||
private fun doTestImpl(name: String, mainClassText: String) {
|
||||
val reference = getReference(mainClassText)
|
||||
TestCase.assertEquals("Reference text", name, reference.canonicalText)
|
||||
val resolved = reference.resolve()
|
||||
TestCase.assertNotNull("Reference is not resolved: " + reference.canonicalText, resolved)
|
||||
TestCase.assertTrue("Target is a member", resolved is PsiMember)
|
||||
val member = resolved as PsiMember?
|
||||
TestCase.assertEquals("Target name", name, member!!.name)
|
||||
}
|
||||
|
||||
private fun doNegativeTest(name: String,
|
||||
@MagicConstant(stringValues = arrayOf(VIRTUAL, STATIC, SPECIAL,
|
||||
GETTER, SETTER,
|
||||
STATIC_GETTER, STATIC_SETTER,
|
||||
VAR_HANDLE, STATIC_VAR_HANDLE)) function: String) {
|
||||
val reference = getReference(getMainClassText(name, function))
|
||||
TestCase.assertEquals("Reference text", name, reference.canonicalText)
|
||||
val resolved = reference.resolve()
|
||||
TestCase.assertNull("Reference shouldn't resolve: " + reference.canonicalText, resolved)
|
||||
}
|
||||
|
||||
private fun getReference(mainClassText: String): PsiReference {
|
||||
myFixture.addClass("""package foo.bar;
|
||||
public class Parent {
|
||||
public int pf1;
|
||||
private int pf2;
|
||||
public static int psf1;
|
||||
private static int psf2;
|
||||
|
||||
public void pm1(int n) {}
|
||||
private void pm2(int n) {}
|
||||
public static void psm1() {}
|
||||
private static void psm2() {}
|
||||
}""")
|
||||
myFixture.addClass("""package foo.bar;
|
||||
public class Test extends Parent {
|
||||
public int f1;
|
||||
private int f2;
|
||||
public static int sf1;
|
||||
private static int sf2;
|
||||
|
||||
public void m1(int n) {}
|
||||
private void m2(int n) {}
|
||||
public static void sm1() {}
|
||||
private static void sm2() {}
|
||||
}""")
|
||||
myFixture.configureByText("Main.java", mainClassText)
|
||||
|
||||
val offset = myFixture.caretOffset
|
||||
val reference = myFixture.file.findReferenceAt(offset)
|
||||
assertNotNull("No reference at the caret", reference)
|
||||
return reference!!
|
||||
}
|
||||
|
||||
private fun getMainClassText(name: String, function: String): String {
|
||||
return """import foo.bar.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.$function(Test.class, "<caret>$name");
|
||||
}
|
||||
}"""
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VIRTUAL = "findVirtual"
|
||||
const val STATIC = "findStatic"
|
||||
const val SPECIAL = "findSpecial"
|
||||
|
||||
const val GETTER = "findGetter"
|
||||
const val SETTER = "findSetter"
|
||||
const val STATIC_GETTER = "findStaticGetter"
|
||||
const val STATIC_SETTER = "findStaticSetter"
|
||||
|
||||
const val VAR_HANDLE = "findVarHandle"
|
||||
const val STATIC_VAR_HANDLE = "findStaticVarHandle"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user