IDEA-68643 When completing a constant, the containing class is not imported

This commit is contained in:
peter
2011-04-26 16:39:45 +02:00
parent 9ec367f032
commit 40cb6b363c
8 changed files with 75 additions and 110 deletions
@@ -25,7 +25,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
@@ -73,8 +72,7 @@ public class DefaultInsertHandler extends TemplateInsertHandler implements Clone
state.caretOffset = processTail(tailType, state.caretOffset, state.tailOffset, editor);
editor.getSelectionModel().removeSelection();
qualifyIfNeeded(context, item);
addImportForItem(context, item);
if (tailType == TailType.DOT || context.getCompletionChar() == '.') {
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null);
@@ -82,31 +80,6 @@ public class DefaultInsertHandler extends TemplateInsertHandler implements Clone
}
private static void qualifyIfNeeded(InsertionContext context, LookupElement item) {
try{
final PsiFile file = context.getFile();
if (item.getObject() instanceof PsiField) {
PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
PsiReference reference = file.findReferenceAt(context.getStartOffset());
if (reference instanceof PsiReferenceExpression && !((PsiReferenceExpression) reference).isQualified()) {
final PsiField member = (PsiField)item.getObject();
final PsiVariable target =
JavaPsiFacade.getInstance(context.getProject()).getResolveHelper().resolveReferencedVariable(member.getName(), (PsiElement)reference);
if (member.getManager().areElementsEquivalent(target, JavaCompletionUtil.getOriginalElement(member))) return;
final PsiClass psiClass = member.getContainingClass();
if (psiClass != null && StringUtil.isNotEmpty(psiClass.getName())) {
context.getEditor().getDocument().insertString(context.getStartOffset(), psiClass.getName() + ".");
}
}
}
addImportForItem(context, item);
}
catch(IncorrectOperationException e){
LOG.error(e);
}
}
private static void handleParentheses(final boolean hasParams, final boolean needParenth, TailType tailType, InsertionContext context, InsertHandlerState myState){
final Document document = context.getEditor().getDocument();
boolean insertRightParenth = tailType != TailType.SMART_COMPLETION;
@@ -669,11 +669,6 @@ public class JavaCompletionUtil {
}
public static LookupItem qualify(final LookupItem ret) {
if (!(ret instanceof JavaMethodCallElement)) {
for (String s : getAllLookupStrings((PsiMember)ret.getObject())) {
ret.setLookupString(s);
}
}
return ret.forceQualify();
}
@@ -114,11 +114,13 @@ public class JavaGlobalMemberNameCompletionContributor extends CompletionContrib
ref.bindToElementViaStaticImport(myContainingClass);
PostprocessReformattingAspect.getInstance(ref.getProject()).doPostponedFormatting();
}
} else {
context.getDocument().insertString(context.getStartOffset(), ".");
JavaCompletionUtil.insertClassReference(myContainingClass, context.getFile(), context.getStartOffset());
}
super.handleInsert(context);
}
@Override
protected boolean shouldQualify(PsiField field, InsertionContext context) {
return !willBeImported() || super.shouldQualify(field, context);
}
}
}
@@ -16,21 +16,16 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.TailTypeDecorator;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,7 +38,6 @@ import java.util.Set;
* @author peter
*/
public class SmartCompletionDecorator extends TailTypeDecorator<LookupElement> {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.SmartCompletionDecorator");
@NotNull private final Collection<ExpectedTypeInfo> myExpectedTypeInfos;
private PsiElement myPosition;
@@ -115,76 +109,9 @@ public class SmartCompletionDecorator extends TailTypeDecorator<LookupElement> {
@Override
public void handleInsert(InsertionContext context) {
myPosition = getPosition(context, this);
LookupItem item = getDelegate().as(LookupItem.CLASS_CONDITION_KEY);
if (item != null) {
analyzeItem(context, item, getObject(), myPosition, myExpectedTypeInfos);
}
super.handleInsert(context);
}
private static void analyzeItem(final InsertionContext context, final LookupItem item, final Object completion, @Nullable PsiElement position, @NotNull Collection<ExpectedTypeInfo> expectedTypes) {
if (position == null) return;
final PsiFile file = position.getContainingFile();
final int startOffset = position.getTextRange().getStartOffset();
PsiReference ref = position.getContainingFile().findReferenceAt(startOffset);
if (ref!=null && completion instanceof PsiNamedElement) {
if (completion instanceof PsiField) {
final PsiMember member = (PsiMember)completion;
if (item.getAttribute(LookupItem.FORCE_QUALIFY) != null
&& member.hasModifierProperty(PsiModifier.STATIC)
&& PsiUtil.isAccessible(member, position, null)) {
final PsiClass containingClass = member.getContainingClass();
if (containingClass != null) {
final String refText = ref.getElement().getText();
final Document document = context.getEditor().getDocument();
document.insertString(context.getEditor().getCaretModel().getOffset(), " ");
final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(context.getProject());
psiDocumentManager.commitDocument(document);
LOG.assertTrue(!psiDocumentManager.isUncommited(psiDocumentManager.getDocument(file)));
final PsiReference finalRef = file.findReferenceAt(startOffset);
if (finalRef == null) {
final String text = document.getText();
LOG.error("startOffset=" + startOffset + "\n" +
"caretOffset=" + context.getEditor().getCaretModel().getOffset() + "\n" +
"ref.getText()=" + refText + "\n" +
"file=" + file + "\n" +
"documentPart=" + text.substring(Math.max(startOffset - 100, 0), Math.min(startOffset + 100, text.length())));
}
final String name = member.getName();
assert name != null;
final PsiElement psiElement = file.getManager().performActionWithFormatterDisabled(new Computable<PsiElement>() {
public PsiElement compute() {
try {
return finalRef.bindToElement(containingClass);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
return null;
}
});
final PsiElement element = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(psiElement);
int whereToInsert = element.getTextRange().getEndOffset();
final String insertString = "." + name;
document.insertString(whereToInsert, insertString);
final int endOffset = whereToInsert + insertString.length();
context.getEditor().getCaretModel().moveToOffset(endOffset);
context.getOffsetMap().addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, endOffset);
context.getOffsetMap().addOffset(CompletionInitializationContext.START_OFFSET, whereToInsert);
context.getOffsetMap().addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, endOffset);
item.setLookupString(name);
document.deleteString(endOffset, endOffset + 1);
}
}
}
}
}
public static boolean hasUnboundTypeParams(final PsiMethod method) {
final PsiTypeParameter[] typeParameters = method.getTypeParameters();
if (typeParameters.length == 0) return false;
@@ -4,6 +4,7 @@ import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.CodeCompletionFeatures;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
@@ -32,11 +33,29 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
setAttribute(SUBSTITUTOR, substitutor);
}
@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) {
super.handleInsert(context);
PsiVariable variable = getObject();
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), variable.getName());
PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument());
if (variable instanceof PsiField && 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);
@@ -58,14 +77,41 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
else if (completionChar == '.') {
AutoPopupController.getInstance(context.getProject()).autoPopupMemberLookup(context.getEditor(), null);
}
else if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(getObject().getType())) {
else if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(variable.getType())) {
context.setAddCompletionChar(false);
if (ref != null) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
context.getDocument().insertString(ref.getTextRange().getStartOffset(), "!");
}
}
}
protected boolean shouldQualify(PsiField field, InsertionContext context) {
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, JavaCompletionUtil.getOriginalElement(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) {
context.getDocument().insertString(context.getStartOffset(), ".");
JavaCompletionUtil.insertClassReference(containingClass, file, context.getStartOffset());
PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument());
}
}
}
@@ -0,0 +1,7 @@
class Intermediate {
{
foo.Usage.foo(FO<caret>)
}
}
@@ -0,0 +1,9 @@
import foo.Super;
class Intermediate {
{
foo.Usage.foo(Super.FOO<caret>)
}
}
@@ -847,6 +847,12 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase {
public void testSuggestExpectedTypeMembersInCall() throws Throwable { doTest('\n') }
public void testSuggestExpectedTypeMembersNonImported() throws Throwable {
myFixture.addClass("package foo; public class Super { public static final Super FOO = null; }")
myFixture.addClass("package foo; public class Usage { public static void foo(Super s) {} }")
doTest('\n')
}
public void testClassNameWithInnersTab() throws Throwable { doTest('\t') }
public void testClassNameWithGenericsTab() throws Throwable {doTest('\t') }