IDEA-79862 Java: auto-insert space after completing "extends" and "implements"
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / completion / JavaCompletionData.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.codeInsight.completion;
17
18 import com.intellij.codeInsight.ExpectedTypeInfo;
19 import com.intellij.codeInsight.TailType;
20 import com.intellij.codeInsight.TailTypes;
21 import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
22 import com.intellij.codeInsight.lookup.LookupElement;
23 import com.intellij.codeInsight.lookup.LookupItem;
24 import com.intellij.codeInsight.lookup.TailTypeDecorator;
25 import com.intellij.patterns.*;
26 import com.intellij.pom.java.LanguageLevel;
27 import com.intellij.psi.*;
28 import com.intellij.psi.filters.*;
29 import com.intellij.psi.filters.classes.EnumOrAnnotationTypeFilter;
30 import com.intellij.psi.filters.classes.InterfaceFilter;
31 import com.intellij.psi.filters.element.ReferenceOnFilter;
32 import com.intellij.psi.filters.getters.JavaMembersGetter;
33 import com.intellij.psi.filters.position.*;
34 import com.intellij.psi.filters.types.TypeCodeFragmentIsVoidEnabledFilter;
35 import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement;
36 import com.intellij.psi.jsp.JspElementType;
37 import com.intellij.psi.templateLanguages.OuterLanguageElement;
38 import com.intellij.psi.util.PsiTreeUtil;
39 import com.intellij.psi.util.PsiUtil;
40 import com.intellij.util.Consumer;
41 import com.intellij.util.ProcessingContext;
42 import org.jetbrains.annotations.NonNls;
43
44 import static com.intellij.patterns.PsiJavaPatterns.*;
45 import static com.intellij.patterns.StandardPatterns.not;
46
47 public class JavaCompletionData extends JavaAwareCompletionData{
48
49   private static final @NonNls String[] ourBlockFinalizers = {"{", "}", ";", ":", "else"};
50   private static final PsiElementPattern<PsiElement,?> AFTER_DOT = psiElement().afterLeaf(".");
51   public static final LeftNeighbour INSTANCEOF_PLACE = new LeftNeighbour(new OrFilter(
52       new ReferenceOnFilter(new ClassFilter(PsiVariable.class)),
53       new TextFilter(PsiKeyword.THIS),
54       new AndFilter(new TextFilter(")"), new ParentElementFilter(new AndFilter(
55         new ClassFilter(PsiTypeCastExpression.class, false),
56         new OrFilter(
57           new ParentElementFilter(new ClassFilter(PsiExpression.class)),
58           new ClassFilter(PsiExpression.class))))),
59       new AndFilter(new TextFilter("]"), new ParentElementFilter(new ClassFilter(PsiArrayAccessExpression.class)))));
60   public static final PsiJavaElementPattern.Capture<PsiElement> VARIABLE_AFTER_FINAL =
61     PsiJavaPatterns.psiElement().afterLeaf(PsiKeyword.FINAL).inside(PsiDeclarationStatement.class);
62   public static final LeftNeighbour AFTER_TRY_BLOCK = new LeftNeighbour(new AndFilter(
63     new TextFilter("}"),
64     new ParentElementFilter(new AndFilter(
65       new LeftNeighbour(new TextFilter(PsiKeyword.TRY)),
66       new ParentElementFilter(new ClassFilter(PsiTryStatement.class)))
67     )));
68   public static final PsiJavaElementPattern.Capture<PsiElement> INSIDE_PARAMETER_LIST =
69     PsiJavaPatterns.psiElement().withParent(
70       psiElement(PsiJavaCodeReferenceElement.class).insideStarting(
71         psiElement().withTreeParent(
72           psiElement(PsiParameterList.class).andNot(psiElement(PsiAnnotationParameterList.class)))));
73
74   private static final AndFilter START_OF_CODE_FRAGMENT = new AndFilter(
75     new ScopeFilter(new AndFilter(
76       new ClassFilter(JavaCodeFragment.class),
77       new ClassFilter(PsiExpressionCodeFragment.class, false),
78       new ClassFilter(PsiJavaCodeReferenceCodeFragment.class, false),
79       new ClassFilter(PsiTypeCodeFragment.class, false)
80     )),
81     new StartElementFilter()
82   );
83
84   static final ElementFilter END_OF_BLOCK = new OrFilter(
85     new AndFilter(
86       new LeftNeighbour(
87           new OrFilter(
88               new AndFilter (
89                   new TextFilter(ourBlockFinalizers),
90                   new NotFilter (
91                     new SuperParentFilter(new ClassFilter(PsiAnnotation.class))
92                   )
93               ),
94               new TextFilter("*/"),
95               new TokenTypeFilter(JspElementType.HOLDER_TEMPLATE_DATA),
96               new ClassFilter(OuterLanguageElement.class),
97               new AndFilter(
98                   new TextFilter(")"),
99                   new NotFilter(
100                       new OrFilter(
101                           new ParentElementFilter(new ClassFilter(PsiExpressionList.class)),
102                           new ParentElementFilter(new ClassFilter(PsiParameterList.class)),
103                           new ParentElementFilter(new ClassFilter(PsiTypeCastExpression.class))
104                       )
105                   )
106               ))),
107       new NotFilter(new TextFilter("."))
108     ),
109     START_OF_CODE_FRAGMENT
110   );
111
112   static final ElementPattern<PsiElement> START_SWITCH = psiElement().afterLeaf(psiElement().withText("{").withParents(PsiCodeBlock.class, PsiSwitchStatement.class));
113
114   private static final ElementPattern<PsiElement> SUPER_OR_THIS_PATTERN =
115     and(JavaSmartCompletionContributor.INSIDE_EXPRESSION,
116         not(psiElement().afterLeaf(PsiKeyword.CASE)),
117         not(psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.THIS, PsiKeyword.SUPER))),
118         not(START_SWITCH));
119
120
121   public static final AndFilter CLASS_START = new AndFilter(
122     new OrFilter(
123       END_OF_BLOCK,
124       new PatternFilter(psiElement().afterLeaf(
125         or(
126           psiElement().withoutText(".").inside(psiElement(PsiModifierList.class)),
127           psiElement().isNull())))
128     ),
129     new PatternFilter(not(psiElement().afterLeaf("@"))));
130
131   private static final String[] PRIMITIVE_TYPES = new String[]{
132     PsiKeyword.SHORT, PsiKeyword.BOOLEAN,
133     PsiKeyword.DOUBLE, PsiKeyword.LONG,
134     PsiKeyword.INT, PsiKeyword.FLOAT,
135     PsiKeyword.CHAR, PsiKeyword.BYTE
136   };
137
138   final static ElementFilter CLASS_BODY = new OrFilter(
139     new AfterElementFilter(new TextFilter("{")),
140     new ScopeFilter(new ClassFilter(JspClassLevelDeclarationStatement.class)));
141   public static final ElementPattern<PsiElement> START_FOR =
142     psiElement().afterLeaf(psiElement().withText("(").afterLeaf("for")).withParents(PsiJavaCodeReferenceElement.class,
143                                                                                     PsiExpressionStatement.class, PsiForStatement.class);
144   private static final PsiJavaElementPattern.Capture<PsiElement> CLASS_REFERENCE =
145     psiElement().withParent(psiReferenceExpression().referencing(psiClass()));
146   public static final ElementPattern<PsiElement> EXPR_KEYWORDS = and(
147     psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent(
148       not(
149         or(psiElement(PsiTypeCastExpression.class),
150            psiElement(PsiSwitchLabelStatement.class),
151            psiElement(PsiExpressionStatement.class)
152         )
153       )
154     )),
155     not(psiElement().afterLeaf("."))
156   );
157
158   public JavaCompletionData(){
159     declareCompletionSpaces();
160
161     initVariantsInFileScope();
162     initVariantsInClassScope();
163     initVariantsInMethodScope();
164
165     defineScopeEquivalence(PsiMethod.class, PsiClassInitializer.class);
166     defineScopeEquivalence(PsiMethod.class, JavaCodeFragment.class);
167   }
168
169   public static final ElementPattern<PsiElement> DECLARATION_START = psiElement().andNot(psiElement().afterLeaf("@", ".")).
170     andOr(
171       psiElement().and(new FilterPattern(CLASS_BODY)).
172         andOr(
173           new FilterPattern(END_OF_BLOCK),
174           psiElement().afterLeaf(or(
175             psiElement().inside(PsiModifierList.class),
176             psiElement().withElementType(JavaTokenType.GT).inside(PsiTypeParameterList.class)
177           ))),
178       psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiMember.class),
179       psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiClassLevelDeclarationStatement.class)
180     );
181
182   private void declareCompletionSpaces() {
183     declareFinalScope(PsiFile.class);
184
185     {
186       // Class body
187       final CompletionVariant variant = new CompletionVariant(CLASS_BODY);
188       variant.includeScopeClass(PsiClass.class, true);
189       registerVariant(variant);
190     }
191     {
192       // Method body
193       final CompletionVariant variant = new CompletionVariant(new AndFilter(new InsideElementFilter(new ClassFilter(PsiCodeBlock.class)),
194                                                                             new NotFilter(new InsideElementFilter(new ClassFilter(JspClassLevelDeclarationStatement.class)))));
195       variant.includeScopeClass(PsiMethod.class, true);
196       variant.includeScopeClass(PsiClassInitializer.class, true);
197       registerVariant(variant);
198     }
199
200     {
201       // Field initializer
202       final CompletionVariant variant = new CompletionVariant(new AfterElementFilter(new TextFilter("=")));
203       variant.includeScopeClass(PsiField.class, true);
204       registerVariant(variant);
205     }
206
207     declareFinalScope(PsiLiteralExpression.class);
208     declareFinalScope(PsiComment.class);
209   }
210
211   protected void initVariantsInFileScope(){
212 // package keyword completion
213     {
214       final CompletionVariant variant = new CompletionVariant(PsiJavaFile.class, new StartElementFilter());
215       variant.addCompletion(PsiKeyword.PACKAGE, TailType.INSERT_SPACE);
216       registerVariant(variant);
217     }
218
219 // import keyword completion
220     {
221       final CompletionVariant variant = new CompletionVariant(PsiJavaFile.class, new OrFilter(
222         new StartElementFilter(),
223         END_OF_BLOCK
224       ));
225       variant.addCompletion(PsiKeyword.IMPORT);
226
227       registerVariant(variant);
228     }
229 // other in file scope
230     {
231       final CompletionVariant variant = new CompletionVariant(PsiJavaFile.class, CLASS_START);
232       variant.includeScopeClass(PsiClass.class);
233
234       variant.addCompletion(PsiKeyword.CLASS);
235       variant.addCompletion(PsiKeyword.INTERFACE);
236
237       registerVariant(variant);
238     }
239
240     {
241       final CompletionVariant variant = new CompletionVariant(PsiTypeCodeFragment.class, new StartElementFilter());
242       addPrimitiveTypes(variant, TailType.NONE);
243       final CompletionVariant variant1 = new CompletionVariant(PsiTypeCodeFragment.class,
244                                                                new AndFilter(
245                                                                  new StartElementFilter(),
246                                                                  new TypeCodeFragmentIsVoidEnabledFilter()
247                                                                )
248                                                                );
249       variant1.addCompletion(PsiKeyword.VOID, TailType.NONE);
250       registerVariant(variant);
251       registerVariant(variant1);
252
253     }
254
255   }
256
257   /**
258    * aClass == null for JspDeclaration scope
259    */
260   protected void initVariantsInClassScope() {
261 // Completion for extends keyword
262 // position
263     {
264       final ElementFilter position = new AndFilter(
265           new NotFilter(CLASS_BODY),
266           new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))),
267           new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))),
268           new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))),
269           new NotFilter(new ScopeFilter(new EnumOrAnnotationTypeFilter())),
270           new LeftNeighbour(new OrFilter(
271             new ClassFilter(PsiIdentifier.class),
272             new TextFilter(">"))));
273 // completion
274       final CompletionVariant variant = new CompletionVariant(position);
275       variant.includeScopeClass(PsiClass.class, true);
276       variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD);
277       variant.excludeScopeClass(PsiAnonymousClass.class);
278       variant.excludeScopeClass(PsiTypeParameter.class);
279
280       registerVariant(variant);
281     }
282 // Completion for implements keyword
283 // position
284     {
285       final ElementFilter position = new AndFilter(
286           new NotFilter(CLASS_BODY),
287           new NotFilter(new BeforeElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))),
288           new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))),
289           new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))),
290           new LeftNeighbour(new OrFilter(
291             new ClassFilter(PsiIdentifier.class),
292             new TextFilter(">"))),
293           new NotFilter(new ScopeFilter(new InterfaceFilter())));
294 // completion
295       final CompletionVariant variant = new CompletionVariant(position);
296       variant.includeScopeClass(PsiClass.class, true);
297       variant.addCompletion(PsiKeyword.IMPLEMENTS, TailType.HUMBLE_SPACE_BEFORE_WORD);
298       variant.excludeScopeClass(PsiAnonymousClass.class);
299
300       registerVariant(variant);
301     }
302
303
304     {
305       final CompletionVariant variant = new CompletionVariant(PsiElement.class, psiElement().afterLeaf(
306           psiElement(PsiIdentifier.class).afterLeaf(
307             psiElement().withText(string().oneOf(",", "<")).withParent(PsiTypeParameterList.class))));
308       //variant.includeScopeClass(PsiClass.class, true);
309       variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD);
310       registerVariant(variant);
311     }
312   }
313
314   private void initVariantsInMethodScope() {
315 // Completion for classes in method throws section
316 // position
317     {
318       final ElementFilter position = new LeftNeighbour(new AndFilter(
319         new TextFilter(")"),
320         new ParentElementFilter(new ClassFilter(PsiParameterList.class))));
321
322 // completion
323       CompletionVariant variant = new CompletionVariant(PsiMethod.class, position);
324       variant.includeScopeClass(PsiClass.class); // for throws on separate line
325       variant.addCompletion(PsiKeyword.THROWS);
326
327       registerVariant(variant);
328
329 //in annotation methods
330       variant = new CompletionVariant(PsiAnnotationMethod.class, position);
331       variant.addCompletion(PsiKeyword.DEFAULT);
332       registerVariant(variant);
333     }
334
335     {
336 // instanceof keyword
337       final ElementFilter position = INSTANCEOF_PLACE;
338       final CompletionVariant variant = new CompletionVariant(position);
339       variant.includeScopeClass(PsiExpression.class, true);
340       variant.includeScopeClass(PsiMethod.class);
341       variant.addCompletion(PsiKeyword.INSTANCEOF);
342
343       registerVariant(variant);
344     }
345
346     {
347 // Keyword completion in returns  !!!!
348       final CompletionVariant variant = new CompletionVariant(PsiMethod.class, new LeftNeighbour(new TextFilter(PsiKeyword.RETURN)));
349       variant.addCompletion(PsiKeyword.TRUE, TailType.NONE);
350       variant.addCompletion(PsiKeyword.FALSE, TailType.NONE);
351       registerVariant(variant);
352     }
353
354
355 // Catch/Finally completion
356     {
357       final ElementFilter position = AFTER_TRY_BLOCK;
358
359       final CompletionVariant variant = new CompletionVariant(position);
360       variant.includeScopeClass(PsiCodeBlock.class, true);
361       variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH);
362       variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE);
363       registerVariant(variant);
364     }
365
366 // Catch/Finnaly completion
367     {
368       final ElementFilter position = new LeftNeighbour(new AndFilter(
369         new TextFilter("}"),
370         new ParentElementFilter(new AndFilter(
371           new LeftNeighbour(new NotFilter(new TextFilter(PsiKeyword.TRY))),
372           new OrFilter(
373             new ParentElementFilter(new ClassFilter(PsiTryStatement.class)),
374             new ParentElementFilter(new ClassFilter(PsiCatchSection.class)))
375           ))));
376
377       final CompletionVariant variant = new CompletionVariant(position);
378       variant.includeScopeClass(PsiCodeBlock.class, false);
379       variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH);
380       variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE);
381       registerVariant(variant);
382     }
383
384 // Completion for else expression
385 // completion
386     {
387       final ElementFilter position = new LeftNeighbour(
388         new OrFilter(
389           new AndFilter(new TextFilter("}"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 3)),
390           new AndFilter(new TextFilter(";"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 2))
391         ));
392       final CompletionVariant variant = new CompletionVariant(PsiMethod.class, position);
393       variant.addCompletion(PsiKeyword.ELSE);
394
395       registerVariant(variant);
396     }
397
398   }
399
400   private static void addPrimitiveTypes(CompletionVariant variant, TailType tailType){
401     variant.addCompletion(PRIMITIVE_TYPES, tailType);
402   }
403   
404   private static TailType getReturnTail(PsiElement position) {
405     PsiElement scope = position;
406     while(true){
407       if (scope instanceof PsiFile || scope instanceof PsiClassInitializer){
408         return TailType.NONE;
409       }
410
411       if (scope instanceof PsiMethod){
412         final PsiMethod method = (PsiMethod)scope;
413         if(method.isConstructor() || PsiType.VOID.equals(method.getReturnType())) {
414           return TailType.SEMICOLON;
415         }
416
417         return TailType.HUMBLE_SPACE_BEFORE_WORD;
418       }
419       scope = scope.getParent();
420     }
421   }
422
423   private static void addStatementKeywords(CompletionResultSet variant, PsiElement position) {
424     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH));
425     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH));
426     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailType.createSimpleTailType('{')));
427     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FOR), TailTypes.FOR_LPARENTH));
428     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.IF), TailTypes.IF_LPARENTH));
429     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.TRY), TailType.createSimpleTailType('{')));
430     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.THROW), TailType.INSERT_SPACE));
431     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE));
432     variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.SYNCHRONIZED), TailTypes.SYNCHRONIZED_LPARENTH));
433
434     if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_4)) {
435       variant.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.ASSERT), TailType.INSERT_SPACE));
436     }
437
438     TailType returnTail = getReturnTail(position);
439     LookupElement ret = createKeyword(position, PsiKeyword.RETURN);
440     if (returnTail != TailType.NONE) {
441       ret = new OverrideableSpace(ret, returnTail);
442     }
443     variant.addElement(ret);
444   }
445
446   @Override
447   public void fillCompletions(CompletionParameters parameters, final CompletionResultSet result) {
448     final PsiElement position = parameters.getPosition();
449     if (PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null) {
450       return;
451     }
452
453     PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class);
454     if (statement == null) {
455       statement = PsiTreeUtil.getParentOfType(position, PsiDeclarationStatement.class);
456     }
457     if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) {
458       if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).accepts(statement)) {
459         result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD));
460       }
461     }
462
463     if (isStatementPosition(position)) {
464       if (PsiTreeUtil.getParentOfType(position, PsiSwitchStatement.class, false, PsiMember.class) != null) {
465         result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE));
466         result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON));
467         if (START_SWITCH.accepts(position)) {
468           return;
469         }
470       }
471
472       addBreakContinue(result, position);
473       addStatementKeywords(result, position);
474     }
475
476     if (SUPER_OR_THIS_PATTERN.accepts(position)) {
477       if (!AFTER_DOT.accepts(position) || isInsideQualifierClass(position)) {
478         result.addElement(createKeyword(position, PsiKeyword.THIS));
479
480         final LookupItem superItem = (LookupItem)createKeyword(position, PsiKeyword.SUPER);
481         if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(position)) {
482           final PsiMethod method = PsiTreeUtil.getParentOfType(position, PsiMethod.class, false, PsiClass.class);
483           assert method != null;
484           final boolean hasParams = superConstructorHasParameters(method);
485           superItem.setInsertHandler(new ParenthesesInsertHandler<LookupElement>() {
486             @Override
487             protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) {
488               return hasParams;
489             }
490
491             @Override
492             public void handleInsert(InsertionContext context, LookupElement item) {
493               super.handleInsert(context, item);
494               TailType.insertChar(context.getEditor(), context.getTailOffset(), ';');
495             }
496           });
497         }
498
499         result.addElement(superItem);
500       }
501     }
502
503     if (EXPR_KEYWORDS.accepts(position)) {
504       result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE));
505       result.addElement(createKeyword(position, PsiKeyword.NULL));
506       result.addElement(createKeyword(position, PsiKeyword.TRUE));
507       result.addElement(createKeyword(position, PsiKeyword.FALSE));
508     }
509
510     if (INSIDE_PARAMETER_LIST.accepts(position) && !psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) && !AFTER_DOT.accepts(position)) {
511       result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD));
512     }
513
514     if (CLASS_START.isAcceptable(position, position) &&
515         PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) == null) {
516       for (String s : ModifierChooser.getKeywords(position)) {
517         result.addElement(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD));
518       }
519     }
520
521     addPrimitiveTypes(result, position);
522
523     if (isAfterTypeDot(position)) {
524       result.addElement(createKeyword(position, PsiKeyword.CLASS));
525     }
526
527     addUnfinishedMethodTypeParameters(position, result);
528
529     if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) &&
530         !BasicExpressionCompletionContributor.AFTER_DOT.accepts(position) &&
531         !(position.getParent() instanceof PsiLiteralExpression)) {
532       for (final ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
533         new JavaMembersGetter(info.getDefaultType(), position).addMembers(parameters, parameters.getInvocationCount() > 1, new Consumer<LookupElement>() {
534           @Override
535           public void consume(LookupElement element) {
536             result.addElement(element);
537           }
538         });
539       }
540     }
541   }
542
543   private static void addUnfinishedMethodTypeParameters(PsiElement position, CompletionResultSet result) {
544     final ProcessingContext context = new ProcessingContext();
545     if (psiElement().inside(
546       psiElement(PsiTypeElement.class).afterLeaf(
547         psiElement().withText(">").withParent(
548           psiElement(PsiTypeParameterList.class).withParent(PsiErrorElement.class).save("typeParameterList")))).accepts(position, context)) {
549       final PsiTypeParameterList list = (PsiTypeParameterList)context.get("typeParameterList");
550       PsiElement current = list.getParent().getParent();
551       if (current instanceof PsiField) {
552         current = current.getParent();
553       }
554       if (current instanceof PsiClass) {
555         for (PsiTypeParameter typeParameter : list.getTypeParameters()) {
556           result.addElement(new JavaPsiClassReferenceElement(typeParameter));
557         }
558       }
559     }
560   }
561
562   static boolean isAfterPrimitiveOrArrayType(PsiElement element) {
563     return psiElement().withParent(
564       psiReferenceExpression().withFirstChild(
565         psiElement(PsiClassObjectAccessExpression.class).withLastChild(
566           not(psiElement().withText(PsiKeyword.CLASS))))).accepts(element);
567   }
568
569   static boolean isAfterTypeDot(PsiElement position) {
570     if (INSIDE_PARAMETER_LIST.accepts(position) || position.getContainingFile() instanceof PsiJavaCodeReferenceCodeFragment) {
571       return false;
572     }
573
574     return psiElement().afterLeaf(psiElement().withText(".").afterLeaf(CLASS_REFERENCE)).accepts(position) ||
575            isAfterPrimitiveOrArrayType(position);
576   }
577
578   private static void addPrimitiveTypes(CompletionResultSet result, PsiElement position) {
579     boolean inCast = psiElement()
580       .afterLeaf(psiElement().withText("(").withParent(psiElement(PsiParenthesizedExpression.class, PsiTypeCastExpression.class)))
581       .accepts(position);
582
583     boolean declaration = DECLARATION_START.accepts(position);
584     if (START_FOR.accepts(position) ||
585         INSIDE_PARAMETER_LIST.accepts(position) && !AFTER_DOT.accepts(position) ||
586         VARIABLE_AFTER_FINAL.accepts(position) ||
587         inCast ||
588         declaration ||
589         isStatementPosition(position)) {
590       for (String primitiveType : PRIMITIVE_TYPES) {
591         LookupElement keyword = createKeyword(position, primitiveType);
592         result.addElement(inCast ? keyword : new OverrideableSpace(keyword, TailType.HUMBLE_SPACE_BEFORE_WORD));
593       }
594     }
595     if (declaration) {
596       result.addElement(new OverrideableSpace(createKeyword(position, PsiKeyword.VOID), TailType.HUMBLE_SPACE_BEFORE_WORD));
597     }
598   }
599
600   private static void addBreakContinue(CompletionResultSet result, PsiElement position) {
601     PsiLoopStatement loop = PsiTreeUtil.getParentOfType(position, PsiLoopStatement.class);
602
603     LookupElement br = createKeyword(position, PsiKeyword.BREAK);
604     LookupElement cont = createKeyword(position, PsiKeyword.CONTINUE);
605     TailType tailType;
606     if (psiElement().insideSequence(true, psiElement(PsiLabeledStatement.class),
607                                     or(psiElement(PsiFile.class), psiElement(PsiMethod.class),
608                                        psiElement(PsiClassInitializer.class))).accepts(position)) {
609       tailType = TailType.HUMBLE_SPACE_BEFORE_WORD;
610     }
611     else {
612       tailType = TailType.SEMICOLON;
613     }
614     br = TailTypeDecorator.withTail(br, tailType);
615     cont = TailTypeDecorator.withTail(cont, tailType);
616
617     if (loop != null && new InsideElementFilter(new ClassFilter(PsiStatement.class)).isAcceptable(position, loop)) {
618       result.addElement(br);
619       result.addElement(cont);
620     }
621     if (psiElement().inside(PsiSwitchStatement.class).accepts(position)) {
622       result.addElement(br);
623     }
624   }
625
626   private static boolean isStatementPosition(PsiElement position) {
627     if (PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) {
628       return false;
629     }
630
631     if (psiElement().withSuperParent(2, PsiConditionalExpression.class).accepts(position)) {
632       return false;
633     }
634
635     if (END_OF_BLOCK.isAcceptable(position, position) &&
636         PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) != null) {
637       return true;
638     }
639
640     if (psiElement().withParents(PsiReferenceExpression.class, PsiExpressionStatement.class, PsiIfStatement.class).accepts(position)) {
641       PsiElement stmt = position.getParent().getParent();
642       PsiIfStatement ifStatement = (PsiIfStatement)stmt.getParent();
643       if (ifStatement.getElseBranch() == stmt || ifStatement.getThenBranch() == stmt) {
644         return true;
645       }
646     }
647
648     return false;
649   }
650
651   private static LookupElement createKeyword(PsiElement position, String keyword) {
652     return BasicExpressionCompletionContributor.createKeywordLookupItem(position, keyword);
653   }
654
655   private static boolean isInsideQualifierClass(PsiElement position) {
656     if (position.getParent() instanceof PsiJavaCodeReferenceElement) {
657       final PsiElement qualifier = ((PsiJavaCodeReferenceElement)position.getParent()).getQualifier();
658       if (qualifier instanceof PsiJavaCodeReferenceElement) {
659         final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve();
660         if (qualifierClass instanceof PsiClass) {
661           PsiElement parent = position;
662           final PsiManager psiManager = position.getManager();
663           while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) {
664             if (psiManager.areElementsEquivalent(parent, qualifierClass)) {
665               return true;
666             }
667           }
668         }
669       }
670     }
671     return false;
672   }
673
674   private static boolean superConstructorHasParameters(PsiMethod method) {
675     final PsiClass psiClass = method.getContainingClass();
676     if (psiClass == null) {
677       return false;
678     }
679
680     final PsiClass superClass = psiClass.getSuperClass();
681     if (superClass != null) {
682       for (final PsiMethod psiMethod : superClass.getConstructors()) {
683         final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
684         if (resolveHelper.isAccessible(psiMethod, method, null) && psiMethod.getParameterList().getParameters().length > 0) {
685           return true;
686         }
687       }
688     }
689     return false;
690   }
691
692   private static class OverrideableSpace extends TailTypeDecorator<LookupElement> {
693     private final TailType myTail;
694
695     public OverrideableSpace(LookupElement keyword, TailType tail) {
696       super(keyword);
697       myTail = tail;
698     }
699
700     @Override
701     protected TailType computeTailType(InsertionContext context) {
702       return context.shouldAddCompletionChar() ? TailType.NONE : myTail;
703     }
704   }
705 }