mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-68643 When completing a constant, the containing class is not imported
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class Intermediate {
|
||||
{
|
||||
foo.Usage.foo(FO<caret>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import foo.Super;
|
||||
|
||||
class Intermediate {
|
||||
{
|
||||
foo.Usage.foo(Super.FOO<caret>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -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') }
|
||||
|
||||
Reference in New Issue
Block a user