2 * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
3 * Use is subject to license terms.
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
;
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") {
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
;
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
)) {
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
)) {
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) {
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() {
171 completeAnnotationAttributeName(_result
, file
, insertedElement
, completionData
, checkAccess
);
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() {
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
),
190 result
.getPrefixMatcher())) {
191 JavaCompletionUtil
.highlightMemberOfContainer(element
.as(LookupItem
.class));
193 result
.addElement(TailTypeDecorator
.createDecorator(element
, TailType
.createSimpleTailType(';')));
195 setTailTypeByFile(element
, file
);
196 result
.addElement(element
);
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(
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() {
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
);
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) {
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);
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);
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
);
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;
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
;
444 if (scope
instanceof PsiFile
445 || scope
instanceof PsiMethod
446 || scope
instanceof PsiClassInitializer
){
447 item
.setTailType(TailType
.SEMICOLON
);
450 else if (scope
instanceof PsiLabeledStatement
){
451 item
.setTailType(TailType
.NONE
);
454 scope
= scope
.getParent();
457 if(PsiKeyword
.RETURN
.equals(((PsiKeyword
)completion
).getText())){
458 PsiElement scope
= position
;
460 if (scope
instanceof PsiFile
461 || scope
instanceof PsiClassInitializer
){
462 item
.setTailType(TailType
.NONE
);
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
);
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() + ";"));
502 final PsiElement element
= file
.findElementAt(context
.getStartOffset());
504 if (psiElement().inside(PsiAnnotation
.class).accepts(element
)) {
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
) {
520 if (!iterator
.atEnd() && iterator
.getTokenType() == JavaTokenType
.LPARENTH
) {
524 while (!iterator
.atEnd() && JavaTokenType
.WHITE_SPACE_OR_COMMENT_BIT_SET
.contains(iterator
.getTokenType())) {
528 if (iterator
.atEnd() || iterator
.getTokenType() != JavaTokenType
.IDENTIFIER
) return false;
531 while (!iterator
.atEnd() && JavaTokenType
.WHITE_SPACE_OR_COMMENT_BIT_SET
.contains(iterator
.getTokenType())) {
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;
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;
556 final PsiJavaCodeReferenceElement qualifier
= extractReference(element
.getQualifier());
557 if (qualifier
== null) break;
561 if (!(element
.getParent() instanceof PsiMethodCallExpression
) && element
.multiResolve(true).length
== 0) {
562 new ImportClassFix(element
).doFix(editor
, false, false);
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();