From e583481074e628617c6c088bbf34e1de346300b9 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 13 May 2019 18:09:25 +0200 Subject: [PATCH] java completion: disprefer already called builder methods (IDEA-211080) GitOrigin-RevId: 5a579dbd900505da03b987041f58c45104997d8f --- .../completion/JavaCompletionSorting.java | 36 ++++++++++++++++--- .../DispreferAlreadyCalledBuilderMethods.java | 13 +++++++ .../NormalCompletionOrderingTest.groovy | 4 +++ 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normalSorting/DispreferAlreadyCalledBuilderMethods.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java index 241b7c0ff5da..643b0a1ef7a5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java @@ -19,16 +19,14 @@ import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import static com.intellij.patterns.PsiJavaPatterns.psiElement; @@ -58,9 +56,13 @@ public class JavaCompletionSorting { } else if (PsiTreeUtil.getParentOfType(position, PsiReferenceList.class) == null) { sorter = ((CompletionSorterImpl)sorter).withClassifier("liftShorterClasses", true, new LiftShorterClasses(position)); } + + List afterPriority = new ArrayList<>(); + ContainerUtil.addIfNotNull(afterPriority, dispreferPreviousChainCalls(position)); if (smart) { - sorter = sorter.weighAfter("priority", new PreferDefaultTypeWeigher(expectedTypes, parameters, false)); + afterPriority.add(new PreferDefaultTypeWeigher(expectedTypes, parameters, false)); } + sorter = sorter.weighAfter("priority", afterPriority.toArray(new LookupElementWeigher[0])); List afterStats = new ArrayList<>(); afterStats.add(new PreferByKindWeigher(type, position, expectedTypes)); @@ -85,6 +87,30 @@ public class JavaCompletionSorting { return result.withRelevanceSorter(sorter); } + @Nullable + private static LookupElementWeigher dispreferPreviousChainCalls(PsiElement position) { + Set previousChainCalls = new HashSet<>(); + if (position.getParent() instanceof PsiReferenceExpression) { + PsiMethodCallExpression qualifier = getCallQualifier((PsiReferenceExpression)position.getParent()); + while (qualifier != null) { + ContainerUtil.addIfNotNull(previousChainCalls, qualifier.resolveMethod()); + qualifier = getCallQualifier(qualifier.getMethodExpression()); + } + } + return previousChainCalls.isEmpty() ? null : new LookupElementWeigher("dispreferPreviousChainCalls") { + @Override + public Comparable weigh(@NotNull LookupElement element, @NotNull WeighingContext context) { + //noinspection SuspiciousMethodCalls + return previousChainCalls.contains(element.getPsiElement()); + } + }; + } + + @Nullable + private static PsiMethodCallExpression getCallQualifier(PsiReferenceExpression ref) { + return ObjectUtils.tryCast(ref.getQualifier(), PsiMethodCallExpression.class); + } + @NotNull private static ExpectedTypeInfo[] getExpectedTypesWithDfa(CompletionParameters parameters, PsiElement position) { if (psiElement().beforeLeaf(psiElement().withText(".")).accepts(position)) { diff --git a/java/java-tests/testData/codeInsight/completion/normalSorting/DispreferAlreadyCalledBuilderMethods.java b/java/java-tests/testData/codeInsight/completion/normalSorting/DispreferAlreadyCalledBuilderMethods.java new file mode 100644 index 000000000000..b47d7b71a49a --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normalSorting/DispreferAlreadyCalledBuilderMethods.java @@ -0,0 +1,13 @@ +class Test { + { + MyBuilder b; + b.a().b(). + } +} + +interface MyBuilder { + MyBuilder a(); + MyBuilder b(); + MyBuilder c(); + MyBuilder d(); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy index b321057381c6..f7c7c618b1b6 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -871,6 +871,10 @@ class Foo { checkPreferredItems 1, 'Intf', 'IntfImpl' } + void testDispreferAlreadyCalledBuilderMethods() { + checkPreferredItems 0, 'c', 'd' + } + void testPreferPrintln() { myFixture.configureByText 'a.java', 'class Foo { { System.out.prix } }' myFixture.completeBasic()