diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java index 5ad4543848c3..0f7903bdaf14 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/FunctionalExpressionCompletionProvider.java @@ -21,14 +21,10 @@ import com.intellij.codeInsight.lookup.AutoCompletionPolicy; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.icons.AllIcons; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.patterns.ElementPattern; -import com.intellij.patterns.PatternCondition; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -37,27 +33,17 @@ import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfacePa import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.Function; +import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static com.intellij.patterns.PlatformPatterns.psiElement; +import java.util.*; /** * User: anna */ public class FunctionalExpressionCompletionProvider extends CompletionProvider { - static final ElementPattern LAMBDA = psiElement().with(new PatternCondition("LAMBDA_CONTEXT") { - @Override - public boolean accepts(@NotNull PsiElement element, ProcessingContext context) { - return isLambdaContext(element); - }}); private static boolean isLambdaContext(@NotNull PsiElement element) { final PsiElement rulezzRef = element.getParent(); @@ -71,20 +57,20 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider getLambdaVariants(@NotNull CompletionParameters parameters, boolean prioritize) { + static List getLambdaVariants(@NotNull CompletionParameters parameters, boolean smart) { if (!PsiUtil.isLanguageLevel8OrHigher(parameters.getOriginalFile()) || !isLambdaContext(parameters.getPosition())) return Collections.emptyList(); List result = ContainerUtil.newArrayList(); - for (ExpectedTypeInfo expectedType : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { + ExpectedTypeInfo[] expectedTypes = JavaSmartCompletionContributor.getExpectedTypes(parameters); + for (ExpectedTypeInfo expectedType : expectedTypes) { final PsiType defaultType = expectedType.getDefaultType(); if (LambdaUtil.isFunctionalType(defaultType)) { final PsiType functionalInterfaceType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(defaultType); final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType); if (functionalInterfaceMethod != null) { - assert functionalInterfaceType != null; PsiParameter[] params = new PsiParameter[0]; final PsiElement originalPosition = parameters.getPosition(); final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(functionalInterfaceMethod, PsiUtil.resolveGenericsClassInType(functionalInterfaceType)); @@ -98,12 +84,8 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider() { - @Override - public String fun(PsiParameter parameter) { - return getParamName(parameter, javaCodeStyleManager, originalPosition); - } - }, ",") + ")"; + params.length == 1 ? getParamName(params[0], originalPosition) + : "(" + StringUtil.join(params, parameter -> getParamName(parameter, originalPosition), ",") + ")"; final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)JavaPsiFacade.getElementFactory(project) @@ -112,46 +94,54 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider {}").withInsertHandler(new InsertHandler() { - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - final Editor editor = context.getEditor(); - EditorModificationUtil.insertStringAtCaret(editor, " -> "); - } - }) + .withPresentableText(paramsString + " -> {}") + .withInsertHandler((context, item) -> EditorModificationUtil.insertStringAtCaret(context.getEditor(), " -> ")) .withTypeText(functionalInterfaceType.getPresentableText()) .withIcon(AllIcons.Nodes.Function); LookupElement lambdaElement = builder.withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); - if (prioritize) { + if (!smart) { lambdaElement = PrioritizedLookupElement.withPriority(lambdaElement, 1); } result.add(lambdaElement); } - final PsiType expectedReturnType = substitutor.substitute(functionalInterfaceMethod.getReturnType()); - if (expectedReturnType != null) { - if (params.length > 0) { - collectVariantsByReceiver(prioritize, functionalInterfaceType, params, originalPosition, substitutor, expectedReturnType, result); - } - collectThisVariants(functionalInterfaceType, params, originalPosition, substitutor, expectedReturnType, result); - final PsiClass psiClass = PsiUtil.resolveClassInType(expectedReturnType); - if (psiClass != null && !(psiClass instanceof PsiTypeParameter)) { - if (expectedReturnType.getArrayDimensions() == 0) { - final PsiMethod[] constructors = psiClass.getConstructors(); - for (PsiMethod psiMethod : constructors) { - if (areParameterTypesAppropriate(psiMethod, params, substitutor, 0)) { - result.add(createConstructorReferenceLookup(functionalInterfaceType, expectedReturnType)); - } - } - if (constructors.length == 0 && params.length == 0) { - result.add(createConstructorReferenceLookup(functionalInterfaceType, expectedReturnType)); - } - } - else if (params.length == 1 && PsiType.INT.equals(params[0].getType())){ - result.add(createConstructorReferenceLookup(functionalInterfaceType, expectedReturnType)); - } + for (LookupElement element : getMethodReferenceVariants(smart, functionalInterfaceType, functionalInterfaceMethod, params, originalPosition, substitutor)) { + result.add(smart ? JavaSmartCompletionContributor.decorate(element, Arrays.asList(expectedTypes)) : element); + } + } + } + } + return result; + } + + private static List getMethodReferenceVariants(boolean smart, + PsiType functionalInterfaceType, + PsiMethod functionalInterfaceMethod, + PsiParameter[] params, + PsiElement originalPosition, + PsiSubstitutor substitutor) { + List result = new ArrayList<>(); + final PsiType expectedReturnType = substitutor.substitute(functionalInterfaceMethod.getReturnType()); + if (expectedReturnType != null) { + if (params.length > 0) { + result.addAll(collectVariantsByReceiver(!smart, functionalInterfaceType, params, originalPosition, substitutor, expectedReturnType)); + } + result.addAll(collectThisVariants(functionalInterfaceType, params, originalPosition, substitutor, expectedReturnType)); + final PsiClass psiClass = PsiUtil.resolveClassInType(expectedReturnType); + if (psiClass != null && !(psiClass instanceof PsiTypeParameter)) { + if (expectedReturnType.getArrayDimensions() == 0) { + final PsiMethod[] constructors = psiClass.getConstructors(); + for (PsiMethod psiMethod : constructors) { + if (areParameterTypesAppropriate(psiMethod, params, substitutor, 0)) { + result.add(createConstructorReferenceLookup(functionalInterfaceType, expectedReturnType)); } } + if (constructors.length == 0 && params.length == 0) { + result.add(createConstructorReferenceLookup(functionalInterfaceType, expectedReturnType)); + } + } + else if (params.length == 1 && PsiType.INT.equals(params[0].getType())){ + result.add(createConstructorReferenceLookup(functionalInterfaceType, expectedReturnType)); } } } @@ -166,10 +156,11 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider result) { + private static List collectThisVariants(PsiType functionalInterfaceType, + PsiParameter[] params, + PsiElement originalPosition, + PsiSubstitutor substitutor, PsiType expectedReturnType) { + List result = new ArrayList<>(); final PsiClass psiClass = PsiTreeUtil.getParentOfType(originalPosition, PsiClass.class); if (psiClass != null) { for (PsiMethod psiMethod : psiClass.getMethods()) { @@ -179,14 +170,7 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider() { - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - final int startOffset = context.getStartOffset(); - final Document document = context.getDocument(); - document.insertString(startOffset, "this::"); - } - }) + .withInsertHandler((context, item) -> context.getDocument().insertString(context.getStartOffset(), "this::")) .withTypeText(functionalInterfaceType.getPresentableText()) .withIcon(AllIcons.Nodes.MethodReference) .withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); @@ -194,48 +178,47 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider result) { + private static List collectVariantsByReceiver(boolean prioritize, + PsiType functionalInterfaceType, + PsiParameter[] params, + PsiElement originalPosition, + PsiSubstitutor substitutor, + PsiType expectedReturnType) { + List result = new ArrayList<>(); final PsiType functionalInterfaceParamType = substitutor.substitute(params[0].getType()); final PsiClass paramClass = PsiUtil.resolveClassInClassTypeOnly(functionalInterfaceParamType); if (paramClass != null && !paramClass.hasTypeParameters()) { final Set visited = new HashSet(); for (PsiMethod psiMethod : paramClass.getAllMethods()) { final PsiType returnType = psiMethod.getReturnType(); + PsiClass containingClass = psiMethod.getContainingClass(); + PsiClass qualifierClass = containingClass != null ? containingClass : paramClass; if (visited.add(psiMethod.getName()) && isInstanceMethodWithAppropriateReturnType(expectedReturnType, psiMethod, returnType) && areParameterTypesAppropriate(psiMethod, params, substitutor, 1) && JavaResolveUtil.isAccessible(psiMethod, null, psiMethod.getModifierList(), originalPosition, null, null)) { LookupElement methodRefLookupElement = LookupElementBuilder .create(psiMethod) - .withPresentableText(paramClass.getName() + "::" + psiMethod.getName()) - .withInsertHandler(new InsertHandler() { - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - final int startOffset = context.getStartOffset(); - final Document document = context.getDocument(); - final PsiFile file = context.getFile(); - document.insertString(startOffset, "::"); - JavaCompletionUtil.insertClassReference(paramClass, file, startOffset); - } + .withPresentableText(qualifierClass.getName() + "::" + psiMethod.getName()) + .withInsertHandler((context, item) -> { + int startOffset = context.getStartOffset(); + context.getDocument().insertString(startOffset, "::"); + JavaCompletionUtil.insertClassReference(qualifierClass, context.getFile(), startOffset); }) .withTypeText(functionalInterfaceType.getPresentableText()) .withIcon(AllIcons.Nodes.MethodReference) .withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); - if (prioritize && psiMethod.getContainingClass() == paramClass) { + if (prioritize && containingClass == paramClass) { methodRefLookupElement = PrioritizedLookupElement.withPriority(methodRefLookupElement, 1); } result.add(methodRefLookupElement); } } } + return result; } private static boolean isInstanceMethodWithAppropriateReturnType(PsiType expectedReturnType, PsiMethod psiMethod, PsiType returnType) { @@ -258,7 +241,8 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider children = new ArrayList<>(); + children.stream().filter(Parent::hasFlag) + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/MethodReferenceOnAncestor.java b/java/java-tests/testData/codeInsight/completion/smartType/MethodReferenceOnAncestor.java new file mode 100644 index 000000000000..780cad9617af --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/MethodReferenceOnAncestor.java @@ -0,0 +1,16 @@ +import java.util.*; + +class MyTest { + + public interface Parent { + boolean hasFlag(); + } + + public interface Child extends Parent {} + + public static void main(String[] args) { + List children = new ArrayList<>(); + children.stream().filter(hasF) + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java index 6ff7aa17c75d..94a459998e09 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java @@ -127,6 +127,8 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase { doTest(true); } + public void testMethodReferenceOnAncestor() { doTest(true); } + public void testNoLambdaSuggestionForGenericsFunctionalInterfaceMethod() throws Exception { configureByFile("/" + getTestName(false) + ".java"); assertEmpty(myItems);