mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
IDEA-140728 Suggest to automatically fill parameter of Stream#collect() method calls with standard Collectors instances
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.ExpectedTypeInfo;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.codeInsight.lookup.TypedLookupItem;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.*;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class CollectConversion {
|
||||
|
||||
static void addCollectConversion(PsiReferenceExpression ref, Collection<ExpectedTypeInfo> expectedTypes, Consumer<LookupElement> consumer) {
|
||||
final PsiExpression qualifier = ref.getQualifierExpression();
|
||||
PsiType component = qualifier == null ? null : PsiUtil.substituteTypeParameter(qualifier.getType(), JAVA_UTIL_STREAM_STREAM, 0, true);
|
||||
if (component == null) return;
|
||||
|
||||
JavaPsiFacade facade = JavaPsiFacade.getInstance(ref.getProject());
|
||||
GlobalSearchScope scope = ref.getResolveScope();
|
||||
PsiClass list = facade.findClass(JAVA_UTIL_LIST, scope);
|
||||
PsiClass set = facade.findClass(JAVA_UTIL_SET, scope);
|
||||
if (facade.findClass(JAVA_UTIL_STREAM_COLLECTORS, scope) == null || list == null || set == null) return;
|
||||
|
||||
boolean hasList = false;
|
||||
boolean hasSet = false;
|
||||
for (ExpectedTypeInfo info : expectedTypes) {
|
||||
PsiType type = info.getDefaultType();
|
||||
PsiClass expectedClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
PsiType expectedComponent = PsiUtil.extractIterableTypeParameter(type, true);
|
||||
if (expectedClass == null || expectedComponent == null || !TypeConversionUtil.isAssignable(expectedComponent, component)) continue;
|
||||
|
||||
if (!hasList && InheritanceUtil.isInheritorOrSelf(list, expectedClass, true)) {
|
||||
hasList = true;
|
||||
consumer.consume(new MyLookupElement("toList", type));
|
||||
}
|
||||
|
||||
if (!hasSet && InheritanceUtil.isInheritorOrSelf(set, expectedClass, true)) {
|
||||
hasSet = true;
|
||||
consumer.consume(new MyLookupElement("toSet", type));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyLookupElement extends LookupElement implements TypedLookupItem {
|
||||
private final String myLookupString;
|
||||
private final String myTypeText;
|
||||
private final String myMethodName;
|
||||
@NotNull private final PsiType myExpectedType;
|
||||
|
||||
MyLookupElement(String methodName, @NotNull PsiType expectedType) {
|
||||
myMethodName = methodName;
|
||||
myExpectedType = expectedType;
|
||||
myLookupString = "collect(Collectors." + myMethodName + "())";
|
||||
myTypeText = myExpectedType.getPresentableText();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getLookupString() {
|
||||
return myLookupString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllLookupStrings() {
|
||||
return ContainerUtil.newHashSet(myLookupString, myMethodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderElement(LookupElementPresentation presentation) {
|
||||
super.renderElement(presentation);
|
||||
presentation.setTypeText(myTypeText);
|
||||
presentation.setIcon(PlatformIcons.METHOD_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(InsertionContext context) {
|
||||
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), getInsertString());
|
||||
context.commitDocument();
|
||||
|
||||
PsiMethodCallExpression call =
|
||||
PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiMethodCallExpression.class, false);
|
||||
if (call == null) return;
|
||||
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if (args.length != 1 || !(args[0] instanceof PsiMethodCallExpression)) return;
|
||||
|
||||
JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(args[0]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getInsertString() {
|
||||
return "collect(" + JAVA_UTIL_STREAM_COLLECTORS + "." + myMethodName + "())";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiType getType() {
|
||||
return myExpectedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.ExpectedTypeInfo;
|
||||
import com.intellij.codeInsight.ExpectedTypesProvider;
|
||||
import com.intellij.codeInsight.TailType;
|
||||
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix;
|
||||
@@ -233,6 +235,13 @@ public class JavaCompletionContributor extends CompletionContributor {
|
||||
new JavaInheritorsGetter(ConstructorInsertHandler.BASIC_INSTANCE).generateVariants(parameters, matcher, inheritors);
|
||||
}
|
||||
|
||||
PsiElement parent = position.getParent();
|
||||
if (parent instanceof PsiReferenceExpression) {
|
||||
final List<ExpectedTypeInfo> expected = Arrays.asList(ExpectedTypesProvider.getExpectedTypes((PsiExpression)parent, true));
|
||||
CollectConversion.addCollectConversion((PsiReferenceExpression)parent, expected,
|
||||
JavaSmartCompletionContributor.decorateWithoutTypeCheck(result, expected));
|
||||
}
|
||||
|
||||
if (IMPORT_REFERENCE.accepts(position)) {
|
||||
result.addElement(LookupElementBuilder.create("*"));
|
||||
}
|
||||
@@ -254,7 +263,6 @@ public class JavaCompletionContributor extends CompletionContributor {
|
||||
|
||||
addAllClasses(parameters, result, inheritors);
|
||||
|
||||
final PsiElement parent = position.getParent();
|
||||
if (parent instanceof PsiReferenceExpression &&
|
||||
!((PsiReferenceExpression)parent).isQualified() &&
|
||||
parameters.isExtendedCompletion() &&
|
||||
|
||||
@@ -215,12 +215,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
|
||||
extend(CompletionType.SMART, INSIDE_EXPRESSION, new ExpectedTypeBasedCompletionProvider() {
|
||||
@Override
|
||||
protected void addCompletions(final CompletionParameters params, final CompletionResultSet result, final Collection<ExpectedTypeInfo> _infos) {
|
||||
Consumer<LookupElement> noTypeCheck = new Consumer<LookupElement>() {
|
||||
@Override
|
||||
public void consume(final LookupElement lookupElement) {
|
||||
result.addElement(decorate(lookupElement, _infos));
|
||||
}
|
||||
};
|
||||
Consumer<LookupElement> noTypeCheck = decorateWithoutTypeCheck(result, _infos);
|
||||
|
||||
THashSet<ExpectedTypeInfo> mergedInfos = new THashSet<ExpectedTypeInfo>(_infos, EXPECTED_TYPE_INFO_STRATEGY);
|
||||
List<Runnable> chainedEtc = new ArrayList<Runnable>();
|
||||
@@ -231,6 +226,11 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
addExpectedTypeMembers(params, mergedInfos, true, noTypeCheck);
|
||||
|
||||
PsiElement parent = params.getPosition().getParent();
|
||||
if (parent instanceof PsiReferenceExpression) {
|
||||
CollectConversion.addCollectConversion((PsiReferenceExpression)parent, mergedInfos, noTypeCheck);
|
||||
}
|
||||
|
||||
for (final ExpectedTypeInfo info : mergedInfos) {
|
||||
BasicExpressionCompletionContributor.fillCompletionVariants(new JavaSmartCompletionParameters(params, info), new Consumer<LookupElement>() {
|
||||
@Override
|
||||
@@ -363,6 +363,16 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
|
||||
extend(CompletionType.SMART, METHOD_REFERENCE, new MethodReferenceCompletionProvider());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static Consumer<LookupElement> decorateWithoutTypeCheck(final CompletionResultSet result, final Collection<ExpectedTypeInfo> infos) {
|
||||
return new Consumer<LookupElement>() {
|
||||
@Override
|
||||
public void consume(final LookupElement lookupElement) {
|
||||
result.addElement(decorate(lookupElement, infos));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void addExpectedTypeMembers(CompletionParameters params,
|
||||
THashSet<ExpectedTypeInfo> mergedInfos,
|
||||
boolean quick,
|
||||
|
||||
@@ -67,7 +67,7 @@ public class SmartCompletionDecorator extends TailTypeDecorator<LookupElement> {
|
||||
|
||||
final PsiExpression enclosing = PsiTreeUtil.getContextOfType(myPosition, PsiExpression.class, true);
|
||||
|
||||
if (enclosing != null && object instanceof PsiElement) {
|
||||
if (enclosing != null) {
|
||||
final PsiType type = JavaCompletionUtil.getLookupElementType(delegate);
|
||||
final TailType itemType = item != null ? item.getTailType() : TailType.NONE;
|
||||
if (type != null && type.isValid()) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class Foo {
|
||||
void m() {
|
||||
List<CharSequence> l = Arrays.asList("a", "b").stream().collect(Collectors.toList());<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.util.*;
|
||||
|
||||
class Foo {
|
||||
void m() {
|
||||
List<CharSequence> l = Arrays.asList("a", "b").stream().toli<caret>
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,6 @@ class FooFactory {
|
||||
|
||||
class XXX {
|
||||
{
|
||||
Foo f = FooFactory.createFoo()<caret>
|
||||
Foo f = FooFactory.createFoo();<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.util.*;
|
||||
|
||||
class Foo {
|
||||
void m() {
|
||||
List<CharSequence> l = Arrays.asList("a", "b").stream().colle<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class Foo {
|
||||
void m() {
|
||||
List<CharSequence> l = Arrays.asList("a", "b").stream().collect(Collectors.toList());<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.util.*;
|
||||
|
||||
class Foo {
|
||||
void m() {
|
||||
Iterable<CharSequence> l = Arrays.asList("a", "b").stream().colle<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class Foo {
|
||||
void m() {
|
||||
Iterable<CharSequence> l = Arrays.asList("a", "b").stream().collect(Collectors.toSet());<caret>
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,10 @@ public void testConvertToObjectStream() {
|
||||
checkResultByFile("/" + getTestName(false) + "-out.java");
|
||||
}
|
||||
|
||||
public void testCollectorsToList() {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class Normal8CompletionTest extends LightFixtureCompletionTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_LATEST;
|
||||
return JAVA_8;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,4 +110,16 @@ class Test88 {
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
public void testCollectorsToList() {
|
||||
configureByTestName()
|
||||
selectItem(myItems.find { it.lookupString.contains('toList') })
|
||||
checkResultByFile(getTestName(false) + "_after.java")
|
||||
}
|
||||
|
||||
public void testCollectorsToSet() {
|
||||
configureByTestName()
|
||||
selectItem(myItems.find { it.lookupString.contains('toSet') })
|
||||
checkResultByFile(getTestName(false) + "_after.java")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ public interface CommonClassNames {
|
||||
@NonNls String JAVA_UTIL_CONCURRENT_CALLABLE = "java.util.concurrent.Callable";
|
||||
|
||||
@NonNls String JAVA_UTIL_STREAM_BASE_STREAM = "java.util.stream.BaseStream";
|
||||
@NonNls String JAVA_UTIL_STREAM_STREAM = "java.util.stream.Stream";
|
||||
@NonNls String JAVA_UTIL_STREAM_COLLECTORS = "java.util.stream.Collectors";
|
||||
|
||||
@NonNls String JAVA_LANG_INVOKE_MH_POLYMORPHIC = "java.lang.invoke.MethodHandle.PolymorphicSignature";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user