method ref completion: complete receiver typed method ref without param in valid lambda context (IDEA-140730)

This commit is contained in:
Anna Kozlova
2015-05-28 19:42:08 +02:00
parent 3b59425c3d
commit 5ffbec6759
6 changed files with 94 additions and 17 deletions
@@ -20,6 +20,8 @@ import com.intellij.codeInsight.generation.GenerateMembersUtil;
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;
@@ -27,28 +29,32 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Function;
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;
/**
* User: anna
*/
public class LambdaCompletionProvider extends CompletionProvider<CompletionParameters> {
public class FunctionalExpressionCompletionProvider extends CompletionProvider<CompletionParameters> {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
result.addAllElements(getLambdaVariants(parameters));
result.addAllElements(getLambdaVariants(parameters, false));
}
static List<LookupElement> getLambdaVariants(@NotNull CompletionParameters parameters) {
static List<LookupElement> getLambdaVariants(@NotNull CompletionParameters parameters, boolean prioritize) {
if (!PsiUtil.isLanguageLevel8OrHigher(parameters.getOriginalFile())) return Collections.emptyList();
List<LookupElement> result = ContainerUtil.newArrayList();
@@ -56,15 +62,15 @@ public class LambdaCompletionProvider extends CompletionProvider<CompletionParam
final PsiType defaultType = expectedType.getDefaultType();
if (LambdaUtil.isFunctionalType(defaultType)) {
final PsiType functionalInterfaceType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(defaultType);
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (method != null) {
PsiParameter[] params = method.getParameterList().getParameters();
final Project project = method.getProject();
final PsiElement originalPosition = parameters.getOriginalPosition();
final JVMElementFactory jvmElementFactory = originalPosition != null ? JVMElementFactories.getFactory(originalPosition.getLanguage(), project) : null;
final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (functionalInterfaceMethod != null) {
PsiParameter[] params = functionalInterfaceMethod.getParameterList().getParameters();
final Project project = functionalInterfaceMethod.getProject();
final PsiElement originalPosition = parameters.getPosition();
final JVMElementFactory jvmElementFactory = JVMElementFactories.getFactory(originalPosition.getLanguage(), project);
final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project);
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(functionalInterfaceMethod, PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
if (jvmElementFactory != null) {
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(method, PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
params = GenerateMembersUtil.overriddenParameters(params, jvmElementFactory, javaCodeStyleManager, substitutor, originalPosition);
}
@@ -82,14 +88,57 @@ public class LambdaCompletionProvider extends CompletionProvider<CompletionParam
lambdaExpression = (PsiLambdaExpression)codeStyleManager.reformat(lambdaExpression);
paramsString = lambdaExpression.getParameterList().getText();
final LookupElementBuilder builder =
LookupElementBuilder.create(paramsString).withPresentableText(paramsString + " -> {}").withInsertHandler(new InsertHandler<LookupElement>() {
LookupElementBuilder.create(functionalInterfaceMethod, paramsString).withPresentableText(paramsString + " -> {}").withInsertHandler(new InsertHandler<LookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
final Editor editor = context.getEditor();
EditorModificationUtil.insertStringAtCaret(editor, " -> ");
}
});
result.add(builder.withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE));
}).withIcon(AllIcons.Nodes.AnonymousClass);
LookupElement lambdaElement = builder.withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
if (prioritize) {
lambdaElement = PrioritizedLookupElement.withPriority(lambdaElement, 1);
}
result.add(lambdaElement);
if (params.length == 1) {
final PsiType expectedReturnType = substitutor.substitute(functionalInterfaceMethod.getReturnType());
if (expectedReturnType != null) {
final PsiClass paramClass = PsiUtil.resolveClassInClassTypeOnly(params[0].getType());
if (paramClass != null && !paramClass.hasTypeParameters()) {
final Set<String> visited = new HashSet<String>();
for (PsiMethod psiMethod : paramClass.getAllMethods()) {
final PsiType returnType = psiMethod.getReturnType();
if (returnType != null &&
psiMethod.getParameterList().getParametersCount() == 0 &&
visited.add(psiMethod.getName()) &&
!psiMethod.hasModifierProperty(PsiModifier.STATIC) &&
JavaResolveUtil.isAccessible(psiMethod, null, psiMethod.getModifierList(), originalPosition, null, null) &&
TypeConversionUtil.isAssignable(expectedReturnType, returnType)) {
LookupElement methodRefLookupElement = LookupElementBuilder
.create(psiMethod)
.withPresentableText(paramClass.getName() + "::" + psiMethod.getName())
.withInsertHandler(new InsertHandler<LookupElement>() {
@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);
}
})
.withIcon(AllIcons.Nodes.AnonymousClass)
.withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
if (prioritize && psiMethod.getContainingClass() == paramClass) {
methodRefLookupElement = PrioritizedLookupElement.withPriority(methodRefLookupElement, 1);
}
result.add(methodRefLookupElement);
}
}
}
}
}
}
}
}
@@ -225,9 +225,7 @@ public class JavaCompletionContributor extends CompletionContributor {
}
if (JavaSmartCompletionContributor.LAMBDA.accepts(parameters.getPosition())) {
for (LookupElement element : LambdaCompletionProvider.getLambdaVariants(parameters)) {
result.addElement(PrioritizedLookupElement.withPriority(element, 1));
}
result.addAllElements(FunctionalExpressionCompletionProvider.getLambdaVariants(parameters, true));
}
PrefixMatcher matcher = result.getPrefixMatcher();
@@ -359,7 +359,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
}
});
extend(CompletionType.SMART, LAMBDA, new LambdaCompletionProvider());
extend(CompletionType.SMART, LAMBDA, new FunctionalExpressionCompletionProvider());
extend(CompletionType.SMART, METHOD_REFERENCE, new MethodReferenceCompletionProvider());
}
@@ -0,0 +1,13 @@
import java.util.List;
class MyTest {
void m(List<Person> l) {
l.stream().map(Person::getName)
}
}
interface Person {
String getName();
}
@@ -0,0 +1,13 @@
import java.util.List;
class MyTest {
void m(List<Person> l) {
l.stream().map(<caret>)
}
}
interface Person {
String getName();
}
@@ -123,6 +123,10 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase {
doTest(false);
}
public void testSimpleMethodReference() throws Exception {
doTest(true);
}
private void doTest() {
doTest(true);
}