From 40cb6b363cf09eb01dd3b11792a00533c2392649 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 26 Apr 2011 15:54:04 +0200 Subject: [PATCH] IDEA-68643 When completing a constant, the containing class is not imported --- .../completion/DefaultInsertHandler.java | 29 +------- .../completion/JavaCompletionUtil.java | 5 -- ...GlobalMemberNameCompletionContributor.java | 8 +- .../completion/SmartCompletionDecorator.java | 73 ------------------- .../lookup/VariableLookupItem.java | 48 +++++++++++- ...SuggestExpectedTypeMembersNonImported.java | 7 ++ ...tExpectedTypeMembersNonImported_after.java | 9 +++ .../completion/NormalCompletionTest.groovy | 6 ++ 8 files changed, 75 insertions(+), 110 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/DefaultInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/DefaultInsertHandler.java index ecd9917022b1..26b9bedfb608 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/DefaultInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/DefaultInsertHandler.java @@ -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; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 85426642491a..e5aee87bbcb8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -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(); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java index 9368017189ee..bdfe1f25cd25 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java @@ -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); + } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java index 271fd394403d..85aac72e83ff 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SmartCompletionDecorator.java @@ -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 { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.SmartCompletionDecorator"); @NotNull private final Collection myExpectedTypeInfos; private PsiElement myPosition; @@ -115,76 +109,9 @@ public class SmartCompletionDecorator extends TailTypeDecorator { @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 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() { - 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; diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java index 45db8746f149..7c7ec0b24bfc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java @@ -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 implements Typed setAttribute(SUBSTITUTOR, substitutor); } + @Override + public LookupItem 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 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()); + } } } diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported.java b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported.java new file mode 100644 index 000000000000..729682cb739d --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported.java @@ -0,0 +1,7 @@ +class Intermediate { + { + foo.Usage.foo(FO) + } +} + + diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported_after.java b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported_after.java new file mode 100644 index 000000000000..af9763699259 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SuggestExpectedTypeMembersNonImported_after.java @@ -0,0 +1,9 @@ +import foo.Super; + +class Intermediate { + { + foo.Usage.foo(Super.FOO) + } +} + + diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index a27af5041cc3..fe0ec896d194 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -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') }