remove ending dots in package completion
[fedora-idea.git] / codeInsight / impl / com / intellij / codeInsight / completion / JavaCompletionContributor.java
blob2af44c172c8b158425d65628d2a10776361acf72
1 /*
2 * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
3 * Use is subject to license terms.
4 */
5 package com.intellij.codeInsight.completion;
7 import com.intellij.codeInsight.TailType;
8 import com.intellij.codeInsight.TailTypes;
9 import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix;
10 import com.intellij.codeInsight.hint.ShowParameterInfoHandler;
11 import com.intellij.codeInsight.lookup.LookupElement;
12 import com.intellij.codeInsight.lookup.LookupItem;
13 import com.intellij.codeInsight.lookup.LookupItemUtil;
14 import com.intellij.codeInsight.lookup.TailTypeDecorator;
15 import com.intellij.lang.LangBundle;
16 import com.intellij.lang.StdLanguages;
17 import com.intellij.openapi.actionSystem.IdeActions;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.editor.ex.EditorEx;
22 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Comparing;
25 import com.intellij.openapi.util.Computable;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.patterns.ElementPattern;
28 import com.intellij.patterns.PatternCondition;
29 import static com.intellij.patterns.PsiJavaPatterns.*;
30 import com.intellij.patterns.PsiNameValuePairPattern;
31 import com.intellij.psi.*;
32 import com.intellij.psi.filters.*;
33 import com.intellij.psi.filters.classes.AssignableFromContextFilter;
34 import com.intellij.psi.filters.classes.ThisOrAnyInnerFilter;
35 import com.intellij.psi.filters.element.ExcludeDeclaredFilter;
36 import com.intellij.psi.filters.element.ModifierFilter;
37 import com.intellij.psi.filters.getters.ExpectedTypesGetter;
38 import com.intellij.psi.filters.types.AssignableFromFilter;
39 import com.intellij.psi.scope.ElementClassFilter;
40 import com.intellij.psi.util.PsiTreeUtil;
41 import com.intellij.psi.util.PsiUtil;
42 import com.intellij.util.PairConsumer;
43 import com.intellij.util.ProcessingContext;
44 import gnu.trove.THashSet;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import java.util.Arrays;
49 import java.util.HashSet;
50 import java.util.LinkedHashSet;
51 import java.util.Set;
53 /**
54 * @author peter
56 public class JavaCompletionContributor extends CompletionContributor {
57 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionContributor");
58 private static final Java15CompletionData ourJava15CompletionData = new Java15CompletionData();
59 private static final JavaCompletionData ourJavaCompletionData = new JavaCompletionData();
60 private static final PsiNameValuePairPattern NAME_VALUE_PAIR = psiNameValuePair().withSuperParent(
62 psiElement(PsiAnnotation.class));
63 private static final ElementPattern<PsiElement> ANNOTATION_ATTRIBUTE_NAME =
64 or(psiElement(PsiIdentifier.class).withParent(NAME_VALUE_PAIR),
65 psiElement().afterLeaf("(").withParent(psiReferenceExpression().withParent(NAME_VALUE_PAIR)));
66 public static final ElementPattern SWITCH_LABEL =
67 psiElement().withSuperParent(2, psiElement(PsiSwitchLabelStatement.class).withSuperParent(2,
68 psiElement(PsiSwitchStatement.class).with(new PatternCondition<PsiSwitchStatement>("enumExpressionType") {
69 @Override
70 public boolean accepts(@NotNull PsiSwitchStatement psiSwitchStatement, ProcessingContext context) {
71 final PsiExpression expression = psiSwitchStatement.getExpression();
72 if(expression == null) return false;
73 final PsiType type = expression.getType();
74 return type instanceof PsiClassType;
76 })));
78 @Nullable
79 private static ElementFilter getReferenceFilter(PsiElement position) {
80 // Completion after extends in interface, type parameter and implements in class
81 final PsiClass containingClass = PsiTreeUtil.getParentOfType(position, PsiClass.class, false, PsiCodeBlock.class, PsiMethod.class, PsiExpressionList.class);
82 if (containingClass != null && psiElement().afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS, ",", "&").accepts(position)) {
83 return new AndFilter(ElementClassFilter.CLASS, new NotFilter(new AssignableFromContextFilter()));
86 if (JavaCompletionData.DECLARATION_START.isAcceptable(position, position)) {
87 return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER);
90 if (JavaCompletionData.INSIDE_PARAMETER_LIST.accepts(position)) {
91 return ElementClassFilter.CLASS;
94 // Completion for classes in method throws section
95 if (psiElement().afterLeaf(PsiKeyword.THROWS, ",").inside(psiElement(PsiReferenceList.class).withParent(PsiMethod.class)).accepts(position)) {
96 return new OrFilter(new ThisOrAnyInnerFilter(new AssignableFromFilter("java.lang.Throwable")), ElementClassFilter.PACKAGE_FILTER);
99 if (psiElement().afterLeaf(PsiKeyword.INSTANCEOF).accepts(position)) {
100 return new ElementExtractorFilter(ElementClassFilter.CLASS);
103 if (JavaCompletionData.AFTER_FINAL.accepts(position)) {
104 return ElementClassFilter.CLASS;
107 if (JavaCompletionData.AFTER_TRY_BLOCK.isAcceptable(position, position) ||
108 JavaCompletionData.START_SWITCH.isAcceptable(position, position) ||
109 JavaCompletionData.INSTANCEOF_PLACE.isAcceptable(position, position)) {
110 return null;
113 if (psiElement().afterLeaf(psiElement().withText("(").withParent(PsiTryStatement.class)).accepts(position)) {
114 return new OrFilter(new ThisOrAnyInnerFilter(new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE)), ElementClassFilter.PACKAGE_FILTER);
117 if (JavaSmartCompletionContributor.AFTER_THROW_NEW.accepts(position)) {
118 return new ThisOrAnyInnerFilter(new AssignableFromFilter("java.lang.Throwable"));
121 if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) {
122 return ElementClassFilter.CLASS;
125 if (psiElement().inside(PsiReferenceParameterList.class).accepts(position)) {
126 return ElementClassFilter.CLASS;
129 if (psiElement().inside(PsiAnnotationParameterList.class).accepts(position)) {
130 return new OrFilter(new ClassFilter(PsiAnnotationMethod.class), ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER, new AndFilter(new ClassFilter(PsiField.class),
131 new ModifierFilter(PsiModifier.STATIC, PsiModifier.FINAL)));
134 if (psiElement().afterLeaf("=").inside(PsiVariable.class).accepts(position)) {
135 return new OrFilter(
136 new ClassFilter(PsiVariable.class, false),
137 new ExcludeDeclaredFilter(new ClassFilter(PsiVariable.class)));
140 if (SWITCH_LABEL.accepts(position)) {
141 return new ClassFilter(PsiField.class) {
142 @Override
143 public boolean isAcceptable(Object element, PsiElement context) {
144 return element instanceof PsiEnumConstant;
149 return TrueFilter.INSTANCE;
152 public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet _result) {
153 if (parameters.getCompletionType() != CompletionType.BASIC) return;
155 if (parameters.getPosition().getContainingFile().getLanguage() == StdLanguages.JAVA) {
156 final PsiFile file = parameters.getOriginalFile();
157 final int startOffset = parameters.getOffset();
158 final PsiElement lastElement = file.findElementAt(startOffset - 1);
159 final PsiElement insertedElement = parameters.getPosition();
160 final JavaAwareCompletionData completionData = ApplicationManager.getApplication().runReadAction(new Computable<JavaAwareCompletionData>() {
161 public JavaAwareCompletionData compute() {
162 return getCompletionDataByElementInner(lastElement);
166 final boolean checkAccess = parameters.getInvocationCount() == 1;
168 if (ANNOTATION_ATTRIBUTE_NAME.accepts(insertedElement)) {
169 ApplicationManager.getApplication().runReadAction(new Runnable() {
170 public void run() {
171 completeAnnotationAttributeName(_result, file, insertedElement, completionData, checkAccess);
174 _result.stopHere();
175 return;
178 LegacyCompletionContributor.processReferences(parameters, _result, completionData, new PairConsumer<PsiReference, CompletionResultSet>() {
179 public void consume(final PsiReference reference, final CompletionResultSet result) {
180 ApplicationManager.getApplication().runReadAction(new Runnable() {
181 public void run() {
182 if (reference instanceof PsiJavaReference) {
183 final ElementFilter filter = getReferenceFilter(insertedElement);
184 if (filter != null) {
185 final boolean isSwitchLabel = SWITCH_LABEL.accepts(insertedElement);
186 for (LookupElement element : JavaCompletionUtil.processJavaReference(insertedElement,
187 (PsiJavaReference) reference,
188 new ElementExtractorFilter(filter),
189 checkAccess,
190 result.getPrefixMatcher())) {
191 JavaCompletionUtil.highlightMemberOfContainer(element.as(LookupItem.class));
192 if (isSwitchLabel) {
193 result.addElement(TailTypeDecorator.createDecorator(element, TailType.createSimpleTailType(';')));
194 } else {
195 setTailTypeByFile(element, file);
196 result.addElement(element);
200 return;
204 final Object[] variants = reference.getVariants();
205 if (variants == null) {
206 LOG.assertTrue(false, "Reference=" + reference);
208 for (Object completion : variants) {
209 if (completion == null) {
210 LOG.assertTrue(false, "Position=" + insertedElement + "\n;Reference=" + reference + "\n;variants=" + Arrays.toString(
211 variants));
213 result.addElement(LookupItemUtil.objectToLookupItem(completion));
220 final Set<LookupElement> lookupSet = new LinkedHashSet<LookupElement>();
221 final Set<CompletionVariant> keywordVariants = new HashSet<CompletionVariant>();
222 completionData.addKeywordVariants(keywordVariants, insertedElement, parameters.getOriginalFile());
223 final CompletionResultSet result = _result.withPrefixMatcher(completionData.findPrefix(insertedElement, startOffset));
224 completionData.completeKeywordsBySet(lookupSet, keywordVariants, insertedElement, result.getPrefixMatcher(), parameters.getOriginalFile());
226 ApplicationManager.getApplication().runReadAction(new Runnable() {
227 public void run() {
228 completionData.fillCompletions(parameters, result);
232 for (final LookupElement item : lookupSet) {
233 if (item.getInsertHandler() == null) {
234 ((LookupItem)item).setInsertHandler(new InsertHandler() {
235 public void handleInsert(final InsertionContext context, final LookupElement item) {
236 analyzeItem((LookupItem)item, item.getObject(), parameters.getPosition());
237 new DefaultInsertHandler().handleInsert(context, item);
242 result.addElement(item);
244 result.stopHere();
248 private static void setTailTypeByFile(LookupElement element, PsiFile file) {
249 final TailType tailType = JavaCompletionUtil.getDefaultTailTypeForFile(file);
250 if (tailType != null) {
251 final LookupItem lookupItem = element.as(LookupItem.class);
252 if (lookupItem != null) {
253 lookupItem.setTailType(tailType);
259 private static void completeAnnotationAttributeName(CompletionResultSet result, PsiFile file, PsiElement insertedElement,
260 JavaAwareCompletionData completionData, boolean checkAccess) {
261 PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class);
262 PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)pair.getParent();
263 PsiAnnotation anno = (PsiAnnotation)parameterList.getParent();
264 boolean showClasses = psiElement().afterLeaf("(").accepts(insertedElement);
265 PsiClass annoClass = null;
266 final PsiJavaCodeReferenceElement referenceElement = anno.getNameReferenceElement();
267 if (referenceElement != null) {
268 final PsiElement element = referenceElement.resolve();
269 if (element instanceof PsiClass) {
270 annoClass = (PsiClass)element;
271 if (annoClass.findMethodsByName("value", false).length == 0) {
272 showClasses = false;
277 if (showClasses && insertedElement.getParent() instanceof PsiReferenceExpression) {
278 final THashSet<LookupElement> set = new THashSet<LookupElement>();
279 completionData.completeReference((PsiReference) insertedElement.getParent(),
280 insertedElement, set, TailType.NONE, file, TrueFilter.INSTANCE,
281 completionData.myGenericVariant, checkAccess);
282 for (final LookupElement element : set) {
283 result.addElement(element);
288 if (annoClass != null) {
289 final PsiNameValuePair[] existingPairs = parameterList.getAttributes();
291 methods: for (PsiMethod method : annoClass.getMethods()) {
292 final String attrName = method.getName();
293 for (PsiNameValuePair apair : existingPairs) {
294 if (Comparing.equal(apair.getName(), attrName)) continue methods;
296 result.addElement(new LookupItem<PsiMethod>(method, attrName).setInsertHandler(new InsertHandler<LookupElement>() {
297 public void handleInsert(InsertionContext context, LookupElement item) {
298 final Editor editor = context.getEditor();
299 TailType.EQ.processTail(editor, editor.getCaretModel().getOffset());
300 context.setAddCompletionChar(false);
302 }));
307 private static JavaAwareCompletionData getCompletionDataByElementInner(PsiElement element) {
308 return element != null && PsiUtil.isLanguageLevel5OrHigher(element) ? ourJava15CompletionData : ourJavaCompletionData;
312 public String advertise(@NotNull final CompletionParameters parameters) {
313 if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null;
315 if (parameters.getCompletionType() != CompletionType.SMART && shouldSuggestSmartCompletion(parameters.getPosition())) {
316 if (CompletionUtil.shouldShowFeature(parameters, CodeCompletionFeatures.EDITING_COMPLETION_SMARTTYPE_GENERAL)) {
317 final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
318 if (shortcut != null) {
319 return CompletionBundle.message("completion.smart.hint", shortcut);
324 if (parameters.getCompletionType() != CompletionType.CLASS_NAME && shouldSuggestClassNameCompletion(parameters.getPosition())) {
325 if (CompletionUtil.shouldShowFeature(parameters, CodeCompletionFeatures.EDITING_COMPLETION_CLASSNAME)) {
326 final String shortcut = getActionShortcut(IdeActions.ACTION_CLASS_NAME_COMPLETION);
327 if (shortcut != null) {
328 return CompletionBundle.message("completion.class.name.hint", shortcut);
333 if (parameters.getCompletionType() == CompletionType.SMART && parameters.getInvocationCount() == 1) {
334 final PsiType[] psiTypes = ExpectedTypesGetter.getExpectedTypes(parameters.getPosition(), true);
335 if (psiTypes.length > 0) {
336 if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_TOAR)) {
337 final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
338 if (shortcut != null) {
339 for (final PsiType psiType : psiTypes) {
340 final PsiType type = PsiUtil.extractIterableTypeParameter(psiType, false);
341 if (type != null) {
342 return CompletionBundle.message("completion.smart.aslist.hint", shortcut, type.getPresentableText());
347 if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_ASLIST)) {
348 final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
349 if (shortcut != null) {
350 for (final PsiType psiType : psiTypes) {
351 if (psiType instanceof PsiArrayType) {
352 final PsiType componentType = ((PsiArrayType)psiType).getComponentType();
353 if (!(componentType instanceof PsiPrimitiveType)) {
354 return CompletionBundle.message("completion.smart.toar.hint", shortcut, componentType.getPresentableText());
361 if (CompletionUtil.shouldShowFeature(parameters, JavaCompletionFeatures.SECOND_SMART_COMPLETION_CHAIN)) {
362 final String shortcut = getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION);
363 if (shortcut != null) {
364 return CompletionBundle.message("completion.smart.chain.hint", shortcut);
369 return null;
372 public String handleEmptyLookup(@NotNull final CompletionParameters parameters, final Editor editor) {
373 if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null;
375 final String ad = advertise(parameters);
376 final String suffix = ad == null ? "" : "; " + StringUtil.decapitalize(ad);
377 if (parameters.getCompletionType() == CompletionType.SMART) {
378 if (!ApplicationManager.getApplication().isUnitTestMode()) {
380 final Project project = parameters.getPosition().getProject();
381 final PsiFile file = parameters.getOriginalFile();
383 PsiExpression expression = PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
384 if (expression != null && expression.getParent() instanceof PsiExpressionList) {
385 int lbraceOffset = expression.getParent().getTextRange().getStartOffset();
386 new ShowParameterInfoHandler().invoke(project, editor, file, lbraceOffset, null);
389 if (expression instanceof PsiLiteralExpression) {
390 return LangBundle.message("completion.no.suggestions") + suffix;
393 if (expression instanceof PsiInstanceOfExpression) {
394 final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression)expression;
395 if (PsiTreeUtil.isAncestor(instanceOfExpression.getCheckType(), parameters.getPosition(), false)) {
396 return LangBundle.message("completion.no.suggestions") + suffix;
401 final Set<PsiType> expectedTypes = JavaCompletionUtil.getExpectedTypes(parameters);
402 if (expectedTypes != null) {
403 PsiType type = expectedTypes.size() == 1 ? expectedTypes.iterator().next() : null;
404 if (type != null) {
405 final PsiType deepComponentType = type.getDeepComponentType();
406 if (deepComponentType instanceof PsiClassType) {
407 if (((PsiClassType)deepComponentType).resolve() != null) {
408 return CompletionBundle.message("completion.no.suggestions.of.type", type.getPresentableText()) + suffix;
410 return CompletionBundle.message("completion.unknown.type", type.getPresentableText()) + suffix;
412 if (!PsiType.NULL.equals(type)) {
413 return CompletionBundle.message("completion.no.suggestions.of.type", type.getPresentableText()) + suffix;
418 return LangBundle.message("completion.no.suggestions") + suffix;
421 private static boolean shouldSuggestSmartCompletion(final PsiElement element) {
422 if (shouldSuggestClassNameCompletion(element)) return false;
424 final PsiElement parent = element.getParent();
425 if (parent instanceof PsiReferenceExpression && ((PsiReferenceExpression)parent).getQualifier() != null) return false;
426 if (parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiReferenceExpression) return true;
428 return new ExpectedTypesGetter().get(element, null).length > 0;
431 private static boolean shouldSuggestClassNameCompletion(final PsiElement element) {
432 if (element == null) return false;
433 final PsiElement parent = element.getParent();
434 if (parent == null) return false;
435 return parent.getParent() instanceof PsiTypeElement || parent.getParent() instanceof PsiExpressionStatement || parent.getParent() instanceof PsiReferenceList;
438 public static void analyzeItem(final LookupItem item, final Object completion, final PsiElement position) {
439 if(completion instanceof PsiKeyword){
440 if(PsiKeyword.BREAK.equals(((PsiKeyword)completion).getText())
441 || PsiKeyword.CONTINUE.equals(((PsiKeyword)completion).getText())){
442 PsiElement scope = position;
443 while(true){
444 if (scope instanceof PsiFile
445 || scope instanceof PsiMethod
446 || scope instanceof PsiClassInitializer){
447 item.setTailType(TailType.SEMICOLON);
448 break;
450 else if (scope instanceof PsiLabeledStatement){
451 item.setTailType(TailType.NONE);
452 break;
454 scope = scope.getParent();
457 if(PsiKeyword.RETURN.equals(((PsiKeyword)completion).getText())){
458 PsiElement scope = position;
459 while(true){
460 if (scope instanceof PsiFile
461 || scope instanceof PsiClassInitializer){
462 item.setTailType(TailType.NONE);
463 break;
465 else if (scope instanceof PsiMethod){
466 final PsiMethod method = (PsiMethod)scope;
467 if(method.isConstructor() || PsiType.VOID.equals(method.getReturnType())) {
468 item.setTailType(TailType.SEMICOLON);
470 else item.setTailType(TailType.SPACE);
472 break;
474 scope = scope.getParent();
477 if(PsiKeyword.SYNCHRONIZED.equals(((PsiKeyword)completion).getText())){
478 if (PsiTreeUtil.getParentOfType(position, PsiMember.class, PsiCodeBlock.class) instanceof PsiCodeBlock){
479 item.setTailType(TailTypes.SYNCHRONIZED_LPARENTH);
486 public void beforeCompletion(@NotNull final CompletionInitializationContext context) {
487 final PsiFile file = context.getFile();
488 final Project project = context.getProject();
490 JavaCompletionUtil.initOffsets(file, project, context.getOffsetMap(), context.getCompletionType());
492 if (file instanceof PsiJavaFile) {
493 autoImport(file, context.getStartOffset() - 1, context.getEditor());
496 if (context.getCompletionType() == CompletionType.BASIC && file instanceof PsiJavaFile) {
497 if (semicolonNeeded(context)) {
498 context.setFileCopyPatcher(new DummyIdentifierPatcher(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";"));
499 return;
502 final PsiElement element = file.findElementAt(context.getStartOffset());
504 if (psiElement().inside(PsiAnnotation.class).accepts(element)) {
505 return;
508 context.setFileCopyPatcher(new DummyIdentifierPatcher(CompletionInitializationContext.DUMMY_IDENTIFIER.trim()));
512 private static boolean semicolonNeeded(CompletionInitializationContext context) {
513 HighlighterIterator iterator = ((EditorEx) context.getEditor()).getHighlighter().createIterator(context.getStartOffset());
514 if (iterator.atEnd()) return false;
516 if (iterator.getTokenType() == JavaTokenType.IDENTIFIER) {
517 iterator.advance();
520 if (!iterator.atEnd() && iterator.getTokenType() == JavaTokenType.LPARENTH) {
521 return true;
524 while (!iterator.atEnd() && JavaTokenType.WHITE_SPACE_OR_COMMENT_BIT_SET.contains(iterator.getTokenType())) {
525 iterator.advance();
528 if (iterator.atEnd() || iterator.getTokenType() != JavaTokenType.IDENTIFIER) return false;
529 iterator.advance();
531 while (!iterator.atEnd() && JavaTokenType.WHITE_SPACE_OR_COMMENT_BIT_SET.contains(iterator.getTokenType())) {
532 iterator.advance();
534 if (iterator.atEnd()) return false;
536 return iterator.getTokenType() == JavaTokenType.EQ || iterator.getTokenType() == JavaTokenType.LPARENTH;
539 private static void autoImport(final PsiFile file, int offset, final Editor editor) {
540 final CharSequence text = editor.getDocument().getCharsSequence();
541 while (offset > 0 && Character.isJavaIdentifierPart(text.charAt(offset))) offset--;
542 if (offset <= 0) return;
544 while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
545 if (offset <= 0 || text.charAt(offset) != '.') return;
547 offset--;
549 while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
550 if (offset <= 0) return;
552 PsiJavaCodeReferenceElement element = extractReference(PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiExpression.class, false));
553 if (element == null) return;
555 while (true) {
556 final PsiJavaCodeReferenceElement qualifier = extractReference(element.getQualifier());
557 if (qualifier == null) break;
559 element = qualifier;
561 if (!(element.getParent() instanceof PsiMethodCallExpression) && element.multiResolve(true).length == 0) {
562 new ImportClassFix(element).doFix(editor, false, false);
566 @Nullable
567 private static PsiJavaCodeReferenceElement extractReference(@Nullable PsiElement expression) {
568 if (expression instanceof PsiJavaCodeReferenceElement) {
569 return (PsiJavaCodeReferenceElement)expression;
571 if (expression instanceof PsiMethodCallExpression) {
572 return ((PsiMethodCallExpression)expression).getMethodExpression();
574 return null;