mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
method chains search: check assignability; use name suggester; use JavaChainLookupElement
This commit is contained in:
+12
-3
@@ -22,6 +22,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import org.jetbrains.jps.backwardRefs.LightRef
|
||||
import org.jetbrains.jps.backwardRefs.SignatureData
|
||||
import java.util.function.Predicate
|
||||
|
||||
class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
|
||||
private val signatureData: SignatureData,
|
||||
@@ -48,15 +49,23 @@ class MethodIncompleteSignature(val ref: LightRef.JavaLightMethodRef,
|
||||
val isStatic: Boolean
|
||||
get() = signatureData.isStatic
|
||||
|
||||
fun resolveQualifier(project: Project, resolveScope: GlobalSearchScope) = JavaPsiFacade.getInstance(project).findClass(owner, resolveScope)
|
||||
fun resolveQualifier(project: Project,
|
||||
resolveScope: GlobalSearchScope,
|
||||
accessValidator: Predicate<PsiMember>): PsiClass? {
|
||||
val clazz = JavaPsiFacade.getInstance(project).findClass(owner, resolveScope)
|
||||
return if (clazz != null && accessValidator.test(clazz)) clazz else null
|
||||
}
|
||||
|
||||
fun resolve(project: Project, resolveScope: GlobalSearchScope): Array<PsiMethod> {
|
||||
fun resolve(project: Project,
|
||||
resolveScope: GlobalSearchScope,
|
||||
accessValidator: Predicate<PsiMember>): Array<PsiMethod> {
|
||||
if (CONSTRUCTOR_METHOD_NAME == name) {
|
||||
return PsiMethod.EMPTY_ARRAY
|
||||
}
|
||||
val aClass = resolveQualifier(project, resolveScope) ?: return PsiMethod.EMPTY_ARRAY
|
||||
val aClass = resolveQualifier(project, resolveScope, accessValidator) ?: return PsiMethod.EMPTY_ARRAY
|
||||
return aClass.findMethodsByName(name, true)
|
||||
.filter { it.hasModifierProperty(PsiModifier.STATIC) == isStatic }
|
||||
.filter { accessValidator.test(it) }
|
||||
.filter {
|
||||
val returnType = it.returnType
|
||||
when (signatureData.iteratorKind) {
|
||||
|
||||
@@ -49,7 +49,6 @@ public class ChainsSearcher {
|
||||
initializer,
|
||||
pathMaximalLength,
|
||||
maxResultSize,
|
||||
searchTarget.getClassQName(),
|
||||
context);
|
||||
}
|
||||
|
||||
@@ -75,7 +74,6 @@ public class ChainsSearcher {
|
||||
SearchInitializer initializer,
|
||||
int pathMaximalLength,
|
||||
int maxResultSize,
|
||||
String targetQName,
|
||||
ChainCompletionContext context) {
|
||||
SearchInitializer.InitResult initResult = initializer.init(Collections.emptySet());
|
||||
|
||||
@@ -90,7 +88,7 @@ public class ChainsSearcher {
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedList::new));
|
||||
|
||||
ResultHolder result = new ResultHolder(context.getPsiManager());
|
||||
ResultHolder result = new ResultHolder();
|
||||
while (!q.isEmpty()) {
|
||||
ProgressManager.checkCanceled();
|
||||
OccurrencesAware<MethodsChain> currentVertex = q.poll();
|
||||
@@ -109,6 +107,7 @@ public class ChainsSearcher {
|
||||
SortedSet<OccurrencesAware<MethodIncompleteSignature>> nextMethods = indexReader.findMethodReferenceOccurrences(currentReturnType, SignatureData.ZERO_DIM);
|
||||
MaxSizeTreeSet<OccurrencesAware<MethodIncompleteSignature>> currentSignatures =
|
||||
new MaxSizeTreeSet<>(maxResultSize);
|
||||
String targetQName = context.getTarget().getClassQName();
|
||||
for (OccurrencesAware<MethodIncompleteSignature> indexValue : nextMethods) {
|
||||
MethodIncompleteSignature vertex = indexValue.getUnderlying();
|
||||
int occurrences = indexValue.getOccurrences();
|
||||
@@ -181,10 +180,8 @@ public class ChainsSearcher {
|
||||
|
||||
private static class ResultHolder {
|
||||
private final List<MethodsChain> myResult;
|
||||
private final PsiManager myContext;
|
||||
|
||||
private ResultHolder(PsiManager psiManager) {
|
||||
myContext = psiManager;
|
||||
private ResultHolder() {
|
||||
myResult = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -224,14 +221,14 @@ public class ChainsSearcher {
|
||||
}
|
||||
|
||||
public List<MethodsChain> getResult() {
|
||||
return findSimilar(reduceChainsSize(myResult, PsiManager.getInstance(myContext.getProject())), myContext);
|
||||
return findSimilar(reduceChainsSize(myResult));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myResult.size();
|
||||
}
|
||||
|
||||
private static List<MethodsChain> reduceChainsSize(List<MethodsChain> chains, PsiManager psiManager) {
|
||||
private static List<MethodsChain> reduceChainsSize(List<MethodsChain> chains) {
|
||||
return ContainerUtil.map(chains, chain -> {
|
||||
Iterator<PsiMethod[]> chainIterator = chain.iterator();
|
||||
if (!chainIterator.hasNext()) {
|
||||
@@ -255,11 +252,11 @@ public class ChainsSearcher {
|
||||
PsiMethod[] fSupers = f.findDeepestSuperMethods();
|
||||
PsiMethod fSuper = fSupers.length == 0 ? first[0] : fSupers[0];
|
||||
for (PsiMethod currentMethod : currentMethods) {
|
||||
if (psiManager.areElementsEquivalent(currentMethod, fSuper)) {
|
||||
if (currentMethod == fSuper) {
|
||||
return createChainFromFirstElement(chain, currentMethod.getContainingClass());
|
||||
}
|
||||
for (PsiMethod method : currentMethod.findDeepestSuperMethods()) {
|
||||
if (psiManager.areElementsEquivalent(method, fSuper)) {
|
||||
if (method == fSuper) {
|
||||
return createChainFromFirstElement(chain, method.getContainingClass());
|
||||
}
|
||||
}
|
||||
@@ -271,8 +268,8 @@ public class ChainsSearcher {
|
||||
});
|
||||
}
|
||||
|
||||
private static List<MethodsChain> findSimilar(List<MethodsChain> chains, PsiManager psiManager) {
|
||||
ResultHolder resultHolder = new ResultHolder(psiManager);
|
||||
private static List<MethodsChain> findSimilar(List<MethodsChain> chains) {
|
||||
ResultHolder resultHolder = new ResultHolder();
|
||||
for (MethodsChain chain : chains) {
|
||||
resultHolder.add(chain);
|
||||
}
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ public class MethodsChainLookupRangingHelper {
|
||||
}
|
||||
|
||||
if (newVariableClass != null) {
|
||||
chainLookupElement = new ChainCompletionNewVariableLookupElement(newVariableClass, chainLookupElement);
|
||||
chainLookupElement = new JavaChainLookupElement(new ChainCompletionNewVariableLookupElement(newVariableClass), chainLookupElement);
|
||||
}
|
||||
|
||||
ChainRelevance relevance =
|
||||
|
||||
+22
-39
@@ -16,43 +16,47 @@
|
||||
package com.intellij.compiler.chainsSearch.completion.lookup;
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ChainCompletionNewVariableLookupElement extends LookupElementDecorator<LookupElement> {
|
||||
private static final Logger LOG = Logger.getInstance(ChainCompletionNewVariableLookupElement.class);
|
||||
|
||||
public class ChainCompletionNewVariableLookupElement extends LookupElement {
|
||||
@NotNull
|
||||
private final PsiClass myQualifierClass;
|
||||
@NotNull
|
||||
private final String myNewVarName;
|
||||
|
||||
public ChainCompletionNewVariableLookupElement(@NotNull final PsiClass qualifierClass, final LookupElement calledMethods) {
|
||||
super(calledMethods);
|
||||
myNewVarName = StringUtil.decapitalize(ObjectUtils.notNull(qualifierClass.getName()));
|
||||
public ChainCompletionNewVariableLookupElement(@NotNull final PsiClass qualifierClass) {
|
||||
Project project = qualifierClass.getProject();
|
||||
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
myNewVarName = Stream
|
||||
.of(codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, elementFactory.createType(qualifierClass)).names)
|
||||
.sorted(Comparator.comparing(String::length).reversed())
|
||||
.findFirst()
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
myQualifierClass = qualifierClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCompletionPolicy getAutoCompletionPolicy() {
|
||||
return AutoCompletionPolicy.NEVER_AUTOCOMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(final InsertionContext context) {
|
||||
final RangeMarker rangeMarker = context.getDocument().createRangeMarker(context.getStartOffset(), context.getStartOffset());
|
||||
getDelegate().handleInsert(context);
|
||||
context.getDocument().insertString(rangeMarker.getStartOffset(), myNewVarName + ".");
|
||||
context.commitDocument();
|
||||
final PsiFile file = context.getFile();
|
||||
((PsiJavaFile)file).importClass(myQualifierClass);
|
||||
final PsiElement caretElement = ObjectUtils.notNull(file.findElementAt(context.getEditor().getCaretModel().getOffset()));
|
||||
@@ -71,33 +75,12 @@ public class ChainCompletionNewVariableLookupElement extends LookupElementDecora
|
||||
elementFactory.createType(myQualifierClass),
|
||||
elementFactory.createExpressionFromText(PsiKeyword.NULL, null));
|
||||
|
||||
PsiStatement newVarDeclaration = (PsiStatement)statement.getParent().addBefore(newVarDeclarationTemplate, statement);
|
||||
final PsiLiteralExpression nullKeyword = findNullElement(newVarDeclaration);
|
||||
final int offset = nullKeyword.getTextOffset();
|
||||
final int endOffset = offset + nullKeyword.getTextLength();
|
||||
context.getEditor().getSelectionModel().setSelection(offset, endOffset);
|
||||
context.getEditor().getCaretModel().moveToOffset(offset);
|
||||
statement.getParent().addBefore(newVarDeclarationTemplate, statement);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getLookupString() {
|
||||
return getDelegate().getLookupString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderElement(final LookupElementPresentation presentation) {
|
||||
super.renderElement(presentation);
|
||||
presentation.setItemText(myNewVarName + "." + presentation.getItemText());
|
||||
}
|
||||
|
||||
private static PsiLiteralExpression findNullElement(final PsiElement psiElement) {
|
||||
final Collection<PsiLiteralExpression> literalExpressions = PsiTreeUtil.findChildrenOfType(psiElement, PsiLiteralExpression.class);
|
||||
for (final PsiLiteralExpression literalExpression : literalExpressions) {
|
||||
if (PsiKeyword.NULL.equals(literalExpression.getText())) {
|
||||
return literalExpression;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
return myNewVarName;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -33,6 +33,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -50,7 +51,7 @@ public class ChainCompletionContext {
|
||||
@NotNull
|
||||
private final Project myProject;
|
||||
@NotNull
|
||||
private final PsiManager myPsiManager;
|
||||
private final PsiResolveHelper myResolveHelper;
|
||||
@NotNull
|
||||
private final FactoryMap<MethodIncompleteSignature, PsiClass> myQualifierClassResolver;
|
||||
@NotNull
|
||||
@@ -66,19 +67,19 @@ public class ChainCompletionContext {
|
||||
myContext = context;
|
||||
myResolveScope = context.getResolveScope();
|
||||
myProject = context.getProject();
|
||||
myPsiManager = PsiManager.getInstance(myProject);
|
||||
myResolveHelper = PsiResolveHelper.SERVICE.getInstance(myProject);
|
||||
myQualifierClassResolver = new FactoryMap<MethodIncompleteSignature, PsiClass>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiClass create(MethodIncompleteSignature sign) {
|
||||
return sign.resolveQualifier(myProject, myResolveScope);
|
||||
return sign.resolveQualifier(myProject, myResolveScope, accessValidator());
|
||||
}
|
||||
};
|
||||
myResolver = new FactoryMap<MethodIncompleteSignature, PsiMethod[]>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected PsiMethod[] create(MethodIncompleteSignature sign) {
|
||||
return sign.resolve(myProject, myResolveScope);
|
||||
return sign.resolve(myProject, myResolveScope, accessValidator());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -119,11 +120,6 @@ public class ChainCompletionContext {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiManager getPsiManager() {
|
||||
return myPsiManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement findRelevantStringInContext(String stringParameterName) {
|
||||
String sanitizedTarget = MethodChainsSearchUtil.sanitizedToLowerCase(stringParameterName);
|
||||
@@ -159,6 +155,10 @@ public class ChainCompletionContext {
|
||||
return myResolver.get(sign);
|
||||
}
|
||||
|
||||
private Predicate<PsiMember> accessValidator() {
|
||||
return m -> myResolveHelper.isAccessible(m, myContext, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ChainCompletionContext createContext(@Nullable PsiType targetType,
|
||||
@Nullable PsiElement containingElement, boolean suggestIterators) {
|
||||
|
||||
+5
-6
@@ -100,9 +100,7 @@ public class ChainSearchTarget {
|
||||
@Nullable
|
||||
private static ChainSearchTarget create(PsiClassType classType) {
|
||||
PsiClass resolvedClass = PsiUtil.resolveClassInClassTypeOnly(classType);
|
||||
if (resolvedClass == null || resolvedClass instanceof PsiTypeParameter || LambdaUtil.isFunctionalClass(resolvedClass)) return null;
|
||||
String packageName = JavaHierarchyUtil.getPackageName(resolvedClass);
|
||||
if (packageName == null || EXCLUDED_PACKAGES.contains(packageName)) return null;
|
||||
if (resolvedClass == null) return null;
|
||||
byte iteratorKind = SignatureData.ZERO_DIM;
|
||||
String iteratorClass = getIteratorKind(resolvedClass);
|
||||
if (iteratorClass != null) {
|
||||
@@ -110,9 +108,10 @@ public class ChainSearchTarget {
|
||||
if (resolvedClass == null) return null;
|
||||
iteratorKind = SignatureData.ITERATOR_ONE_DIM;
|
||||
}
|
||||
if (resolvedClass.hasTypeParameters()) {
|
||||
return null;
|
||||
}
|
||||
if (resolvedClass.hasTypeParameters() || resolvedClass instanceof PsiTypeParameter || LambdaUtil.isFunctionalClass(resolvedClass)) return null;
|
||||
String packageName = JavaHierarchyUtil.getPackageName(resolvedClass);
|
||||
if (packageName == null || EXCLUDED_PACKAGES.contains(packageName)) return null;
|
||||
|
||||
String classQName = resolvedClass.getQualifiedName();
|
||||
if (classQName == null) {
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ interface Project {}
|
||||
|
||||
public class TestCompletion {
|
||||
void m() {
|
||||
PsiManager psiManager = <selection><caret>null</selection>;
|
||||
PsiManager psiManager = null;
|
||||
Project p = psiManager.getProject()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ interface Project {}
|
||||
public class TestCompletion {
|
||||
void m() {
|
||||
String asd = "123";
|
||||
PsiManager psiManager = <selection><caret>null</selection>;
|
||||
PsiManager psiManager = null;
|
||||
Project p = psiManager.getProject(asd, zxc)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -84,7 +84,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
|
||||
}
|
||||
|
||||
public void testOneChainContainsOther2() {
|
||||
assertLookupElementStringEquals(assertOneElement(doCompletion()), "getManager");
|
||||
assertLookupElementStringEquals(assertOneElement(doCompletion()), "psiElement.getManager");
|
||||
}
|
||||
|
||||
public void testTwoVariablesWithOneTypeOrSuperType() {
|
||||
@@ -177,7 +177,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
|
||||
assertLookupElementStringEquals(lookupElements.get(0), "f.createFileFromText");
|
||||
assertLookupElementStringEquals(lookupElements.get(1), "getInstance().findFile");
|
||||
assertLookupElementStringEquals(lookupElements.get(2), "getInstance().getPsiFile");
|
||||
assertLookupElementStringEquals(lookupElements.get(3), "getContainingClass");
|
||||
assertLookupElementStringEquals(lookupElements.get(3), "psiClass.getContainingClass");
|
||||
}
|
||||
|
||||
public void testResultRelevance() {
|
||||
@@ -191,7 +191,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
|
||||
List<WeightableChainLookupElement> weightableChainLookupElements = doCompletion();
|
||||
assertSize(2, weightableChainLookupElements);
|
||||
assertEquals("e.getProject1", weightableChainLookupElements.get(0).getLookupString());
|
||||
assertEquals("getProject", weightableChainLookupElements.get(1).getLookupString());
|
||||
assertEquals("psiManager.getProject", weightableChainLookupElements.get(1).getLookupString());
|
||||
}
|
||||
|
||||
public void testRenderingVariableInContextAndNotInContext() {
|
||||
|
||||
Reference in New Issue
Block a user