2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.codeInsight
.completion
;
18 import com
.intellij
.codeInsight
.TailType
;
19 import com
.intellij
.codeInsight
.TailTypes
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.ImportClassFix
;
21 import com
.intellij
.codeInsight
.hint
.ShowParameterInfoHandler
;
22 import com
.intellij
.codeInsight
.lookup
.LookupElement
;
23 import com
.intellij
.codeInsight
.lookup
.LookupItem
;
24 import com
.intellij
.codeInsight
.lookup
.LookupItemUtil
;
25 import com
.intellij
.codeInsight
.lookup
.TailTypeDecorator
;
26 import com
.intellij
.lang
.LangBundle
;
27 import com
.intellij
.lang
.StdLanguages
;
28 import com
.intellij
.openapi
.actionSystem
.IdeActions
;
29 import com
.intellij
.openapi
.application
.ApplicationManager
;
30 import com
.intellij
.openapi
.diagnostic
.Logger
;
31 import com
.intellij
.openapi
.editor
.Editor
;
32 import com
.intellij
.openapi
.editor
.ex
.EditorEx
;
33 import com
.intellij
.openapi
.editor
.highlighter
.HighlighterIterator
;
34 import com
.intellij
.openapi
.project
.Project
;
35 import com
.intellij
.openapi
.util
.Comparing
;
36 import com
.intellij
.openapi
.util
.Computable
;
37 import com
.intellij
.openapi
.util
.text
.StringUtil
;
38 import com
.intellij
.patterns
.ElementPattern
;
39 import com
.intellij
.patterns
.PatternCondition
;
40 import static com
.intellij
.patterns
.PsiJavaPatterns
.*;
41 import com
.intellij
.patterns
.PsiNameValuePairPattern
;
42 import com
.intellij
.psi
.*;
43 import com
.intellij
.psi
.filters
.*;
44 import com
.intellij
.psi
.filters
.classes
.AssignableFromContextFilter
;
45 import com
.intellij
.psi
.filters
.classes
.ThisOrAnyInnerFilter
;
46 import com
.intellij
.psi
.filters
.element
.ExcludeDeclaredFilter
;
47 import com
.intellij
.psi
.filters
.element
.ModifierFilter
;
48 import com
.intellij
.psi
.filters
.getters
.ExpectedTypesGetter
;
49 import com
.intellij
.psi
.filters
.types
.AssignableFromFilter
;
50 import com
.intellij
.psi
.scope
.ElementClassFilter
;
51 import com
.intellij
.psi
.util
.PsiTreeUtil
;
52 import com
.intellij
.psi
.util
.PsiUtil
;
53 import com
.intellij
.util
.PairConsumer
;
54 import com
.intellij
.util
.ProcessingContext
;
55 import org
.jetbrains
.annotations
.NotNull
;
56 import org
.jetbrains
.annotations
.Nullable
;
58 import java
.util
.Arrays
;
59 import java
.util
.HashSet
;
60 import java
.util
.LinkedHashSet
;
66 public class JavaCompletionContributor
extends CompletionContributor
{
67 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.completion.JavaCompletionContributor");
68 private static final Java15CompletionData ourJava15CompletionData
= new Java15CompletionData();
69 private static final JavaCompletionData ourJavaCompletionData
= new JavaCompletionData();
70 private static final PsiNameValuePairPattern NAME_VALUE_PAIR
= psiNameValuePair().withSuperParent(
72 psiElement(PsiAnnotation
.class));
73 private static final ElementPattern
<PsiElement
> ANNOTATION_ATTRIBUTE_NAME
=
74 or(psiElement(PsiIdentifier
.class).withParent(NAME_VALUE_PAIR
),
75 psiElement().afterLeaf("(").withParent(psiReferenceExpression().withParent(NAME_VALUE_PAIR
)));
76 public static final ElementPattern SWITCH_LABEL
=
77 psiElement().withSuperParent(2, psiElement(PsiSwitchLabelStatement
.class).withSuperParent(2,
78 psiElement(PsiSwitchStatement
.class).with(new PatternCondition
<PsiSwitchStatement
>("enumExpressionType") {
80 public boolean accepts(@NotNull PsiSwitchStatement psiSwitchStatement
, ProcessingContext context
) {
81 final PsiExpression expression
= psiSwitchStatement
.getExpression();
82 if(expression
== null) return false;
83 final PsiType type
= expression
.getType();
84 return type
instanceof PsiClassType
;
89 private static ElementFilter
getReferenceFilter(PsiElement position
) {
90 // Completion after extends in interface, type parameter and implements in class
91 final PsiClass containingClass
= PsiTreeUtil
.getParentOfType(position
, PsiClass
.class, false, PsiCodeBlock
.class, PsiMethod
.class, PsiExpressionList
.class);
92 if (containingClass
!= null && psiElement().afterLeaf(PsiKeyword
.EXTENDS
, PsiKeyword
.IMPLEMENTS
, ",", "&").accepts(position
)) {
93 return new AndFilter(ElementClassFilter
.CLASS
, new NotFilter(new AssignableFromContextFilter()));
96 if (JavaCompletionData
.DECLARATION_START
.isAcceptable(position
, position
)) {
97 return new OrFilter(ElementClassFilter
.CLASS
, ElementClassFilter
.PACKAGE_FILTER
);
100 if (JavaCompletionData
.INSIDE_PARAMETER_LIST
.accepts(position
)) {
101 return ElementClassFilter
.CLASS
;
104 // Completion for classes in method throws section
105 if (psiElement().afterLeaf(PsiKeyword
.THROWS
, ",").inside(psiElement(PsiReferenceList
.class).withParent(PsiMethod
.class)).accepts(position
)) {
106 return new OrFilter(new ThisOrAnyInnerFilter(new AssignableFromFilter("java.lang.Throwable")), ElementClassFilter
.PACKAGE_FILTER
);
109 if (psiElement().afterLeaf(PsiKeyword
.INSTANCEOF
).accepts(position
)) {
110 return new ElementExtractorFilter(ElementClassFilter
.CLASS
);
113 if (JavaCompletionData
.AFTER_FINAL
.accepts(position
)) {
114 return ElementClassFilter
.CLASS
;
117 if (JavaCompletionData
.AFTER_TRY_BLOCK
.isAcceptable(position
, position
) ||
118 JavaCompletionData
.START_SWITCH
.isAcceptable(position
, position
) ||
119 JavaCompletionData
.INSTANCEOF_PLACE
.isAcceptable(position
, position
)) {
123 if (psiElement().afterLeaf(psiElement().withText("(").withParent(PsiTryStatement
.class)).accepts(position
)) {
124 return new OrFilter(new ThisOrAnyInnerFilter(new AssignableFromFilter(CommonClassNames
.JAVA_LANG_THROWABLE
)), ElementClassFilter
.PACKAGE_FILTER
);
127 if (JavaSmartCompletionContributor
.AFTER_THROW_NEW
.accepts(position
)) {
128 return new ThisOrAnyInnerFilter(new AssignableFromFilter("java.lang.Throwable"));
131 if (JavaSmartCompletionContributor
.AFTER_NEW
.accepts(position
)) {
132 return ElementClassFilter
.CLASS
;
135 if (psiElement().inside(PsiReferenceParameterList
.class).accepts(position
)) {
136 return ElementClassFilter
.CLASS
;
139 if (psiElement().inside(PsiAnnotationParameterList
.class).accepts(position
)) {
140 return new OrFilter(new ClassFilter(PsiAnnotationMethod
.class), ElementClassFilter
.CLASS
, ElementClassFilter
.PACKAGE_FILTER
, new AndFilter(new ClassFilter(PsiField
.class),
141 new ModifierFilter(PsiModifier
.STATIC
, PsiModifier
.FINAL
)));
144 if (psiElement().afterLeaf("=").inside(PsiVariable
.class).accepts(position
)) {
146 new ClassFilter(PsiVariable
.class, false),
147 new ExcludeDeclaredFilter(new ClassFilter(PsiVariable
.class)));
150 if (SWITCH_LABEL
.accepts(position
)) {
151 return new ClassFilter(PsiField
.class) {
153 public boolean isAcceptable(Object element
, PsiElement context
) {
154 return element
instanceof PsiEnumConstant
;
159 return TrueFilter
.INSTANCE
;
162 public void fillCompletionVariants(final CompletionParameters parameters
, final CompletionResultSet _result
) {
163 if (parameters
.getCompletionType() != CompletionType
.BASIC
) {
167 final PsiElement insertedElement
= parameters
.getPosition();
168 if (insertedElement
.getContainingFile().getLanguage() != StdLanguages
.JAVA
) {
172 final PsiFile file
= parameters
.getOriginalFile();
173 final int startOffset
= parameters
.getOffset();
174 final PsiElement lastElement
= file
.findElementAt(startOffset
- 1);
175 final JavaAwareCompletionData completionData
= ApplicationManager
.getApplication().runReadAction(new Computable
<JavaAwareCompletionData
>() {
176 public JavaAwareCompletionData
compute() {
177 return getCompletionDataByElementInner(lastElement
);
181 if (ANNOTATION_ATTRIBUTE_NAME
.accepts(insertedElement
)) {
182 ApplicationManager
.getApplication().runReadAction(new Runnable() {
184 completeAnnotationAttributeName(_result
, file
, insertedElement
, parameters
);
191 final boolean checkAccess
= parameters
.getInvocationCount() == 1;
193 LegacyCompletionContributor
.processReferences(parameters
, _result
, completionData
, new PairConsumer
<PsiReference
, CompletionResultSet
>() {
194 public void consume(final PsiReference reference
, final CompletionResultSet result
) {
195 ApplicationManager
.getApplication().runReadAction(new Runnable() {
197 if (reference
instanceof PsiJavaReference
) {
198 final ElementFilter filter
= getReferenceFilter(insertedElement
);
199 if (filter
!= null) {
200 final boolean isSwitchLabel
= SWITCH_LABEL
.accepts(insertedElement
);
201 for (LookupElement element
: JavaCompletionUtil
.processJavaReference(insertedElement
,
202 (PsiJavaReference
) reference
,
203 new ElementExtractorFilter(filter
),
205 result
.getPrefixMatcher(), parameters
)) {
207 result
.addElement(TailTypeDecorator
.withTail(element
, TailType
.createSimpleTailType(':')));
209 final LookupItem item
= element
.as(LookupItem
.class);
210 if (file
instanceof PsiJavaCodeReferenceCodeFragment
&&
211 !((PsiJavaCodeReferenceCodeFragment
)file
).isClassesAccepted() && item
!= null) {
212 item
.setTailType(TailType
.NONE
);
215 result
.addElement(element
);
223 final Object
[] variants
= reference
.getVariants();
224 if (variants
== null) {
225 LOG
.assertTrue(false, "Reference=" + reference
);
227 for (Object completion
: variants
) {
228 if (completion
== null) {
229 LOG
.assertTrue(false, "Position=" + insertedElement
+ "\n;Reference=" + reference
+ "\n;variants=" + Arrays
.toString(
232 if (completion
instanceof LookupElement
) {
233 result
.addElement((LookupElement
)completion
);
235 result
.addElement(LookupItemUtil
.objectToLookupItem(completion
));
243 final Set
<LookupElement
> lookupSet
= new LinkedHashSet
<LookupElement
>();
244 final Set
<CompletionVariant
> keywordVariants
= new HashSet
<CompletionVariant
>();
245 completionData
.addKeywordVariants(keywordVariants
, insertedElement
, parameters
.getOriginalFile());
246 final CompletionResultSet result
= _result
.withPrefixMatcher(completionData
.findPrefix(insertedElement
, startOffset
));
247 completionData
.completeKeywordsBySet(lookupSet
, keywordVariants
, insertedElement
, result
.getPrefixMatcher(), parameters
.getOriginalFile());
249 ApplicationManager
.getApplication().runReadAction(new Runnable() {
251 completionData
.fillCompletions(parameters
, result
);
255 for (final LookupElement item
: lookupSet
) {
256 if (item
instanceof LookupItem
&& ((LookupItem
)item
).getInsertHandler() == null) {
257 ((LookupItem
)item
).setInsertHandler(new InsertHandler() {
258 public void handleInsert(final InsertionContext context
, final LookupElement item
) {
259 analyzeItem((LookupItem
)item
, item
.getObject(), parameters
.getPosition());
260 new DefaultInsertHandler().handleInsert(context
, item
);
265 result
.addElement(item
);
270 private static void completeAnnotationAttributeName(CompletionResultSet result
, PsiFile file
, PsiElement insertedElement
,
271 CompletionParameters parameters
) {
272 PsiNameValuePair pair
= PsiTreeUtil
.getParentOfType(insertedElement
, PsiNameValuePair
.class);
273 PsiAnnotationParameterList parameterList
= (PsiAnnotationParameterList
)pair
.getParent();
274 PsiAnnotation anno
= (PsiAnnotation
)parameterList
.getParent();
275 boolean showClasses
= psiElement().afterLeaf("(").accepts(insertedElement
);
276 PsiClass annoClass
= null;
277 final PsiJavaCodeReferenceElement referenceElement
= anno
.getNameReferenceElement();
278 if (referenceElement
!= null) {
279 final PsiElement element
= referenceElement
.resolve();
280 if (element
instanceof PsiClass
) {
281 annoClass
= (PsiClass
)element
;
282 if (annoClass
.findMethodsByName("value", false).length
== 0) {
288 if (showClasses
&& insertedElement
.getParent() instanceof PsiReferenceExpression
) {
289 final Set
<LookupElement
> set
= JavaCompletionUtil
.processJavaReference(insertedElement
, (PsiJavaReference
)insertedElement
.getParent(), TrueFilter
.INSTANCE
, true, result
.getPrefixMatcher(), parameters
);
291 for (final LookupElement element
: set
) {
292 result
.addElement(element
);
297 if (annoClass
!= null) {
298 final PsiNameValuePair
[] existingPairs
= parameterList
.getAttributes();
300 methods
: for (PsiMethod method
: annoClass
.getMethods()) {
301 final String attrName
= method
.getName();
302 for (PsiNameValuePair apair
: existingPairs
) {
303 if (Comparing
.equal(apair
.getName(), attrName
)) continue methods
;
305 result
.addElement(new LookupItem
<PsiMethod
>(method
, attrName
).setInsertHandler(new InsertHandler
<LookupElement
>() {
306 public void handleInsert(InsertionContext context
, LookupElement item
) {
307 final Editor editor
= context
.getEditor();
308 TailType
.EQ
.processTail(editor
, editor
.getCaretModel().getOffset());
309 context
.setAddCompletionChar(false);
316 private static JavaAwareCompletionData
getCompletionDataByElementInner(PsiElement element
) {
317 return element
!= null && PsiUtil
.isLanguageLevel5OrHigher(element
) ? ourJava15CompletionData
: ourJavaCompletionData
;
321 public String
advertise(@NotNull final CompletionParameters parameters
) {
322 if (!(parameters
.getOriginalFile() instanceof PsiJavaFile
)) return null;
324 if (parameters
.getCompletionType() != CompletionType
.SMART
&& shouldSuggestSmartCompletion(parameters
.getPosition())) {
325 if (CompletionUtil
.shouldShowFeature(parameters
, CodeCompletionFeatures
.EDITING_COMPLETION_SMARTTYPE_GENERAL
)) {
326 final String shortcut
= getActionShortcut(IdeActions
.ACTION_SMART_TYPE_COMPLETION
);
327 if (shortcut
!= null) {
328 return CompletionBundle
.message("completion.smart.hint", shortcut
);
333 if (parameters
.getCompletionType() != CompletionType
.CLASS_NAME
&& shouldSuggestClassNameCompletion(parameters
.getPosition())) {
334 if (CompletionUtil
.shouldShowFeature(parameters
, CodeCompletionFeatures
.EDITING_COMPLETION_CLASSNAME
)) {
335 final String shortcut
= getActionShortcut(IdeActions
.ACTION_CLASS_NAME_COMPLETION
);
336 if (shortcut
!= null) {
337 return CompletionBundle
.message("completion.class.name.hint", shortcut
);
342 if (parameters
.getCompletionType() == CompletionType
.SMART
&& parameters
.getInvocationCount() == 1) {
343 final PsiType
[] psiTypes
= ExpectedTypesGetter
.getExpectedTypes(parameters
.getPosition(), true);
344 if (psiTypes
.length
> 0) {
345 if (CompletionUtil
.shouldShowFeature(parameters
, JavaCompletionFeatures
.SECOND_SMART_COMPLETION_TOAR
)) {
346 final String shortcut
= getActionShortcut(IdeActions
.ACTION_SMART_TYPE_COMPLETION
);
347 if (shortcut
!= null) {
348 for (final PsiType psiType
: psiTypes
) {
349 final PsiType type
= PsiUtil
.extractIterableTypeParameter(psiType
, false);
351 return CompletionBundle
.message("completion.smart.aslist.hint", shortcut
, type
.getPresentableText());
356 if (CompletionUtil
.shouldShowFeature(parameters
, JavaCompletionFeatures
.SECOND_SMART_COMPLETION_ASLIST
)) {
357 final String shortcut
= getActionShortcut(IdeActions
.ACTION_SMART_TYPE_COMPLETION
);
358 if (shortcut
!= null) {
359 for (final PsiType psiType
: psiTypes
) {
360 if (psiType
instanceof PsiArrayType
) {
361 final PsiType componentType
= ((PsiArrayType
)psiType
).getComponentType();
362 if (!(componentType
instanceof PsiPrimitiveType
)) {
363 return CompletionBundle
.message("completion.smart.toar.hint", shortcut
, componentType
.getPresentableText());
370 if (CompletionUtil
.shouldShowFeature(parameters
, JavaCompletionFeatures
.SECOND_SMART_COMPLETION_CHAIN
)) {
371 final String shortcut
= getActionShortcut(IdeActions
.ACTION_SMART_TYPE_COMPLETION
);
372 if (shortcut
!= null) {
373 return CompletionBundle
.message("completion.smart.chain.hint", shortcut
);
381 public String
handleEmptyLookup(@NotNull final CompletionParameters parameters
, final Editor editor
) {
382 if (!(parameters
.getOriginalFile() instanceof PsiJavaFile
)) return null;
384 final String ad
= advertise(parameters
);
385 final String suffix
= ad
== null ?
"" : "; " + StringUtil
.decapitalize(ad
);
386 if (parameters
.getCompletionType() == CompletionType
.SMART
) {
387 if (!ApplicationManager
.getApplication().isUnitTestMode()) {
389 final Project project
= parameters
.getPosition().getProject();
390 final PsiFile file
= parameters
.getOriginalFile();
392 PsiExpression expression
= PsiTreeUtil
.getContextOfType(parameters
.getPosition(), PsiExpression
.class, true);
393 if (expression
!= null && expression
.getParent() instanceof PsiExpressionList
) {
394 int lbraceOffset
= expression
.getParent().getTextRange().getStartOffset();
395 new ShowParameterInfoHandler().invoke(project
, editor
, file
, lbraceOffset
, null);
398 if (expression
instanceof PsiLiteralExpression
) {
399 return LangBundle
.message("completion.no.suggestions") + suffix
;
402 if (expression
instanceof PsiInstanceOfExpression
) {
403 final PsiInstanceOfExpression instanceOfExpression
= (PsiInstanceOfExpression
)expression
;
404 if (PsiTreeUtil
.isAncestor(instanceOfExpression
.getCheckType(), parameters
.getPosition(), false)) {
405 return LangBundle
.message("completion.no.suggestions") + suffix
;
410 final Set
<PsiType
> expectedTypes
= JavaCompletionUtil
.getExpectedTypes(parameters
);
411 if (expectedTypes
!= null) {
412 PsiType type
= expectedTypes
.size() == 1 ? expectedTypes
.iterator().next() : null;
414 final PsiType deepComponentType
= type
.getDeepComponentType();
415 if (deepComponentType
instanceof PsiClassType
) {
416 if (((PsiClassType
)deepComponentType
).resolve() != null) {
417 return CompletionBundle
.message("completion.no.suggestions.of.type", type
.getPresentableText()) + suffix
;
419 return CompletionBundle
.message("completion.unknown.type", type
.getPresentableText()) + suffix
;
421 if (!PsiType
.NULL
.equals(type
)) {
422 return CompletionBundle
.message("completion.no.suggestions.of.type", type
.getPresentableText()) + suffix
;
427 return LangBundle
.message("completion.no.suggestions") + suffix
;
430 private static boolean shouldSuggestSmartCompletion(final PsiElement element
) {
431 if (shouldSuggestClassNameCompletion(element
)) return false;
433 final PsiElement parent
= element
.getParent();
434 if (parent
instanceof PsiReferenceExpression
&& ((PsiReferenceExpression
)parent
).getQualifier() != null) return false;
435 if (parent
instanceof PsiReferenceExpression
&& parent
.getParent() instanceof PsiReferenceExpression
) return true;
437 return new ExpectedTypesGetter().get(element
, null).length
> 0;
440 private static boolean shouldSuggestClassNameCompletion(final PsiElement element
) {
441 if (element
== null) return false;
442 final PsiElement parent
= element
.getParent();
443 if (parent
== null) return false;
444 return parent
.getParent() instanceof PsiTypeElement
|| parent
.getParent() instanceof PsiExpressionStatement
|| parent
.getParent() instanceof PsiReferenceList
;
447 public static void analyzeItem(final LookupItem item
, final Object completion
, final PsiElement position
) {
448 if(completion
instanceof PsiKeyword
){
449 if(PsiKeyword
.BREAK
.equals(((PsiKeyword
)completion
).getText())
450 || PsiKeyword
.CONTINUE
.equals(((PsiKeyword
)completion
).getText())){
451 PsiElement scope
= position
;
453 if (scope
instanceof PsiFile
454 || scope
instanceof PsiMethod
455 || scope
instanceof PsiClassInitializer
){
456 item
.setTailType(TailType
.SEMICOLON
);
459 else if (scope
instanceof PsiLabeledStatement
){
460 item
.setTailType(TailType
.NONE
);
463 scope
= scope
.getParent();
466 if(PsiKeyword
.RETURN
.equals(((PsiKeyword
)completion
).getText())){
467 PsiElement scope
= position
;
469 if (scope
instanceof PsiFile
470 || scope
instanceof PsiClassInitializer
){
471 item
.setTailType(TailType
.NONE
);
474 else if (scope
instanceof PsiMethod
){
475 final PsiMethod method
= (PsiMethod
)scope
;
476 if(method
.isConstructor() || PsiType
.VOID
.equals(method
.getReturnType())) {
477 item
.setTailType(TailType
.SEMICOLON
);
479 else item
.setTailType(TailType
.SPACE
);
483 scope
= scope
.getParent();
486 if(PsiKeyword
.SYNCHRONIZED
.equals(((PsiKeyword
)completion
).getText())){
487 if (PsiTreeUtil
.getParentOfType(position
, PsiMember
.class, PsiCodeBlock
.class) instanceof PsiCodeBlock
){
488 item
.setTailType(TailTypes
.SYNCHRONIZED_LPARENTH
);
495 public void beforeCompletion(@NotNull final CompletionInitializationContext context
) {
496 final PsiFile file
= context
.getFile();
497 final Project project
= context
.getProject();
499 JavaCompletionUtil
.initOffsets(file
, project
, context
.getOffsetMap());
501 if (file
instanceof PsiJavaFile
) {
502 autoImport(file
, context
.getStartOffset() - 1, context
.getEditor());
505 if (context
.getCompletionType() == CompletionType
.BASIC
&& file
instanceof PsiJavaFile
) {
506 if (semicolonNeeded(context
)) {
507 context
.setFileCopyPatcher(new DummyIdentifierPatcher(CompletionInitializationContext
.DUMMY_IDENTIFIER
.trim() + ";"));
511 final PsiElement element
= file
.findElementAt(context
.getStartOffset());
513 if (psiElement().inside(PsiAnnotation
.class).accepts(element
)) {
517 context
.setFileCopyPatcher(new DummyIdentifierPatcher(CompletionInitializationContext
.DUMMY_IDENTIFIER
.trim()));
521 private static boolean semicolonNeeded(CompletionInitializationContext context
) {
522 HighlighterIterator iterator
= ((EditorEx
) context
.getEditor()).getHighlighter().createIterator(context
.getStartOffset());
523 if (iterator
.atEnd()) return false;
525 if (iterator
.getTokenType() == JavaTokenType
.IDENTIFIER
) {
529 if (!iterator
.atEnd() && iterator
.getTokenType() == JavaTokenType
.LPARENTH
) {
533 while (!iterator
.atEnd() && JavaTokenType
.WHITE_SPACE_OR_COMMENT_BIT_SET
.contains(iterator
.getTokenType())) {
537 if (iterator
.atEnd() || iterator
.getTokenType() != JavaTokenType
.IDENTIFIER
) return false;
540 while (!iterator
.atEnd() && JavaTokenType
.WHITE_SPACE_OR_COMMENT_BIT_SET
.contains(iterator
.getTokenType())) {
543 if (iterator
.atEnd()) return false;
545 return iterator
.getTokenType() == JavaTokenType
.EQ
|| iterator
.getTokenType() == JavaTokenType
.LPARENTH
;
548 private static void autoImport(final PsiFile file
, int offset
, final Editor editor
) {
549 final CharSequence text
= editor
.getDocument().getCharsSequence();
550 while (offset
> 0 && Character
.isJavaIdentifierPart(text
.charAt(offset
))) offset
--;
551 if (offset
<= 0) return;
553 while (offset
> 0 && Character
.isWhitespace(text
.charAt(offset
))) offset
--;
554 if (offset
<= 0 || text
.charAt(offset
) != '.') return;
558 while (offset
> 0 && Character
.isWhitespace(text
.charAt(offset
))) offset
--;
559 if (offset
<= 0) return;
561 PsiJavaCodeReferenceElement element
= extractReference(PsiTreeUtil
.findElementOfClassAtOffset(file
, offset
, PsiExpression
.class, false));
562 if (element
== null) return;
565 final PsiJavaCodeReferenceElement qualifier
= extractReference(element
.getQualifier());
566 if (qualifier
== null) break;
570 if (!(element
.getParent() instanceof PsiMethodCallExpression
) && element
.multiResolve(true).length
== 0) {
571 new ImportClassFix(element
).doFix(editor
, false, false);
576 private static PsiJavaCodeReferenceElement
extractReference(@Nullable PsiElement expression
) {
577 if (expression
instanceof PsiJavaCodeReferenceElement
) {
578 return (PsiJavaCodeReferenceElement
)expression
;
580 if (expression
instanceof PsiMethodCallExpression
) {
581 return ((PsiMethodCallExpression
)expression
).getMethodExpression();