completion: place functional expressions after variables of expected type (IDEA-174428, IDEA-178600)

This commit is contained in:
peter
2017-09-15 22:04:11 +02:00
parent 2f1cb559a3
commit dd920e884e
6 changed files with 54 additions and 6 deletions
@@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
@@ -53,6 +54,7 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider<C
JavaCompletionUtil.insertClassReference(psiClass, context.getFile(), start, start + insertedName.length());
}
};
static final Key<Boolean> FUNCTIONAL_EXPR_ITEM = Key.create("FUNCTIONAL_EXPR_ITEM");
private static boolean isLambdaContext(@NotNull PsiElement element) {
final PsiElement rulezzRef = element.getParent();
@@ -105,13 +107,16 @@ public class FunctionalExpressionCompletionProvider extends CompletionProvider<C
.withPresentableText(paramsString + " -> {}")
.withTypeText(functionalInterfaceType.getPresentableText())
.withIcon(AllIcons.Nodes.Function);
LookupElement lambdaElement = builder.withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
result.consume(smart ? lambdaElement : PrioritizedLookupElement.withPriority(lambdaElement, 1));
builder.putUserData(FUNCTIONAL_EXPR_ITEM, true);
result.consume(builder.withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE));
}
addMethodReferenceVariants(
smart, addInheritors, parameters, matcher, functionalInterfaceType, functionalInterfaceMethod, params, originalPosition, substitutor,
element -> result.consume(smart ? JavaSmartCompletionContributor.decorate(element, Arrays.asList(expectedTypes)) : element));
element -> {
element.putUserData(FUNCTIONAL_EXPR_ITEM, true);
result.consume(smart ? JavaSmartCompletionContributor.decorate(element, Arrays.asList(expectedTypes)) : element);
});
}
}
}
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.codeInsight.lookup.TypedLookupItem;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.Key;
@@ -133,6 +134,8 @@ public class PreferByKindWeigher extends LookupElementWeigher {
annoMethod,
probableKeyword,
castVariable,
expectedTypeVariable,
funExpr,
variable,
getter,
qualifiedWithField,
@@ -171,13 +174,17 @@ public class PreferByKindWeigher extends LookupElementWeigher {
if (object instanceof PsiLocalVariable || object instanceof PsiParameter ||
object instanceof PsiThisExpression ||
object instanceof PsiField && !((PsiField)object).hasModifierProperty(PsiModifier.STATIC)) {
return MyResult.variable;
return isExpectedTypeItem(item) ? MyResult.expectedTypeVariable : MyResult.variable;
}
if (object instanceof String && item.getUserData(JavaCompletionUtil.SUPER_METHOD_PARAMETERS) == Boolean.TRUE) {
return MyResult.superMethodParameters;
}
if (item.getUserData(FunctionalExpressionCompletionProvider.FUNCTIONAL_EXPR_ITEM) != null) {
return MyResult.funExpr;
}
if (object instanceof PsiMethod) {
PsiClass containingClass = ((PsiMethod)object).getContainingClass();
if (containingClass != null && CommonClassNames.JAVA_UTIL_COLLECTIONS.equals(containingClass.getQualifiedName())) {
@@ -248,6 +255,12 @@ public class PreferByKindWeigher extends LookupElementWeigher {
return MyResult.normal;
}
private boolean isExpectedTypeItem(@NotNull LookupElement item) {
TypedLookupItem typed = item.as(TypedLookupItem.CLASS_CONDITION_KEY);
PsiType itemType = typed == null ? null : typed.getType();
return itemType != null && Arrays.stream(myExpectedTypes).anyMatch(info -> info.getType().isAssignableFrom(itemType));
}
@NotNull
private ThreeState isProbableKeyword(String keyword) {
if (PsiKeyword.RETURN.equals(keyword)) {
@@ -0,0 +1,10 @@
class Foo {
void processImports(java.util.function.Predicate<String> pr) {}
{
processImports(<caret>);
}
static <T> T getSomeGenericValue(T t) {}
}
@@ -0,0 +1,11 @@
interface I {
void foo(String out);
}
class Foo {
I output;
{
I r = out<caret>
}
}
@@ -106,8 +106,8 @@ class Test {
void bar(int i) {}
}"""
def items = myFixture.completeBasic()
assert LookupElementPresentation.renderElement(items[0]).itemText == 'x -> {}'
assert items.find { LookupElementPresentation.renderElement(it).itemText.contains('this::bar') } != null
assert items.any { LookupElementPresentation.renderElement(it).itemText == 'x -> {}' }
assert items.any { LookupElementPresentation.renderElement(it).itemText.contains('this::bar') }
}
void "test suggest receiver method reference"() {
@@ -301,6 +301,11 @@ class Test88 {
checkResultByFileName()
}
void testPreferVariableToLambda() {
configureByTestName()
myFixture.assertPreferredCompletionItems 0, 'output', 'out -> '
}
private checkResultByFileName() {
checkResultByFile(getTestName(false) + "_after.java")
}
@@ -258,4 +258,8 @@ public void testConvertToObjectStream() {
public void testNewHashMapTypeArguments() { doTest(false); }
public void testNewMapTypeArguments() { doTest(false); }
public void testPreferLambdaOverGenericGetter() {
configureByTestName();
myFixture.assertPreferredCompletionItems(0, "isEmpty", "s -> ", "getSomeGenericValue");
}
}