blob: 21e32a3e3aeb7540bf230537d931f3c8e782f546 [file] [log] [blame]
package com.intellij.codeInsight.lookup;
import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* @author peter
*/
public class VariableLookupItem extends LookupItem<PsiVariable> implements TypedLookupItem, StaticallyImportable {
@Nullable private final MemberLookupHelper myHelper;
public VariableLookupItem(PsiVariable object) {
super(object, object.getName());
myHelper = null;
}
public VariableLookupItem(PsiField field, boolean shouldImport) {
super(field, field.getName());
myHelper = new MemberLookupHelper(field, field.getContainingClass(), shouldImport, false);
if (!shouldImport) {
forceQualify();
}
}
@Override
public PsiType getType() {
return getSubstitutor().substitute(getObject().getType());
}
@NotNull
public PsiSubstitutor getSubstitutor() {
final PsiSubstitutor substitutor = (PsiSubstitutor)getAttribute(LookupItem.SUBSTITUTOR);
return substitutor == null ? PsiSubstitutor.EMPTY : substitutor;
}
public void setSubstitutor(@NotNull PsiSubstitutor substitutor) {
setAttribute(SUBSTITUTOR, substitutor);
}
@Override
public void setShouldBeImported(boolean shouldImportStatic) {
assert myHelper != null;
myHelper.setShouldBeImported(shouldImportStatic);
}
@Override
public boolean canBeImported() {
return myHelper != null;
}
@Override
public boolean willBeImported() {
return myHelper != null && myHelper.willBeImported();
}
@Override
public void renderElement(LookupElementPresentation presentation) {
super.renderElement(presentation);
if (myHelper != null) {
myHelper.renderElement(presentation, getAttribute(FORCE_QUALIFY) != null ? Boolean.TRUE : null, getSubstitutor());
}
}
@Override
public LookupItem<PsiVariable> forceQualify() {
PsiVariable var = getObject();
if (var instanceof PsiField) {
for (String s : JavaCompletionUtil.getAllLookupStrings((PsiField)var)) {
setLookupString(s); //todo set the string that will be inserted
}
}
return super.forceQualify();
}
@Override
public void handleInsert(InsertionContext context) {
PsiVariable variable = getObject();
Document document = context.getDocument();
document.replaceString(context.getStartOffset(), context.getTailOffset(), variable.getName());
context.commitDocument();
if (variable instanceof PsiField) {
if (willBeImported()) {
RangeMarker toDelete = JavaCompletionUtil.insertTemporary(context.getTailOffset(), document, " ");
context.commitDocument();
final PsiReferenceExpression
ref = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiReferenceExpression.class, false);
if (ref != null) {
ref.bindToElementViaStaticImport(((PsiField)variable).getContainingClass());
PostprocessReformattingAspect.getInstance(ref.getProject()).doPostponedFormatting();
}
if (toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
}
context.commitDocument();
}
else if (shouldQualify((PsiField)variable, context)) {
qualifyFieldReference(context, (PsiField)variable);
}
}
PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false);
if (ref != null) {
JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(ref);
}
ref = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false);
PsiElement target = ref == null ? null : ref.resolve();
if (target instanceof PsiLocalVariable || target instanceof PsiParameter) {
makeFinalIfNeeded(context, (PsiVariable)target);
}
final char completionChar = context.getCompletionChar();
if (completionChar == '=') {
context.setAddCompletionChar(false);
TailType.EQ.processTail(context.getEditor(), context.getTailOffset());
}
else if (completionChar == ',' && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) {
context.setAddCompletionChar(false);
TailType.COMMA.processTail(context.getEditor(), context.getTailOffset());
AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(context.getEditor(), null);
}
else if (completionChar == ':') {
context.setAddCompletionChar(false);
TailType.COND_EXPR_COLON.processTail(context.getEditor(), context.getTailOffset());
}
else if (completionChar == '.') {
AutoPopupController.getInstance(context.getProject()).autoPopupMemberLookup(context.getEditor(), null);
}
else if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(variable.getType())) {
context.setAddCompletionChar(false);
if (ref != null) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
document.insertString(ref.getTextRange().getStartOffset(), "!");
}
}
}
public static void makeFinalIfNeeded(@NotNull InsertionContext context, @NotNull PsiVariable variable) {
PsiElement place = context.getFile().findElementAt(context.getTailOffset() - 1);
if (!Registry.is("java.completion.make.outer.variables.final") ||
place == null || PsiUtil.isLanguageLevel8OrHigher(place) || JspPsiUtil.isInJspFile(place)) {
return;
}
if (HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, place) != null &&
!HighlightControlFlowUtil.isReassigned(variable, new HashMap<PsiElement, Collection<ControlFlowUtil.VariableInfo>>())) {
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true);
}
}
private boolean shouldQualify(PsiField field, InsertionContext context) {
if (myHelper != null && !myHelper.willBeImported()) {
return true;
}
if (getAttribute(FORCE_QUALIFY) != null) {
return true;
}
PsiReference reference = context.getFile().findReferenceAt(context.getStartOffset());
if (reference instanceof PsiReferenceExpression && !((PsiReferenceExpression)reference).isQualified()) {
final PsiVariable target = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper()
.resolveReferencedVariable(field.getName(), (PsiElement)reference);
return !field.getManager().areElementsEquivalent(target, CompletionUtil.getOriginalOrSelf(field));
}
return false;
}
private static void qualifyFieldReference(InsertionContext context, PsiField field) {
context.commitDocument();
PsiFile file = context.getFile();
final PsiReference reference = file.findReferenceAt(context.getStartOffset());
if (reference instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)reference).isQualified()) {
return;
}
PsiClass containingClass = field.getContainingClass();
if (containingClass != null && containingClass.getName() != null) {
OffsetKey oldStart = context.trackOffset(context.getStartOffset(), true);
JavaCompletionUtil.insertClassReference(containingClass, file, context.getStartOffset());
context.getDocument().insertString(context.getOffsetMap().getOffset(oldStart), ".");
PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument());
}
}
}