mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-165006 Support chained completion for method references in Java 8+
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
{
|
||||
Runnable r = Syst::setOu<caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
{
|
||||
Runnable r = System::setOut;<caret>
|
||||
}
|
||||
}
|
||||
+18
-10
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user