mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
method chain completion: suggest chains for arrays
This commit is contained in:
+2
-1
@@ -20,6 +20,7 @@ import com.intellij.compiler.chainsSearch.OccurrencesAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.backwardRefs.LightRef;
|
||||
import org.jetbrains.jps.backwardRefs.SignatureData;
|
||||
|
||||
import java.util.SortedSet;
|
||||
|
||||
@@ -33,7 +34,7 @@ public abstract class CompilerReferenceServiceEx extends CompilerReferenceServic
|
||||
|
||||
@NotNull
|
||||
public abstract SortedSet<OccurrencesAware<MethodIncompleteSignature>> findMethodReferenceOccurrences(@NotNull String rawReturnType,
|
||||
boolean allowIterators)
|
||||
@SignatureData.IteratorKind byte iteratorKind)
|
||||
throws ReferenceIndexUnavailableException;
|
||||
|
||||
public abstract boolean mayHappen(@NotNull LightRef qualifier, @NotNull LightRef base, int probabilityThreshold)
|
||||
|
||||
+2
-2
@@ -222,14 +222,14 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
|
||||
@NotNull
|
||||
@Override
|
||||
public SortedSet<OccurrencesAware<MethodIncompleteSignature>> findMethodReferenceOccurrences(@NotNull String rawReturnType,
|
||||
boolean allowIterators) {
|
||||
@SignatureData.IteratorKind byte iteratorKind) {
|
||||
try {
|
||||
myReadDataLock.lock();
|
||||
if (myReader == null) throw new ReferenceIndexUnavailableException();
|
||||
try {
|
||||
final int type = myReader.getNameEnumerator().tryEnumerate(rawReturnType);
|
||||
if (type == 0) return Collections.emptySortedSet();
|
||||
return Stream.of(new SignatureData(type, (byte)0, true), new SignatureData(type, (byte)0, false))
|
||||
return Stream.of(new SignatureData(type, iteratorKind, true), new SignatureData(type, iteratorKind, false))
|
||||
.flatMap(sd -> myReader.getMembersFor(sd)
|
||||
.stream()
|
||||
.filter(r -> r instanceof LightRef.JavaLightMethodRef)
|
||||
|
||||
+15
-5
@@ -16,10 +16,7 @@
|
||||
package com.intellij.compiler.backwardRefs
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClassType
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.jps.backwardRefs.LightRef
|
||||
import org.jetbrains.jps.backwardRefs.SignatureData
|
||||
@@ -60,7 +57,20 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
|
||||
.filter { it.hasModifierProperty(PsiModifier.STATIC) == isStatic }
|
||||
.filter {
|
||||
val returnType = it.returnType
|
||||
returnType is PsiClassType && returnType.resolve()?.qualifiedName == rawReturnType
|
||||
when (signatureData.iteratorKind) {
|
||||
SignatureData.ARRAY_ONE_DIM -> {
|
||||
when (returnType) {
|
||||
is PsiArrayType -> {
|
||||
val componentType = returnType.componentType
|
||||
componentType is PsiClassType && componentType.resolve()?.qualifiedName == rawReturnType
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
SignatureData.ITERATOR_ONE_DIM -> false
|
||||
SignatureData.ZERO_DIM -> returnType is PsiClassType && returnType.resolve()?.qualifiedName == rawReturnType
|
||||
else -> throw IllegalStateException("kind is unsupported ${signatureData.iteratorKind}")
|
||||
}
|
||||
}
|
||||
.sortedBy({ it.parameterList.parametersCount })
|
||||
.toTypedArray()
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.backwardRefs.SignatureData;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -56,7 +57,7 @@ public class ChainsSearcher {
|
||||
private static SearchInitializer createInitializer(TargetType target,
|
||||
CompilerReferenceServiceEx compilerReferenceServiceEx,
|
||||
ChainCompletionContext context) {
|
||||
SortedSet<OccurrencesAware<MethodIncompleteSignature>> methods = compilerReferenceServiceEx.findMethodReferenceOccurrences(target.getClassQName(), false);
|
||||
SortedSet<OccurrencesAware<MethodIncompleteSignature>> methods = compilerReferenceServiceEx.findMethodReferenceOccurrences(target.getClassQName(), target.getArrayKind());
|
||||
return new SearchInitializer(methods, context);
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ public class ChainsSearcher {
|
||||
continue;
|
||||
}
|
||||
String currentReturnType = headSignature.getOwner();
|
||||
SortedSet<OccurrencesAware<MethodIncompleteSignature>> nextMethods = indexReader.findMethodReferenceOccurrences(currentReturnType, false);
|
||||
SortedSet<OccurrencesAware<MethodIncompleteSignature>> nextMethods = indexReader.findMethodReferenceOccurrences(currentReturnType, SignatureData.ZERO_DIM);
|
||||
MaxSizeTreeSet<OccurrencesAware<MethodIncompleteSignature>> currentSignatures =
|
||||
new MaxSizeTreeSet<>(maxResultSize);
|
||||
for (OccurrencesAware<MethodIncompleteSignature> indexValue : nextMethods) {
|
||||
|
||||
@@ -41,7 +41,7 @@ public class MethodsChain {
|
||||
int weight,
|
||||
@NotNull ChainCompletionContext context) {
|
||||
PsiClass qualifier = context.resolveQualifierClass(signature);
|
||||
if (qualifier == null || (!signature.isStatic() && isInheritorOrSelf(context.getTarget().getPsiType(), qualifier))) {
|
||||
if (qualifier == null || (!signature.isStatic() && InheritanceUtil.isInheritorOrSelf(context.getTarget().getTargetClass(), qualifier, true))) {
|
||||
return null;
|
||||
}
|
||||
PsiMethod[] methods = context.resolve(signature);
|
||||
@@ -166,9 +166,4 @@ public class MethodsChain {
|
||||
Set<PsiMethod> deepestSupers1 = methodSet1.stream().flatMap(m -> Arrays.stream(m.findDeepestSuperMethods())).collect(Collectors.toSet());
|
||||
return methodSet2.stream().flatMap(m -> Arrays.stream(m.findDeepestSuperMethods())).anyMatch(deepestSupers1::contains);
|
||||
}
|
||||
|
||||
private static boolean isInheritorOrSelf(@NotNull PsiType type, @NotNull PsiClass superCandidate) {
|
||||
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
return aClass != null && InheritanceUtil.isInheritorOrSelf(aClass, superCandidate, true);
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -165,4 +165,8 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
|
||||
private static ElementPattern<PsiElement> patternForMethodCallParameter() {
|
||||
return psiElement().withSuperParent(3, PsiMethodCallExpressionImpl.class);
|
||||
}
|
||||
|
||||
private static boolean suggestIterators(@NotNull CompletionParameters parameters) {
|
||||
return parameters.getInvocationCount() > 1;
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,24 @@
|
||||
package com.intellij.compiler.chainsSearch.context;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.backwardRefs.SignatureData;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class TargetType {
|
||||
|
||||
private final String myClassQName;
|
||||
private final boolean myArray;
|
||||
private final byte myArrayKind;
|
||||
private final PsiType myPsiType;
|
||||
|
||||
public TargetType(String classQName,
|
||||
boolean isArray,
|
||||
byte arrayKind,
|
||||
PsiType targetType) {
|
||||
myClassQName = classQName;
|
||||
myArray = isArray;
|
||||
myArrayKind = arrayKind;
|
||||
myPsiType = targetType;
|
||||
}
|
||||
|
||||
@@ -40,22 +41,15 @@ public class TargetType {
|
||||
return myClassQName;
|
||||
}
|
||||
|
||||
public boolean isArray() {
|
||||
return myArray;
|
||||
}
|
||||
|
||||
public PsiType getPsiType() {
|
||||
return myPsiType;
|
||||
@SignatureData.IteratorKind
|
||||
public byte getArrayKind() {
|
||||
return myArrayKind;
|
||||
}
|
||||
|
||||
public PsiClass getTargetClass() {
|
||||
return PsiUtil.resolveClassInType(myPsiType);
|
||||
}
|
||||
|
||||
public boolean isAssignableFrom(PsiClass psiClass) {
|
||||
return myPsiType.isAssignableFrom(JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory().createType(psiClass));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static TargetType create(PsiType type) {
|
||||
if (type instanceof PsiArrayType) {
|
||||
@@ -69,23 +63,29 @@ public class TargetType {
|
||||
|
||||
@Nullable
|
||||
private static TargetType create(PsiArrayType arrayType) {
|
||||
PsiType currentComponentType = arrayType.getComponentType();
|
||||
while (currentComponentType instanceof PsiArrayType) {
|
||||
currentComponentType = ((PsiArrayType)currentComponentType).getComponentType();
|
||||
}
|
||||
if (!(currentComponentType instanceof PsiClassType)) {
|
||||
return null;
|
||||
}
|
||||
String targetQName = arrayType.getCanonicalText();
|
||||
return new TargetType(targetQName, true, arrayType);
|
||||
// only 1-dim arrays accepted
|
||||
PsiType componentType = arrayType.getComponentType();
|
||||
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(componentType);
|
||||
if (aClass == null) return null;
|
||||
String targetQName = aClass.getQualifiedName();
|
||||
if (targetQName == null) return null;
|
||||
return new TargetType(targetQName, SignatureData.ARRAY_ONE_DIM, arrayType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TargetType create(PsiClassType classType) {
|
||||
PsiClassType.ClassResolveResult resolvedGenerics = classType.resolveGenerics();
|
||||
PsiClass resolvedClass = resolvedGenerics.getElement();
|
||||
if (resolvedClass == null) {
|
||||
return null;
|
||||
PsiClass resolvedClass = PsiUtil.resolveClassInClassTypeOnly(classType);
|
||||
byte iteratorKind = SignatureData.ZERO_DIM;
|
||||
if (resolvedClass == null) return null;
|
||||
String iteratorClass = isIterator(resolvedClass);
|
||||
if (iteratorClass != null) {
|
||||
PsiClassType streamType = (PsiClassType)PsiUtil.substituteTypeParameter(classType, iteratorClass, 0, false);
|
||||
if (streamType == null) return null;
|
||||
PsiType[] parameters = streamType.getParameters();
|
||||
if (parameters.length != 1 || !(parameters[0] instanceof PsiClassType)) return null;
|
||||
resolvedClass = PsiUtil.resolveClassInClassTypeOnly(parameters[0]);
|
||||
if (resolvedClass == null) return null;
|
||||
iteratorKind = SignatureData.ITERATOR_ONE_DIM;
|
||||
}
|
||||
String classQName = resolvedClass.getQualifiedName();
|
||||
if (classQName == null) {
|
||||
@@ -94,6 +94,19 @@ public class TargetType {
|
||||
if (resolvedClass.hasTypeParameters()) {
|
||||
return null;
|
||||
}
|
||||
return new TargetType(classQName, false, classType);
|
||||
return new TargetType(classQName, iteratorKind, classType);
|
||||
}
|
||||
|
||||
private static String isIterator(PsiClass resolvedClass) {
|
||||
if (InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_LANG_ITERABLE)) {
|
||||
return CommonClassNames.JAVA_LANG_ITERABLE;
|
||||
}
|
||||
if (InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_UTIL_ITERATOR)) {
|
||||
return CommonClassNames.JAVA_UTIL_ITERATOR;
|
||||
}
|
||||
if (InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_UTIL_STREAM_STREAM)) {
|
||||
return CommonClassNames.JAVA_UTIL_STREAM_STREAM;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
import java.jang.String;
|
||||
|
||||
class PsiMethod {
|
||||
}
|
||||
|
||||
interface PsiClass {
|
||||
PsiMethod[] getMethods();
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
PsiClass c;
|
||||
|
||||
public void method() {
|
||||
PsiMethod[] m = <caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiClass c) {
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
c.getMethods();
|
||||
}
|
||||
}
|
||||
|
||||
class PsiMethod {
|
||||
}
|
||||
|
||||
interface PsiClass {
|
||||
PsiMethod[] getMethods();
|
||||
}
|
||||
+6
-1
@@ -99,7 +99,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
|
||||
assertAdvisorLookupElementEquals("getInstance().findFile().findElementAt", 0, 5, 3, 0, assertOneElement(doCompletion()));
|
||||
}
|
||||
|
||||
public void _testChainsWithIndependentCallings() {
|
||||
public void testChainsWithIndependentCallings() {
|
||||
assertOneElement(doCompletion());
|
||||
}
|
||||
|
||||
@@ -210,6 +210,11 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
|
||||
doTestRendering();
|
||||
}
|
||||
|
||||
public void testArray() {
|
||||
WeightableChainLookupElement element = assertOneElement(doCompletion());
|
||||
assertEquals("c.getMethods", element.getLookupString());
|
||||
}
|
||||
|
||||
public void assertAdvisorLookupElementEquals(String lookupText,
|
||||
int unreachableParametersCount,
|
||||
int lastMethodWeight,
|
||||
|
||||
@@ -15,19 +15,23 @@
|
||||
*/
|
||||
package org.jetbrains.jps.backwardRefs;
|
||||
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
|
||||
public class SignatureData {
|
||||
public static final byte ZERO_DIM = 0;
|
||||
public static final byte ARRAY_ONE_DIM = 1;
|
||||
// represents java's Iterator, Iterable and BaseStream
|
||||
public static final byte ITERATOR_ONE_DIM = 2;
|
||||
@MagicConstant(intValues = {ZERO_DIM, ARRAY_ONE_DIM, ITERATOR_ONE_DIM})
|
||||
public @interface IteratorKind {}
|
||||
|
||||
private final int myRawReturnType;
|
||||
private final byte myArrayDimension;
|
||||
private final byte myArrayKind;
|
||||
private final boolean myStatic;
|
||||
|
||||
public SignatureData(int rawReturnType, byte arrayDimension, boolean isStatic) {
|
||||
public SignatureData(int rawReturnType, byte arrayKind, boolean isStatic) {
|
||||
myRawReturnType = rawReturnType;
|
||||
myArrayDimension = arrayDimension;
|
||||
myArrayKind = arrayKind;
|
||||
myStatic = isStatic;
|
||||
}
|
||||
|
||||
@@ -36,7 +40,7 @@ public class SignatureData {
|
||||
}
|
||||
|
||||
public byte getIteratorKind() {
|
||||
return myArrayDimension;
|
||||
return myArrayKind;
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
@@ -51,7 +55,7 @@ public class SignatureData {
|
||||
SignatureData data = (SignatureData)o;
|
||||
|
||||
if (myRawReturnType != data.myRawReturnType) return false;
|
||||
if (myArrayDimension != data.myArrayDimension) return false;
|
||||
if (myArrayKind != data.myArrayKind) return false;
|
||||
if (myStatic != data.myStatic) return false;
|
||||
|
||||
return true;
|
||||
@@ -60,7 +64,7 @@ public class SignatureData {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myRawReturnType;
|
||||
result = 31 * result + myArrayDimension;
|
||||
result = 31 * result + myArrayKind;
|
||||
result = 31 * result + (myStatic ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user