From 979636e035b02e9f466bfa5fd4ddd246d70fbf62 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 9 Mar 2012 21:27:52 +0400 Subject: [PATCH] extract TypeArgumentCompletionProvider --- .../completion/JavaCompletionSorting.java | 2 +- .../JavaSmartCompletionContributor.java | 94 +----------- .../TypeArgumentCompletionProvider.java | 138 ++++++++++++++++++ 3 files changed, 140 insertions(+), 94 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java index 15ec279dcd43..b8d00967cad7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java @@ -246,7 +246,7 @@ public class JavaCompletionSorting { myExpectedTypes = expectedTypes; myParameters = parameters; - final Pair pair = JavaSmartCompletionContributor.getTypeParameterInfo(parameters.getPosition()); + final Pair pair = TypeArgumentCompletionProvider.getTypeParameterInfo(parameters.getPosition()); myTypeParameter = pair == null ? null : pair.first.getTypeParameters()[pair.second.intValue()]; myLocation = new CompletionLocation(myParameters); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java index ad08187d2b45..d2432ea49eb4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java @@ -18,7 +18,6 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.*; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; import com.intellij.patterns.ElementPattern; import com.intellij.patterns.PsiJavaPatterns; import com.intellij.psi.*; @@ -34,10 +33,8 @@ import com.intellij.psi.impl.source.PsiLabelReference; import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.javadoc.PsiDocTag; -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.ProcessingContext; import com.intellij.util.ReflectionCache; @@ -286,63 +283,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor { } }); - extend(CompletionType.SMART, psiElement().inside(psiElement(PsiReferenceParameterList.class)), - new CompletionProvider() { - - protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext processingContext, @NotNull final CompletionResultSet resultSet) { - final PsiElement context = parameters.getPosition(); - - final Pair pair = getTypeParameterInfo(context); - if (pair == null) return; - - final PsiClass referencedClass = pair.first; - final int parameterIndex = pair.second.intValue(); - final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters(); - final PsiTypeParameter targetParameter = typeParameters[parameterIndex]; - - boolean isLast = parameterIndex == typeParameters.length - 1; - final TailType tail = isLast ? new CharTailType('>') : TailType.COMMA; - - PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper(); - final PsiType[] psiTypes = ExpectedTypesGetter.getExpectedTypes(context, false); - if (psiTypes.length > 0) { - for (PsiType type : psiTypes) { - if (!(type instanceof PsiClassType)) continue; - final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics(); - final PsiClass typeClass = result.getElement(); - final PsiSubstitutor substitutor = result.getSubstitutor(); - - if (!InheritanceUtil.isInheritorOrSelf(referencedClass, typeClass, true)) continue; - - final PsiSubstitutor currentSubstitutor = - TypeConversionUtil.getClassSubstitutor(typeClass, referencedClass, PsiSubstitutor.EMPTY); - for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(typeClass)) { - final PsiType argSubstitution = substitutor.substitute(parameter); - final PsiType paramSubstitution = currentSubstitutor.substitute(parameter); - final PsiType substitution = resolveHelper - .getSubstitutionForTypeParameter(targetParameter, paramSubstitution, argSubstitution, false, - PsiUtil.getLanguageLevel(context)); - if (substitution != null && substitution != PsiType.NULL) { - final LookupItem item = PsiTypeLookupItem.createLookupItem(substitution, context); - resultSet.addElement(TailTypeDecorator.withTail(item.setInsertHandler(new DefaultInsertHandler()), tail)); - } - } - } - } else { - final List typeList = Collections.singletonList((PsiClassType)TypeConversionUtil.typeParameterErasure(targetParameter)); - JavaInheritorsGetter - .processInheritors(parameters, typeList, resultSet.getPrefixMatcher(), new Consumer() { - public void consume(final PsiType type) { - final PsiClass psiClass = PsiUtil.resolveClassInType(type); - if (psiClass == null) return; - - resultSet.addElement(TailTypeDecorator.withTail(new JavaPsiClassReferenceElement(psiClass), tail)); - } - }); - - } - } - }); + extend(CompletionType.SMART, psiElement().inside(psiElement(PsiReferenceParameterList.class)), new TypeArgumentCompletionProvider()); extend(CompletionType.SMART, AFTER_NEW, new JavaInheritorsGetter(ConstructorInsertHandler.SMART_INSTANCE)); @@ -409,39 +350,6 @@ public class JavaSmartCompletionContributor extends CompletionContributor { return new JavaPsiClassReferenceElement(psiClass); } - @Nullable - public static Pair getTypeParameterInfo(PsiElement context) { - final PsiReferenceParameterList parameterList = PsiTreeUtil.getContextOfType(context, PsiReferenceParameterList.class, true); - if (parameterList == null) return null; - - PsiElement parent = parameterList.getParent(); - if (!(parent instanceof PsiJavaCodeReferenceElement)) return null; - - final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)parent; - final int parameterIndex; - - int index = 0; - final PsiTypeElement typeElement = PsiTreeUtil.getContextOfType(context, PsiTypeElement.class, true); - if(typeElement != null){ - final PsiTypeElement[] elements = referenceElement.getParameterList().getTypeParameterElements(); - while (index < elements.length) { - final PsiTypeElement element = elements[index++]; - if(element == typeElement) break; - } - } - parameterIndex = index - 1; - - if(parameterIndex < 0) return null; - final PsiElement target = referenceElement.resolve(); - if(!(target instanceof PsiClass)) return null; - - final PsiClass referencedClass = (PsiClass)target; - final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters(); - if(typeParameters.length <= parameterIndex) return null; - - return Pair.create(referencedClass, parameterIndex); - } - @NotNull public static ExpectedTypeInfo[] getExpectedTypes(final CompletionParameters parameters) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java new file mode 100644 index 000000000000..31d7b410b4ca --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2012 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.CharTailType; +import com.intellij.codeInsight.TailType; +import com.intellij.codeInsight.lookup.PsiTypeLookupItem; +import com.intellij.codeInsight.lookup.TailTypeDecorator; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.filters.getters.ExpectedTypesGetter; +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.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** +* @author peter +*/ +class TypeArgumentCompletionProvider extends CompletionProvider { + + protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext processingContext, @NotNull final CompletionResultSet resultSet) { + final PsiElement context = parameters.getPosition(); + + final Pair pair = getTypeParameterInfo(context); + if (pair == null) return; + + final PsiType[] psiTypes = ExpectedTypesGetter.getExpectedTypes(context, false); + if (psiTypes.length > 0) { + for (PsiType type : psiTypes) { + if (type instanceof PsiClassType) { + fillExpectedTypeArgs(resultSet, context, pair.first, pair.second, ((PsiClassType)type).resolveGenerics()); + } + } + } else { + addInheritors(parameters, resultSet, pair.first, pair.second); + } + } + + private static void fillExpectedTypeArgs(CompletionResultSet resultSet, + PsiElement context, + PsiClass actualClass, + int index, + PsiClassType.ClassResolveResult expectedType) { + final PsiClass expectedClass = expectedType.getElement(); + + if (!InheritanceUtil.isInheritorOrSelf(actualClass, expectedClass, true)) return; + assert expectedClass != null; + + final PsiSubstitutor currentSubstitutor = TypeConversionUtil.getClassSubstitutor(expectedClass, actualClass, PsiSubstitutor.EMPTY); + assert currentSubstitutor != null; + + PsiTypeParameter[] params = actualClass.getTypeParameters(); + for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(expectedClass)) { + final PsiType argSubstitution = expectedType.getSubstitutor().substitute(parameter); + final PsiType paramSubstitution = currentSubstitutor.substitute(parameter); + final PsiType substitution = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper() + .getSubstitutionForTypeParameter(params[index], paramSubstitution, argSubstitution, false, PsiUtil.getLanguageLevel(context)); + if (substitution != null && substitution != PsiType.NULL) { + resultSet.addElement(TailTypeDecorator.withTail(PsiTypeLookupItem.createLookupItem(substitution, context), getTail(actualClass, index))); + } + } + } + + private static void addInheritors(CompletionParameters parameters, + final CompletionResultSet resultSet, + final PsiClass referencedClass, + final int parameterIndex) { + final List typeList = Collections.singletonList((PsiClassType)TypeConversionUtil.typeParameterErasure( + referencedClass.getTypeParameters()[parameterIndex])); + JavaInheritorsGetter.processInheritors(parameters, typeList, resultSet.getPrefixMatcher(), new Consumer() { + public void consume(final PsiType type) { + final PsiClass psiClass = PsiUtil.resolveClassInType(type); + if (psiClass == null) return; + + resultSet.addElement(TailTypeDecorator.withTail(new JavaPsiClassReferenceElement(psiClass), + getTail(referencedClass, parameterIndex))); + } + }); + } + + private static TailType getTail(PsiClass referencedClass, int parameterIndex) { + return parameterIndex == referencedClass.getTypeParameters().length - 1 ? new CharTailType('>') : TailType.COMMA; + } + + @Nullable + static Pair getTypeParameterInfo(PsiElement context) { + final PsiReferenceParameterList parameterList = PsiTreeUtil.getContextOfType(context, PsiReferenceParameterList.class, true); + if (parameterList == null) return null; + + PsiElement parent = parameterList.getParent(); + if (!(parent instanceof PsiJavaCodeReferenceElement)) return null; + + final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)parent; + final int parameterIndex; + + int index = 0; + final PsiTypeElement typeElement = PsiTreeUtil.getContextOfType(context, PsiTypeElement.class, true); + if(typeElement != null){ + final PsiTypeElement[] elements = referenceElement.getParameterList().getTypeParameterElements(); + while (index < elements.length) { + final PsiTypeElement element = elements[index++]; + if(element == typeElement) break; + } + } + parameterIndex = index - 1; + + if(parameterIndex < 0) return null; + final PsiElement target = referenceElement.resolve(); + if(!(target instanceof PsiClass)) return null; + + final PsiClass referencedClass = (PsiClass)target; + final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters(); + if(typeParameters.length <= parameterIndex) return null; + + return Pair.create(referencedClass, parameterIndex); + } +}