IDEA-165006 Support chained completion for method references in Java 8+

This commit is contained in:
peter
2017-06-05 19:20:02 +02:00
parent c578a4ede7
commit 03a663c63e
6 changed files with 51 additions and 24 deletions
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.TypedLookupItem;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.ClassConditionKey;
import com.intellij.openapi.util.Key;
@@ -39,19 +38,23 @@ import java.util.Set;
*/
public class JavaChainLookupElement extends LookupElementDecorator<LookupElement> implements TypedLookupItem {
public static final Key<Boolean> CHAIN_QUALIFIER = Key.create("CHAIN_QUALIFIER");
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaChainLookupElement");
public static final ClassConditionKey<JavaChainLookupElement> CLASS_CONDITION_KEY = ClassConditionKey.create(JavaChainLookupElement.class);
private final LookupElement myQualifier;
private final String mySeparator;
public JavaChainLookupElement(LookupElement qualifier, LookupElement main) {
this(qualifier, main, ".");
}
public JavaChainLookupElement(LookupElement qualifier, LookupElement main, String separator) {
super(main);
myQualifier = qualifier;
mySeparator = separator;
}
@NotNull
@Override
public String getLookupString() {
return maybeAddParentheses(myQualifier.getLookupString()) + "." + getDelegate().getLookupString();
return maybeAddParentheses(myQualifier.getLookupString()) + mySeparator + getDelegate().getLookupString();
}
public LookupElement getQualifier() {
@@ -70,7 +73,7 @@ public class JavaChainLookupElement extends LookupElementDecorator<LookupElement
@NotNull
@Override
public String toString() {
return maybeAddParentheses(myQualifier.toString()) + "." + getDelegate();
return maybeAddParentheses(myQualifier.toString()) + mySeparator + getDelegate();
}
private String maybeAddParentheses(String s) {
@@ -98,7 +101,7 @@ public class JavaChainLookupElement extends LookupElementDecorator<LookupElement
myQualifier.renderElement(qualifierPresentation);
String name = maybeAddParentheses(qualifierPresentation.getItemText());
final String qualifierText = myQualifier.as(CastingLookupElementDecorator.CLASS_CONDITION_KEY) != null ? "(" + name + ")" : name;
presentation.setItemText(qualifierText + "." + presentation.getItemText());
presentation.setItemText(qualifierText + mySeparator + presentation.getItemText());
if (myQualifier instanceof JavaPsiClassReferenceElement) {
presentation.appendTailText(((JavaPsiClassReferenceElement)myQualifier).getLocationString(), false);
@@ -129,9 +132,9 @@ public class JavaChainLookupElement extends LookupElementDecorator<LookupElement
if (atTail != ';') {
return;
}
document.replaceString(qualifierContext.getTailOffset(), qualifierContext.getTailOffset() + 1, ".");
document.replaceString(qualifierContext.getTailOffset(), qualifierContext.getTailOffset() + 1, mySeparator);
CompletionUtil.emulateInsertion(getDelegate(), qualifierContext.getTailOffset() + 1, context);
CompletionUtil.emulateInsertion(getDelegate(), qualifierContext.getTailOffset() + mySeparator.length(), context);
context.commitDocument();
int formatStart = context.getOffset(oldStart);
@@ -145,11 +145,12 @@ public class JavaNoVariantsDelegator extends CompletionContributor {
for (LookupElement base : suggestQualifierItems(parameters, (PsiJavaCodeReferenceElement)qualifier, filter)) {
PsiType type = JavaCompletionUtil.getLookupElementType(base);
if (type != null && !PsiType.VOID.equals(type)) {
PsiReferenceExpression ref = ReferenceExpressionCompletionContributor.createMockReference(position, type, base);
String separator = parent instanceof PsiMethodReferenceExpression ? "::" : ".";
PsiReferenceExpression ref = ReferenceExpressionCompletionContributor.createMockReference(position, type, base, separator);
if (ref != null) {
for (final LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters,
result.getPrefixMatcher())) {
qualifiedCollector.addElement(JavaCompletionUtil.highlightIfNeeded(null, new JavaChainLookupElement(base, item), item.getObject(), position));
for (LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters,
result.getPrefixMatcher())) {
qualifiedCollector.addElement(JavaCompletionUtil.highlightIfNeeded(null, new JavaChainLookupElement(base, item, separator), item.getObject(), position));
}
}
}
@@ -213,13 +213,18 @@ public class ReferenceExpressionCompletionContributor {
}
@Nullable
public static PsiReferenceExpression createMockReference(final PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) {
static PsiReferenceExpression createMockReference(PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) {
return createMockReference(place, qualifierType, qualifierItem, ".");
}
@Nullable
static PsiReferenceExpression createMockReference(PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem, String separator) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(place.getProject());
if (qualifierItem.getObject() instanceof PsiClass) {
final String qname = ((PsiClass)qualifierItem.getObject()).getQualifiedName();
if (qname == null) return null;
final String text = qname + ".xxx";
String text = qname + separator + "xxx";
try {
final PsiExpression expr = factory.createExpressionFromText(text, place);
if (expr instanceof PsiReferenceExpression) {
@@ -233,7 +238,7 @@ public class ReferenceExpressionCompletionContributor {
}
}
return (PsiReferenceExpression) factory.createExpressionFromText("xxx.xxx", JavaCompletionUtil
return (PsiReferenceExpression) factory.createExpressionFromText("xxx" + separator + "xxx", JavaCompletionUtil
.createContextWithXxxVariable(place, qualifierType));
}
@@ -0,0 +1,5 @@
class A {
{
Runnable r = Syst::setOu<caret>
}
}
@@ -0,0 +1,5 @@
class A {
{
Runnable r = System::setOut;<caret>
}
}
@@ -212,37 +212,37 @@ class Test88 {
void testCollectorsToList() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('toList') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testStaticallyImportedCollectorsToList() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('collect(toList())') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testAllCollectors() {
configureByTestName()
myFixture.assertPreferredCompletionItems 0, 'collect', 'collect', 'collect(Collectors.toCollection())', 'collect(Collectors.toList())', 'collect(Collectors.toSet())'
selectItem(myItems.find { it.lookupString.contains('toCollection') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testCollectorsToSet() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('toSet') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testNoExplicitTypeArgsInTernary() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('empty') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testCallBeforeLambda() {
configureByTestName()
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testLambdaInAmbiguousCall() {
@@ -264,13 +264,13 @@ class Test88 {
void testNoSemicolonAfterVoidMethodInLambda() {
configureByTestName()
myFixture.type('l\t')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testFinishMethodReferenceWithColon() {
configureByTestName()
myFixture.type(':')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testPreferLocalsOverMethodRefs() {
@@ -286,14 +286,22 @@ class Test88 {
"}")
configureByTestName()
myFixture.type('\n')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testOverrideMethodAsDefault() {
configureByTestName()
assert LookupElementPresentation.renderElement(myFixture.lookupElements[0]).itemText == 'default void run'
myFixture.type('\t')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testChainedMethodReference() {
configureByTestName()
checkResultByFileName()
}
private checkResultByFileName() {
checkResultByFile(getTestName(false) + "_after.java")
}
}