blob: 112b243e0ea30902dba87a64e271238b79cf373c [file] [log] [blame]
/*
* Copyright 2000-2014 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.*;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.codeInsight.completion.scope.CompletionElement;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.*;
import com.intellij.lang.StdLanguages;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PsiJavaPatterns;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.impl.FakePsiElement;
import com.intellij.psi.impl.light.LightVariableBuilder;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.scope.BaseScopeProcessor;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NullableFunction;
import com.intellij.util.PairConsumer;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.SideEffectChecker;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.patterns.PlatformPatterns.psiElement;
public class JavaCompletionUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionUtil");
public static final Key<PairFunction<PsiExpression, CompletionParameters, PsiType>> DYNAMIC_TYPE_EVALUATOR = Key.create("DYNAMIC_TYPE_EVALUATOR");
private static final Key<PsiType> QUALIFIER_TYPE_ATTR = Key.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier"
public static final OffsetKey LPAREN_OFFSET = OffsetKey.create("lparen");
public static final OffsetKey RPAREN_OFFSET = OffsetKey.create("rparen");
public static final OffsetKey ARG_LIST_END_OFFSET = OffsetKey.create("argListEnd");
static final NullableLazyKey<ExpectedTypeInfo[], CompletionLocation> EXPECTED_TYPES = NullableLazyKey.create("expectedTypes", new NullableFunction<CompletionLocation, ExpectedTypeInfo[]>() {
@Override
@Nullable
public ExpectedTypeInfo[] fun(final CompletionLocation location) {
if (PsiJavaPatterns.psiElement().beforeLeaf(PsiJavaPatterns.psiElement().withText("."))
.accepts(location.getCompletionParameters().getPosition())) {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
return JavaSmartCompletionContributor.getExpectedTypes(location.getCompletionParameters());
}
});
private static final ElementPattern<PsiElement> LEFT_PAREN = psiElement(JavaTokenType.LPARENTH).andOr(psiElement().withParent(
PsiExpressionList.class), psiElement().afterLeaf(".", PsiKeyword.NEW));
public static final Key<Boolean> SUPER_METHOD_PARAMETERS = Key.create("SUPER_METHOD_PARAMETERS");
@Nullable
public static Set<PsiType> getExpectedTypes(final CompletionParameters parameters) {
final PsiExpression expr = PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
if (expr != null) {
final Set<PsiType> set = new THashSet<PsiType>();
for (final ExpectedTypeInfo expectedInfo : JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
set.add(expectedInfo.getType());
}
return set;
}
return null;
}
public static final Key<List<PsiMethod>> ALL_METHODS_ATTRIBUTE = Key.create("allMethods");
public static PsiType getQualifierType(LookupItem item) {
return item.getUserData(QUALIFIER_TYPE_ATTR);
}
public static void completeVariableNameForRefactoring(Project project, Set<LookupElement> set, String prefix, PsiType varType, VariableKind varKind) {
final CamelHumpMatcher camelHumpMatcher = new CamelHumpMatcher(prefix);
JavaMemberNameCompletionContributor.completeVariableNameForRefactoring(project, set, camelHumpMatcher, varType, varKind, true, false);
}
public static String[] completeVariableNameForRefactoring(JavaCodeStyleManager codeStyleManager, @Nullable final PsiType varType,
final VariableKind varKind,
SuggestedNameInfo suggestedNameInfo) {
return JavaMemberNameCompletionContributor
.completeVariableNameForRefactoring(codeStyleManager, new CamelHumpMatcher(""), varType, varKind, suggestedNameInfo, true, false);
}
public static boolean isInExcludedPackage(@NotNull final PsiMember member, boolean allowInstanceInnerClasses) {
final String name = PsiUtil.getMemberQualifiedName(member);
if (name == null) return false;
if (!member.hasModifierProperty(PsiModifier.STATIC)) {
if (member instanceof PsiMethod || member instanceof PsiField) {
return false;
}
if (allowInstanceInnerClasses && member instanceof PsiClass && member.getContainingClass() != null) {
return false;
}
}
CodeInsightSettings cis = CodeInsightSettings.getInstance();
for (String excluded : cis.EXCLUDED_PACKAGES) {
if (name.equals(excluded) || name.startsWith(excluded + ".")) {
return true;
}
}
return false;
}
@SuppressWarnings({"unchecked"})
@NotNull
public static <T extends PsiType> T originalize(@NotNull T type) {
if (!type.isValid()) {
return type;
}
T result = (T)type.accept(new PsiTypeVisitor<PsiType>() {
@Override
public PsiType visitArrayType(final PsiArrayType arrayType) {
return new PsiArrayType(originalize(arrayType.getComponentType()));
}
@Override
public PsiType visitCapturedWildcardType(final PsiCapturedWildcardType capturedWildcardType) {
return PsiCapturedWildcardType.create(originalize(capturedWildcardType.getWildcard()), capturedWildcardType.getContext());
}
@Override
public PsiType visitClassType(final PsiClassType classType) {
final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
final PsiClass psiClass = classResolveResult.getElement();
final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
if (psiClass == null) return classType;
LOG.assertTrue(psiClass.isValid());
return new PsiImmediateClassType(CompletionUtil.getOriginalOrSelf(psiClass), originalize(substitutor));
}
@Override
public PsiType visitEllipsisType(final PsiEllipsisType ellipsisType) {
return new PsiEllipsisType(originalize(ellipsisType.getComponentType()));
}
@Override
public PsiType visitPrimitiveType(final PsiPrimitiveType primitiveType) {
return primitiveType;
}
@Override
public PsiType visitType(final PsiType type) {
return type;
}
@Override
public PsiType visitWildcardType(final PsiWildcardType wildcardType) {
final PsiType bound = wildcardType.getBound();
final PsiManager manager = wildcardType.getManager();
if (bound == null) return PsiWildcardType.createUnbounded(manager);
return wildcardType.isExtends() ? PsiWildcardType.createExtends(manager, bound) : PsiWildcardType.createSuper(manager, bound);
}
});
if (result == null) {
throw new AssertionError("Null result for type " + type + " of class " + type.getClass());
}
return result;
}
@Nullable
private static PsiSubstitutor originalize(@Nullable final PsiSubstitutor substitutor) {
if (substitutor == null) return null;
PsiSubstitutor originalSubstitutor = PsiSubstitutor.EMPTY;
for (final Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) {
final PsiType value = entry.getValue();
originalSubstitutor = originalSubstitutor.put(CompletionUtil.getOriginalOrSelf(entry.getKey()), value == null ? null : originalize(value));
}
return originalSubstitutor;
}
public static void initOffsets(final PsiFile file, final OffsetMap offsetMap) {
int offset = Math.max(offsetMap.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET),
offsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET));
PsiElement element = file.findElementAt(offset);
if (element instanceof PsiWhiteSpace &&
(!element.textContains('\n') ||
CodeStyleSettingsManager.getSettings(file.getProject()).getCommonSettings(JavaLanguage.INSTANCE).METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
)) {
element = file.findElementAt(element.getTextRange().getEndOffset());
}
if (element == null) return;
if (LEFT_PAREN.accepts(element)) {
offsetMap.addOffset(LPAREN_OFFSET, element.getTextRange().getStartOffset());
PsiElement list = element.getParent();
PsiElement last = list.getLastChild();
if (last instanceof PsiJavaToken && ((PsiJavaToken)last).getTokenType() == JavaTokenType.RPARENTH) {
offsetMap.addOffset(RPAREN_OFFSET, last.getTextRange().getStartOffset());
}
offsetMap.addOffset(ARG_LIST_END_OFFSET, list.getTextRange().getEndOffset());
}
}
public static void resetParensInfo(final OffsetMap offsetMap) {
offsetMap.removeOffset(LPAREN_OFFSET);
offsetMap.removeOffset(RPAREN_OFFSET);
offsetMap.removeOffset(ARG_LIST_END_OFFSET);
offsetMap.removeOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET);
}
@Nullable
public static List<? extends PsiElement> getAllPsiElements(final LookupElement item) {
List<PsiMethod> allMethods = item.getUserData(ALL_METHODS_ATTRIBUTE);
if (allMethods != null) return allMethods;
if (item.getObject() instanceof PsiElement) return Arrays.asList((PsiElement)item.getObject());
return null;
}
@Nullable
private static PsiType getPsiType(final Object o) {
if (o instanceof ResolveResult) {
return getPsiType(((ResolveResult)o).getElement());
}
if (o instanceof PsiVariable) {
return ((PsiVariable)o).getType();
}
else if (o instanceof PsiMethod) {
return ((PsiMethod)o).getReturnType();
}
else if (o instanceof PsiClass) {
final PsiClass psiClass = (PsiClass)o;
return JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory().createType(psiClass);
}
else if (o instanceof PsiExpression) {
return ((PsiExpression)o).getType();
}
return null;
}
@Nullable
public static PsiType getLookupElementType(final LookupElement element) {
TypedLookupItem typed = element.as(TypedLookupItem.CLASS_CONDITION_KEY);
if (typed != null) {
return typed.getType();
}
final PsiType qualifierType = getPsiType(element.getObject());
final LookupItem lookupItem = element.as(LookupItem.CLASS_CONDITION_KEY);
if (lookupItem != null) {
final Object o = lookupItem.getAttribute(LookupItem.TYPE);
if (o instanceof PsiType) {
return (PsiType)o;
}
final PsiSubstitutor substitutor = (PsiSubstitutor)lookupItem.getAttribute(LookupItem.SUBSTITUTOR);
if (substitutor != null) {
return substitutor.substitute(qualifierType);
}
}
return qualifierType;
}
@Nullable
public static PsiType getQualifiedMemberReferenceType(@Nullable PsiType qualifierType, @NotNull final PsiMember member) {
final Ref<PsiSubstitutor> subst = Ref.create(PsiSubstitutor.EMPTY);
class MyProcessor extends BaseScopeProcessor implements NameHint, ElementClassHint {
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
if (element == member) {
subst.set(state.get(PsiSubstitutor.KEY));
}
return true;
}
@Override
public String getName(@NotNull ResolveState state) {
return member.getName();
}
@Override
public boolean shouldProcess(DeclarationKind kind) {
return member instanceof PsiEnumConstant ? kind == DeclarationKind.ENUM_CONST :
member instanceof PsiField ? kind == DeclarationKind.FIELD :
kind == DeclarationKind.METHOD;
}
@Override
public <T> T getHint(@NotNull Key<T> hintKey) {
return hintKey == NameHint.KEY || hintKey == ElementClassHint.KEY ? (T)this : null;
}
}
PsiScopesUtil.processTypeDeclarations(qualifierType, member, new MyProcessor());
PsiType rawType = member instanceof PsiField ? ((PsiField) member).getType() :
member instanceof PsiMethod ? ((PsiMethod) member).getReturnType() :
JavaPsiFacade.getElementFactory(member.getProject()).createType((PsiClass)member);
return subst.get().substitute(rawType);
}
public static Set<LookupElement> processJavaReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter,
JavaCompletionProcessor.Options options,
final PrefixMatcher matcher, CompletionParameters parameters) {
final Set<LookupElement> set = new LinkedHashSet<LookupElement>();
final Condition<String> nameCondition = new Condition<String>() {
@Override
public boolean value(String s) {
return matcher.prefixMatches(s);
}
};
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
boolean checkInitialized = parameters.getInvocationCount() <= 1 && call != null && PsiKeyword.SUPER.equals(call.getMethodExpression().getText());
final JavaCompletionProcessor processor = new JavaCompletionProcessor(element, elementFilter, options.withInitialized(checkInitialized), nameCondition);
final PsiType plainQualifier = processor.getQualifierType();
PsiType qualifierType = plainQualifier;
PsiType runtimeQualifier = getQualifierCastType(javaReference, parameters);
if (runtimeQualifier != null) {
PsiType composite = qualifierType == null ? runtimeQualifier : PsiIntersectionType.createIntersection(qualifierType, runtimeQualifier);
PsiElement ctx = createContextWithXxxVariable(element, composite);
javaReference = (PsiReferenceExpression) JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText("xxx.xxx", ctx);
qualifierType = runtimeQualifier;
processor.setQualifierType(qualifierType);
}
javaReference.processVariants(processor);
final PsiTypeLookupItem castItem = runtimeQualifier == null ? null : PsiTypeLookupItem.createLookupItem(runtimeQualifier, (PsiReferenceExpression)javaReference);
final boolean pkgContext = inSomePackage(element);
final Set<PsiMember> mentioned = new THashSet<PsiMember>();
for (CompletionElement completionElement : processor.getResults()) {
for (LookupElement item : createLookupElements(completionElement, javaReference)) {
item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType);
final Object o = item.getObject();
if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) {
continue;
}
if (o instanceof PsiMember) {
if (isInExcludedPackage((PsiMember)o, true)) {
continue;
}
mentioned.add(CompletionUtil.getOriginalOrSelf((PsiMember)o));
}
set.add(highlightIfNeeded(qualifierType, castQualifier(item, castItem, plainQualifier, processor), o));
}
}
if (javaReference instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement)javaReference).isQualified()) {
final StaticMemberProcessor memberProcessor = new JavaStaticMemberProcessor(parameters);
memberProcessor.processMembersOfRegisteredClasses(matcher, new PairConsumer<PsiMember, PsiClass>() {
@Override
public void consume(PsiMember member, PsiClass psiClass) {
if (!mentioned.contains(member) && processor.satisfies(member, ResolveState.initial())) {
set.add(memberProcessor.createLookupElement(member, psiClass, true));
}
}
});
}
return set;
}
@Nullable
private static PsiType getQualifierCastType(PsiJavaReference javaReference, CompletionParameters parameters) {
if (javaReference instanceof PsiReferenceExpression) {
final PsiReferenceExpression refExpr = (PsiReferenceExpression)javaReference;
final PsiExpression qualifier = refExpr.getQualifierExpression();
if (qualifier != null) {
final Project project = qualifier.getProject();
PsiType type = null;
final PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR);
if (evaluator != null) {
type = evaluator.fun(qualifier, parameters);
}
if (type == null) {
type = GuessManager.getInstance(project).getControlFlowExpressionType(qualifier);
}
return type;
}
}
return null;
}
@NotNull
private static LookupElement castQualifier(@NotNull LookupElement item,
@Nullable final PsiTypeLookupItem castTypeItem,
@Nullable PsiType plainQualifier, JavaCompletionProcessor processor) {
if (castTypeItem == null) {
return item;
}
if (plainQualifier != null) {
Object o = item.getObject();
if (o instanceof PsiMethod) {
PsiType castType = castTypeItem.getPsiType();
if (plainQualifier instanceof PsiClassType && castType instanceof PsiClassType) {
PsiMethod method = (PsiMethod)o;
PsiClassType.ClassResolveResult plainResult = ((PsiClassType)plainQualifier).resolveGenerics();
PsiClass plainClass = plainResult.getElement();
if (plainClass != null && plainClass.findMethodBySignature(method, true) != null) {
PsiClass castClass = ((PsiClassType)castType).resolveGenerics().getElement();
if (castClass == null || !castClass.isInheritor(plainClass, true)) {
return item;
}
PsiSubstitutor plainSub = plainResult.getSubstitutor();
PsiSubstitutor castSub = TypeConversionUtil.getSuperClassSubstitutor(plainClass, (PsiClassType)castType);
PsiType returnType = method.getReturnType();
if (method.getSignature(plainSub).equals(method.getSignature(castSub)) &&
returnType != null &&
castSub.substitute(returnType).isAssignableFrom(plainSub.substitute(returnType)) &&
processor.isAccessible(plainClass.findMethodBySignature(method, true))
) {
return item;
}
}
}
} else if (containsMember(plainQualifier, o)) {
return item;
}
}
return LookupElementDecorator.withInsertHandler(item, new InsertHandlerDecorator<LookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElementDecorator<LookupElement> item) {
final Document document = context.getEditor().getDocument();
context.commitDocument();
final PsiFile file = context.getFile();
final PsiJavaCodeReferenceElement ref =
PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false);
if (ref != null) {
final PsiElement qualifier = ref.getQualifier();
if (qualifier != null) {
final CommonCodeStyleSettings settings = context.getCodeStyleSettings();
final String parenSpace = settings.SPACE_WITHIN_PARENTHESES ? " " : "";
document.insertString(qualifier.getTextRange().getEndOffset(), parenSpace + ")");
final String spaceWithin = settings.SPACE_WITHIN_CAST_PARENTHESES ? " " : "";
final String prefix = "(" + parenSpace + "(" + spaceWithin;
final String spaceAfter = settings.SPACE_AFTER_TYPE_CAST ? " " : "";
final int exprStart = qualifier.getTextRange().getStartOffset();
document.insertString(exprStart, prefix + spaceWithin + ")" + spaceAfter);
CompletionUtil.emulateInsertion(context, exprStart + prefix.length(), castTypeItem);
PsiDocumentManager.getInstance(file.getProject()).doPostponedOperationsAndUnblockDocument(document);
context.getEditor().getCaretModel().moveToOffset(context.getTailOffset());
}
}
item.getDelegate().handleInsert(context);
}
});
}
public static LookupElement highlightIfNeeded(PsiType qualifierType, LookupElement item, Object object) {
return containsMember(qualifierType, object) ? highlight(item) : item;
}
public static boolean containsMember(PsiType qualifierType, Object object) {
if (qualifierType instanceof PsiArrayType && object instanceof PsiMember) { //length and clone()
PsiFile file = ((PsiMember)object).getContainingFile();
if (file == null || file.getVirtualFile() == null) { //yes, they're a bit dummy
return true;
}
}
else if (qualifierType instanceof PsiClassType) {
PsiClass qualifierClass = ((PsiClassType)qualifierType).resolve();
if (qualifierClass == null) return false;
if (object instanceof PsiMethod && qualifierClass.findMethodBySignature((PsiMethod)object, false) != null) {
return true;
}
if (object instanceof PsiMember) {
return qualifierClass.equals(((PsiMember)object).getContainingClass());
}
}
return false;
}
private static LookupElement highlight(LookupElement decorator) {
return PrioritizedLookupElement.withExplicitProximity(
LookupElementDecorator.withRenderer(decorator, new LookupElementRenderer<LookupElementDecorator<LookupElement>>() {
@Override
public void renderElement(LookupElementDecorator<LookupElement> element, LookupElementPresentation presentation) {
element.getDelegate().renderElement(presentation);
presentation.setItemTextBold(true);
}
}), 1);
}
private static List<? extends LookupElement> createLookupElements(CompletionElement completionElement, PsiJavaReference reference) {
Object completion = completionElement.getElement();
assert !(completion instanceof LookupElement);
if (reference instanceof PsiJavaCodeReferenceElement) {
if (completion instanceof PsiMethod &&
((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiImportStaticStatement) {
return Arrays.asList(JavaLookupElementBuilder.forMethod((PsiMethod)completion, PsiSubstitutor.EMPTY));
}
if (completion instanceof PsiClass) {
return JavaClassNameCompletionContributor.createClassLookupItems((PsiClass)completion,
JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference),
JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER,
Conditions.<PsiClass>alwaysTrue());
}
}
LookupElement _ret = LookupItemUtil.objectToLookupItem(completion);
if (_ret == null || !(_ret instanceof LookupItem)) return Collections.emptyList();
final PsiSubstitutor substitutor = completionElement.getSubstitutor();
if (substitutor != null) {
((LookupItem<?>)_ret).setAttribute(LookupItem.SUBSTITUTOR, substitutor);
}
return Arrays.asList(_ret);
}
public static boolean hasAccessibleConstructor(PsiType type) {
if (type instanceof PsiArrayType) return true;
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
if (psiClass == null || psiClass.isEnum() || psiClass.isAnnotationType()) return false;
if (!(psiClass instanceof PsiCompiledElement)) return true;
final PsiMethod[] methods = psiClass.getConstructors();
if (methods.length == 0) return true;
for (final PsiMethod method : methods) {
if (!method.hasModifierProperty(PsiModifier.PRIVATE)) return true;
}
return false;
}
public static LookupItem qualify(final LookupItem ret) {
return ret.forceQualify();
}
public static Set<String> getAllLookupStrings(@NotNull PsiMember member) {
Set<String> allLookupStrings = ContainerUtil.newLinkedHashSet();
String name = member.getName();
allLookupStrings.add(name);
PsiClass containingClass = member.getContainingClass();
while (containingClass != null) {
final String className = containingClass.getName();
if (className == null) {
break;
}
name = className + "." + name;
allLookupStrings.add(name);
final PsiElement parent = containingClass.getParent();
if (!(parent instanceof PsiClass)) {
break;
}
containingClass = (PsiClass)parent;
}
return allLookupStrings;
}
public static LookupItem setShowFQN(final LookupItem ret) {
ret.setAttribute(JavaPsiClassReferenceElement.PACKAGE_NAME, PsiFormatUtil.getPackageDisplayName((PsiClass)ret.getObject()));
return ret;
}
public static boolean mayHaveSideEffects(@Nullable final PsiElement element) {
return element instanceof PsiExpression && SideEffectChecker.mayHaveSideEffects((PsiExpression)element);
}
public static void insertClassReference(@NotNull PsiClass psiClass, @NotNull PsiFile file, int offset) {
insertClassReference(psiClass, file, offset, offset);
}
public static int insertClassReference(PsiClass psiClass, PsiFile file, int startOffset, int endOffset) {
final Project project = file.getProject();
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitAllDocuments();
final PsiManager manager = file.getManager();
final Document document = FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile());
final PsiReference reference = file.findReferenceAt(startOffset);
if (reference != null) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof PsiClass) {
if (((PsiClass)resolved).getQualifiedName() == null || manager.areElementsEquivalent(psiClass, resolved)) {
return endOffset;
}
}
}
String name = psiClass.getName();
if (name == null) {
return endOffset;
}
assert document != null;
document.replaceString(startOffset, endOffset, name);
int newEndOffset = startOffset + name.length();
final RangeMarker toDelete = insertTemporary(newEndOffset, document, " ");
documentManager.commitAllDocuments();
PsiElement element = file.findElementAt(startOffset);
if (element instanceof PsiIdentifier) {
PsiElement parent = element.getParent();
if (parent instanceof PsiJavaCodeReferenceElement &&
!((PsiJavaCodeReferenceElement)parent).isQualified() &&
!(parent.getParent() instanceof PsiPackageStatement)) {
PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)parent;
if (psiClass.isValid() && !psiClass.getManager().areElementsEquivalent(psiClass, resolveReference(ref))) {
final boolean staticImport = ref instanceof PsiImportStaticReferenceElement;
PsiElement newElement = staticImport
? ((PsiImportStaticReferenceElement)ref).bindToTargetClass(psiClass)
: ref.bindToElement(psiClass);
final RangeMarker rangeMarker = document.createRangeMarker(newElement.getTextRange());
documentManager.doPostponedOperationsAndUnblockDocument(document);
documentManager.commitDocument(document);
newElement = CodeInsightUtilCore.findElementInRange(file, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(),
PsiJavaCodeReferenceElement.class,
JavaLanguage.INSTANCE);
rangeMarker.dispose();
if (newElement != null) {
newEndOffset = newElement.getTextRange().getEndOffset();
if (!(newElement instanceof PsiReferenceExpression)) {
PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)newElement).getParameterList();
if (parameterList != null) {
newEndOffset = parameterList.getTextRange().getStartOffset();
}
}
if (!staticImport &&
!psiClass.getManager().areElementsEquivalent(psiClass, resolveReference((PsiReference)newElement)) &&
!PsiUtil.isInnerClass(psiClass)) {
final String qName = psiClass.getQualifiedName();
if (qName != null) {
document.replaceString(newElement.getTextRange().getStartOffset(), newEndOffset, qName);
newEndOffset = newElement.getTextRange().getStartOffset() + qName.length();
}
}
}
}
}
}
if (toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
}
return newEndOffset;
}
@Nullable
static PsiElement resolveReference(final PsiReference psiReference) {
if (psiReference instanceof PsiPolyVariantReference) {
final ResolveResult[] results = ((PsiPolyVariantReference)psiReference).multiResolve(true);
if (results.length == 1) return results[0].getElement();
}
return psiReference.resolve();
}
public static RangeMarker insertTemporary(final int endOffset, final Document document, final String temporary) {
final CharSequence chars = document.getCharsSequence();
final int length = chars.length();
final RangeMarker toDelete;
if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))){
document.insertString(endOffset, temporary);
toDelete = document.createRangeMarker(endOffset, endOffset + 1);
} else if (endOffset >= length) {
toDelete = document.createRangeMarker(length, length);
}
else {
toDelete = document.createRangeMarker(endOffset, endOffset);
}
toDelete.setGreedyToLeft(true);
toDelete.setGreedyToRight(true);
return toDelete;
}
public static void insertParentheses(final InsertionContext context,
final LookupElement item,
boolean overloadsMatter,
boolean hasParams) {
insertParentheses(context, item, overloadsMatter, hasParams, false);
}
public static void insertParentheses(final InsertionContext context,
final LookupElement item,
boolean overloadsMatter,
boolean hasParams,
final boolean forceClosingParenthesis) {
final Editor editor = context.getEditor();
final char completionChar = context.getCompletionChar();
final PsiFile file = context.getFile();
final TailType tailType = completionChar == '(' ? TailType.NONE :
completionChar == ':' ? TailType.COND_EXPR_COLON :
LookupItem.handleCompletionChar(context.getEditor(), item, completionChar);
final boolean hasTail = tailType != TailType.NONE && tailType != TailType.UNKNOWN;
final boolean smart = completionChar == Lookup.COMPLETE_STATEMENT_SELECT_CHAR;
if (completionChar == '(' || completionChar == '.' || completionChar == ',' || completionChar == ';' || completionChar == ':' || completionChar == ' ') {
context.setAddCompletionChar(false);
}
if (hasTail) {
hasParams = false;
}
final boolean needRightParenth = forceClosingParenthesis ||
!smart && (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET ||
!hasParams && completionChar != '(');
context.commitDocument();
final CommonCodeStyleSettings styleSettings = context.getCodeStyleSettings();
final PsiElement elementAt = file.findElementAt(context.getStartOffset());
if (elementAt == null || !(elementAt.getParent() instanceof PsiMethodReferenceExpression)) {
ParenthesesInsertHandler.getInstance(hasParams,
styleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES,
styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES && hasParams,
needRightParenth,
styleSettings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
).handleInsert(context, item);
}
if (hasParams) {
// Invoke parameters popup
AutoPopupController.getInstance(file.getProject()).autoPopupParameterInfo(editor, overloadsMatter ? null : (PsiElement)item.getObject());
}
if (smart || !needRightParenth || !insertTail(context, item, tailType, hasTail)) {
return;
}
if (completionChar == '.') {
AutoPopupController.getInstance(file.getProject()).autoPopupMemberLookup(context.getEditor(), null);
} else if (completionChar == ',') {
AutoPopupController.getInstance(file.getProject()).autoPopupParameterInfo(context.getEditor(), null);
}
}
public static boolean insertTail(InsertionContext context, LookupElement item, TailType tailType, boolean hasTail) {
TailType toInsert = tailType;
LookupItem<?> lookupItem = item.as(LookupItem.CLASS_CONDITION_KEY);
if (lookupItem == null || lookupItem.getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) {
if (!hasTail && item.getObject() instanceof PsiMethod && ((PsiMethod)item.getObject()).getReturnType() == PsiType.VOID) {
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
if (psiElement().beforeLeaf(psiElement().withText(".")).accepts(context.getFile().findElementAt(context.getTailOffset() - 1))) {
return false;
}
boolean insertAdditionalSemicolon = true;
final PsiReferenceExpression referenceExpression = PsiTreeUtil.getTopmostParentOfType(context.getFile().findElementAt(context.getStartOffset()), PsiReferenceExpression.class);
if (referenceExpression instanceof PsiMethodReferenceExpression && LambdaHighlightingUtil
.insertSemicolon(referenceExpression.getParent())) {
insertAdditionalSemicolon = false;
} else if (referenceExpression != null) {
PsiElement parent = referenceExpression.getParent();
if (parent instanceof PsiMethodCallExpression) {
parent = parent.getParent();
}
if (parent instanceof PsiLambdaExpression && !LambdaHighlightingUtil.insertSemicolonAfter((PsiLambdaExpression)parent)) {
insertAdditionalSemicolon = false;
}
}
if (insertAdditionalSemicolon) {
toInsert = TailType.SEMICOLON;
}
}
}
toInsert.processTail(context.getEditor(), context.getTailOffset());
return true;
}
//need to shorten references in type argument list
public static void shortenReference(final PsiFile file, final int offset) throws IncorrectOperationException {
Project project = file.getProject();
final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
Document document = manager.getDocument(file);
manager.commitDocument(document);
final PsiReference ref = file.findReferenceAt(offset);
if (ref != null) {
PsiElement element = ref.getElement();
if (element != null) {
JavaCodeStyleManager.getInstance(project).shortenClassReferences(element);
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
}
}
}
public static boolean inSomePackage(PsiElement context) {
PsiFile contextFile = context.getContainingFile();
return contextFile instanceof PsiClassOwner && StringUtil.isNotEmpty(((PsiClassOwner)contextFile).getPackageName());
}
public static boolean isSourceLevelAccessible(PsiElement context, PsiClass psiClass, final boolean pkgContext) {
if (!JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper().isAccessible(psiClass, context, null)) {
return false;
}
if (pkgContext) {
PsiClass topLevel = PsiUtil.getTopLevelClass(psiClass);
if (topLevel != null) {
String fqName = topLevel.getQualifiedName();
if (fqName != null && StringUtil.isEmpty(StringUtil.getPackageName(fqName))) {
return false;
}
}
}
return true;
}
public static boolean promptTypeArgs(InsertionContext context, int offset) {
if (offset < 0) {
return false;
}
OffsetKey key = context.trackOffset(offset, false);
PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting();
offset = context.getOffset(key);
if (offset < 0) {
return false;
}
String open = escapeXmlIfNeeded(context, "<");
context.getDocument().insertString(offset, open);
context.getEditor().getCaretModel().moveToOffset(offset + open.length());
context.getDocument().insertString(offset + open.length(), escapeXmlIfNeeded(context, ">"));
context.setAddCompletionChar(false);
return true;
}
public static FakePsiElement createContextWithXxxVariable(final PsiElement place, final PsiType varType) {
return new FakePsiElement() {
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place) {
return processor.execute(new LightVariableBuilder("xxx", varType, place), ResolveState.initial());
}
@Override
public PsiElement getParent() {
return place;
}
};
}
public static String escapeXmlIfNeeded(InsertionContext context, String generics) {
if (context.getFile().getViewProvider().getBaseLanguage() == StdLanguages.JSPX) {
return StringUtil.escapeXml(generics);
}
return generics;
}
}