IDEA-79862 Java: auto-insert space after completing "extends" and "implements"
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / completion / GroovyCompletionData.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
17 package org.jetbrains.plugins.groovy.lang.completion;
18
19
20 import com.intellij.codeInsight.TailType;
21 import com.intellij.codeInsight.completion.CompletionParameters;
22 import com.intellij.codeInsight.completion.CompletionResultSet;
23 import com.intellij.codeInsight.completion.ModifierChooser;
24 import com.intellij.codeInsight.lookup.LookupElement;
25 import com.intellij.codeInsight.lookup.LookupElementBuilder;
26 import com.intellij.codeInsight.lookup.TailTypeDecorator;
27 import com.intellij.lang.ASTNode;
28 import com.intellij.patterns.PlatformPatterns;
29 import com.intellij.psi.*;
30 import com.intellij.psi.templateLanguages.OuterLanguageElement;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.util.ArrayUtil;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes;
35 import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocInlinedTag;
36 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
37 import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
38 import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
39 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
40 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
41 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.*;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
50 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
51 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
52 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
53 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
54 import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
55 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
56 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
57
58 import static com.intellij.patterns.PsiJavaPatterns.psiElement;
59 import static com.intellij.patterns.StandardPatterns.or;
60
61 /**
62  * @author ilyas
63  */
64 public class GroovyCompletionData {
65   public static final String[] BUILT_IN_TYPES = {"boolean", "byte", "char", "short", "int", "float", "long", "double", "void"};
66   public static final String[] MODIFIERS = new String[]{"private", "public", "protected", "transient", "abstract", "native", "volatile", "strictfp", "static"};
67   static final String[] INLINED_DOC_TAGS = {"code", "docRoot", "inheritDoc", "link", "linkplain", "literal"};
68   static final String[] DOC_TAGS = {"author", "deprecated", "exception", "param", "return", "see", "serial", "serialData",
69       "serialField", "since", "throws", "version"};
70
71   public static void addGroovyKeywords(CompletionParameters parameters, CompletionResultSet result) {
72     PsiElement position = parameters.getPosition();
73     PsiElement parent = position.getParent();
74     if (parent instanceof GrLiteral) {
75       return;
76     }
77     
78     if (!PlatformPatterns.psiElement().afterLeaf(".", ".&").accepts(position)) {
79       if (suggestPackage(position)) {
80         result.addElement(keyword(PsiKeyword.PACKAGE, TailType.INSERT_SPACE));
81       }
82       if (suggestImport(position)) {
83         result.addElement(keyword(PsiKeyword.IMPORT, TailType.INSERT_SPACE));
84       }
85
86       addTypeDefinitionKeywords(result, position);
87       for (String keyword : addExtendsImplements(position)) {
88         result.addElement(keyword(keyword, TailType.HUMBLE_SPACE_BEFORE_WORD));
89       }
90
91       registerControlCompletion(position, result);
92
93       if (parent instanceof GrExpression) {
94         addKeywords(result, false, PsiKeyword.TRUE, PsiKeyword.FALSE, PsiKeyword.NULL, PsiKeyword.SUPER, PsiKeyword.THIS);
95         result.addElement(keyword(PsiKeyword.NEW, TailType.INSERT_SPACE));
96         result.addElement(keyword("as", TailType.HUMBLE_SPACE_BEFORE_WORD));
97       }
98
99       if (isInfixOperatorPosition(position)) {
100         addKeywords(result, true, "in", PsiKeyword.INSTANCEOF);
101       } else if (suggestThrows(position)) {
102         result.addElement(keyword(PsiKeyword.THROWS, TailType.INSERT_SPACE));
103       } else if (suggestPrimitiveTypes(position)) {
104         boolean inCast = psiElement()
105           .afterLeaf(psiElement().withText("(").withParent(psiElement(GrParenthesizedExpression.class, GrTypeCastExpression.class)))
106           .accepts(position);
107
108         addKeywords(result, !inCast, BUILT_IN_TYPES);
109       }
110
111       if (psiElement(GrReferenceExpression.class).inside(or(psiElement(GrWhileStatement.class), psiElement(GrForStatement.class))).accepts(parent)) {
112         addKeywords(result, false, PsiKeyword.BREAK, PsiKeyword.CONTINUE);
113       }
114       else if (psiElement(GrReferenceExpression.class).inside(GrCaseSection.class).accepts(parent)) {
115         addKeywords(result, false, PsiKeyword.BREAK);
116       }
117
118       if (psiElement().withSuperParent(2, GrImportStatement.class).accepts(position)) {
119         if (psiElement().afterLeaf(PsiKeyword.IMPORT).accepts(position)) {
120           addKeywords(result, true, PsiKeyword.STATIC);
121         }
122       } else {
123         if (suggestModifiers(position)) {
124           addModifiers(position, result);
125         }
126         if (psiElement().afterLeaf(MODIFIERS).accepts(position) ||
127             GroovyCompletionUtil.isInTypeDefinitionBody(position) && GroovyCompletionUtil.isNewStatement(position, true)) {
128           addKeywords(result, true, PsiKeyword.SYNCHRONIZED);
129         }
130         if (suggestFinalDef(position) || psiElement().afterLeaf(
131           psiElement().withText("(").withParent(GrForStatement.class)).accepts(position)) {
132           addKeywords(result, true, PsiKeyword.FINAL, "def");
133         }
134       }
135     }
136   }
137
138   public static void addModifiers(PsiElement position, CompletionResultSet result) {
139     PsiClass scope = PsiTreeUtil.getParentOfType(position, PsiClass.class);
140     PsiModifierList modifierList = ModifierChooser.findModifierList(position);
141     addKeywords(result, true, ModifierChooser.addMemberModifiers(modifierList, scope != null && scope.isInterface()));
142   }
143
144   private static void addTypeDefinitionKeywords(CompletionResultSet result, PsiElement position) {
145     if (suggestClassInterfaceEnum(position)) {
146       addKeywords(result, true, PsiKeyword.CLASS, PsiKeyword.INTERFACE, PsiKeyword.ENUM);
147     }
148     if (afterAtInType(position)) {
149       result.addElement(keyword(PsiKeyword.INTERFACE, TailType.HUMBLE_SPACE_BEFORE_WORD));
150     }
151   }
152
153   @NotNull
154   private static String[] addExtendsImplements(PsiElement context) {
155     if (context.getParent() == null) {
156       return ArrayUtil.EMPTY_STRING_ARRAY;
157     }
158
159     PsiElement elem = context.getParent();
160     boolean ext = !(elem instanceof GrExtendsClause);
161     boolean impl = !(elem instanceof GrImplementsClause);
162
163     if (elem instanceof GrTypeDefinitionBody) { //inner class
164       elem = PsiUtil.skipWhitespaces(context.getPrevSibling(), false);
165     }
166     else {
167       elem = PsiUtil.skipWhitespaces(elem.getPrevSibling(), false);
168     }
169
170     ext &= elem instanceof GrInterfaceDefinition || elem instanceof GrClassDefinition;
171     impl &= elem instanceof GrEnumTypeDefinition || elem instanceof GrClassDefinition;
172     if (!ext && !impl) return ArrayUtil.EMPTY_STRING_ARRAY;
173
174     PsiElement[] children = elem.getChildren();
175     for (PsiElement child : children) {
176       ext &= !(child instanceof GrExtendsClause);
177       if (child instanceof GrImplementsClause || child instanceof GrTypeDefinitionBody) {
178         return ArrayUtil.EMPTY_STRING_ARRAY;
179       }
180     }
181     if (ext && impl) {
182       return new String[]{PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS};
183     }
184     
185     return new String[]{ext ? PsiKeyword.EXTENDS : PsiKeyword.IMPLEMENTS};
186   }
187
188   public static void addKeywords(CompletionResultSet result, boolean space, String... keywords) {
189     for (String s : keywords) {
190       result.addElement(keyword(s, space ? TailType.HUMBLE_SPACE_BEFORE_WORD : TailType.NONE));
191     }
192   }
193
194   private static LookupElement keyword(final String keyword, @NotNull TailType tail) {
195     LookupElementBuilder element = LookupElementBuilder.create(keyword).setBold();
196     return tail != TailType.NONE ? TailTypeDecorator.withTail(element, tail) : element;
197   }
198
199   private static void registerControlCompletion(PsiElement context, CompletionResultSet result) {
200     String[] controlKeywords = {"try", "while", "with", "switch", "for", "return", "throw", "assert", "synchronized",};
201
202     if (isControlStructure(context)) {
203       addKeywords(result, true, controlKeywords);
204     }
205     if (inCaseSection(context)) {
206       result.addElement(keyword("case", TailType.INSERT_SPACE));
207       result.addElement(keyword("default", TailType.CASE_COLON));
208     }
209     if (afterTry(context)) {
210       addKeywords(result, true, "catch", "finally");
211     }
212     if (afterIfOrElse(context)) {
213       addKeywords(result, true, "else");
214     }
215   }
216
217   public static void addGroovyDocKeywords(CompletionParameters parameters, CompletionResultSet result) {
218     PsiElement position = parameters.getPosition();
219     if (PlatformPatterns.psiElement(GroovyDocTokenTypes.mGDOC_TAG_NAME).andNot(PlatformPatterns.psiElement().afterLeaf(".")).accepts(position)) {
220       String[] tags = position.getParent() instanceof GrDocInlinedTag ? INLINED_DOC_TAGS : DOC_TAGS;
221       for (String docTag : tags) {
222         result.addElement(TailTypeDecorator.withTail(LookupElementBuilder.create(docTag), TailType.INSERT_SPACE));
223       }
224     }
225   }
226
227   private static boolean suggestPackage(PsiElement context) {
228     if (context.getParent() != null &&
229         !(context.getParent() instanceof PsiErrorElement) &&
230         context.getParent().getParent() instanceof GroovyFile &&
231         ((GroovyFile) context.getParent().getParent()).getPackageDefinition() == null) {
232       if (context.getParent() instanceof GrReferenceExpression) {
233         return true;
234       }
235       if (context.getParent() instanceof GrApplicationStatement &&
236           ((GrApplicationStatement) context.getParent()).getExpressionArguments()[0] instanceof GrReferenceExpression) {
237         return true;
238       }
239       return false;
240     }
241     if (context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement)) {
242       return true;
243     }
244
245     final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context);
246     if (leaf != null) {
247       PsiElement parent = leaf.getParent();
248       if (parent instanceof GroovyFile) {
249         GroovyFile groovyFile = (GroovyFile) parent;
250         if (groovyFile.getPackageDefinition() == null) {
251           return GroovyCompletionUtil.isNewStatement(context, false);
252         }
253       }
254     }
255
256     return false;
257   }
258
259   private static boolean suggestImport(PsiElement context) {
260     if (context.getParent() != null &&
261         !(context.getParent() instanceof PsiErrorElement) &&
262         GroovyCompletionUtil.isNewStatement(context, false) &&
263         context.getParent().getParent() instanceof GroovyFile) {
264       return true;
265     }
266     final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context);
267     if (leaf != null) {
268       PsiElement parent = leaf.getParent();
269       if (parent instanceof GroovyFile) {
270         return GroovyCompletionUtil.isNewStatement(context, false);
271       }
272     }
273     return context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement);
274   }
275
276   public static boolean suggestClassInterfaceEnum(PsiElement context) {
277     if (suggestThrows(context) || addExtendsImplements(context).length > 0) {
278       return false;
279     }
280
281     PsiElement parent = context.getParent();
282     if (parent instanceof GrTypeDefinitionBody) {
283       return true;
284     }
285
286     if (parent instanceof GrReferenceExpression) {
287       if (parent.getParent() instanceof GroovyFile) {
288         return true;
289       }
290       if ((parent.getParent() instanceof GrApplicationStatement ||
291            parent.getParent() instanceof GrCall) &&
292           parent.getParent().getParent() instanceof GroovyFile) {
293         return true;
294       }
295     }
296     final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context);
297     if (leaf != null) {
298       PsiElement prev = leaf;
299       prev = PsiImplUtil.realPrevious(prev);
300       if (prev instanceof GrModifierList &&
301           prev.getParent() != null &&
302           prev.getParent().getParent() instanceof GroovyFile) {
303         return true;
304       }
305
306       if (leaf.getParent() instanceof GroovyFile) {
307         return GroovyCompletionUtil.isNewStatement(context, false);
308       }
309     }
310
311     return false;
312   }
313
314   private static boolean afterAtInType(PsiElement context) {
315     PsiElement previous = PsiImplUtil.realPrevious(context.getPrevSibling());
316     if (previous != null &&
317         GroovyTokenTypes.mAT.equals(previous.getNode().getElementType()) &&
318         context.getParent() != null &&
319         context.getParent().getParent() instanceof GroovyFile) {
320       return true;
321     }
322     if (context.getParent() instanceof PsiErrorElement &&
323         context.getParent().getParent() instanceof GrAnnotation) {
324       return true;
325     }
326     return false;
327   }
328
329   private static boolean isControlStructure(PsiElement context) {
330     final int offset = context.getTextRange().getStartOffset();
331     PsiElement prevSibling = context.getPrevSibling();
332     if (context.getParent() instanceof GrReferenceElement && prevSibling != null && prevSibling.getNode() != null) {
333       ASTNode node = prevSibling.getNode();
334       return !TokenSets.DOTS.contains(node.getElementType());
335     }
336     if (GroovyCompletionUtil.isNewStatement(context, true)) {
337       final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(offset - 1, context);
338       if (leaf != null && leaf.getParent() instanceof GrStatementOwner) {
339         return true;
340       }
341     }
342
343     if (context.getParent() != null) {
344       PsiElement parent = context.getParent();
345
346       if (parent instanceof GrExpression &&
347           parent.getParent() instanceof GroovyFile) {
348         return true;
349       }
350
351       if (parent instanceof GrReferenceExpression) {
352
353         PsiElement superParent = parent.getParent();
354         if (superParent instanceof GrExpression) {
355           superParent = superParent.getParent();
356         }
357
358         if (superParent instanceof GrStatementOwner ||
359             superParent instanceof GrIfStatement ||
360             superParent instanceof GrForStatement ||
361             superParent instanceof GrWhileStatement) {
362           return true;
363         }
364       }
365
366       return false;
367     }
368
369     return false;
370   }
371
372   private static boolean inCaseSection(PsiElement context) {
373     if (context.getParent() instanceof GrReferenceExpression &&
374         context.getParent().getParent() instanceof GrCaseSection) {
375       return true;
376     }
377     final PsiElement left = GroovyCompletionUtil.nearestLeftSibling(context);
378     if (left != null && left.getParent() != null &&
379         left.getParent() instanceof GrSwitchStatement &&
380         left.getPrevSibling() != null &&
381         left.getPrevSibling().getNode() != null &&
382         GroovyTokenTypes.mLCURLY.equals(left.getPrevSibling().getNode().getElementType())) {
383       return true;
384     }
385     return false;
386   }
387
388   private static boolean afterTry(PsiElement context) {
389     if (context != null &&
390         GroovyCompletionUtil.nearestLeftSibling(context) instanceof GrTryCatchStatement) {
391       GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context);
392       if (tryStatement == null) return false;
393       if (tryStatement.getFinallyClause() == null) {
394         return true;
395       }
396     }
397     if (context != null &&
398         GroovyCompletionUtil.nearestLeftSibling(context) instanceof PsiErrorElement &&
399         GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling() instanceof GrTryCatchStatement) {
400       GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling();
401       if (tryStatement == null) return false;
402       if (tryStatement.getFinallyClause() == null) {
403         return true;
404       }
405     }
406     if (context != null &&
407         (context.getParent() instanceof GrReferenceExpression || context.getParent() instanceof PsiErrorElement) &&
408         GroovyCompletionUtil.nearestLeftSibling(context.getParent()) instanceof GrTryCatchStatement) {
409       GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context.getParent());
410       if (tryStatement == null) return false;
411       if (tryStatement.getFinallyClause() == null) {
412         return true;
413       }
414     }
415     return false;
416   }
417
418   private static boolean afterIfOrElse(PsiElement context) {
419     if (context.getParent() != null &&
420         GroovyCompletionUtil.nearestLeftSibling(context.getParent()) instanceof GrIfStatement) {
421       return true;
422     }
423     if (context.getParent() != null &&
424         GroovyCompletionUtil.nearestLeftSibling(context) != null &&
425         GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling() instanceof GrIfStatement) {
426       GrIfStatement statement = (GrIfStatement) GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling();
427       if (statement.getElseBranch() == null) {
428         return true;
429       }
430     }
431     if (context.getParent() != null &&
432         context.getParent().getParent() instanceof GrCommandArgumentList &&
433         context.getParent().getParent().getParent().getParent() instanceof GrIfStatement) {
434       GrIfStatement statement = (GrIfStatement) context.getParent().getParent().getParent().getParent();
435       if (statement.getElseBranch() == null) {
436         return true;
437       }
438     }
439     return false;
440   }
441
442   private static boolean suggestThrows(PsiElement context) {
443     PsiElement candidate = null;
444     if (GroovyCompletionUtil.isInTypeDefinitionBody(context)) {
445       PsiElement run = context;
446       while(!(run.getParent() instanceof GrTypeDefinitionBody)) {
447         run = run.getParent();
448         assert run != null;
449       }
450       candidate = PsiTreeUtil.getPrevSiblingOfType(run, GrMember.class);
451     }
452     else if (context.getParent() instanceof PsiErrorElement) {
453      candidate = context.getParent().getPrevSibling();
454     }
455
456     return candidate instanceof GrMethod && ((GrMethod) candidate).getBlock() == null;
457   }
458
459   private static boolean suggestPrimitiveTypes(PsiElement context) {
460     final PsiElement parent = context.getParent();
461     if (parent == null) return false;
462
463     PsiElement previous = PsiImplUtil.realPrevious(parent.getPrevSibling());
464     if (parent instanceof GrReferenceElement && parent.getParent() instanceof GrArgumentList) {
465       PsiElement prevSibling = context.getPrevSibling();
466       if (prevSibling != null && prevSibling.getNode() != null) {
467         if (!TokenSets.DOTS.contains(prevSibling.getNode().getElementType())) {
468           return true;
469         }
470       } else if (!(previous != null && GroovyTokenTypes.mAT.equals(previous.getNode().getElementType()))) {
471         return true;
472       }
473
474     }
475
476     if (previous != null && GroovyTokenTypes.mAT.equals(previous.getNode().getElementType())) {
477       return false;
478     }
479     if (GroovyCompletionUtil.asSimpleVariable(context) ||
480         GroovyCompletionUtil.asTypedMethod(context) ||
481         GroovyCompletionUtil.asVariableInBlock(context)) {
482       return true;
483     }
484     if ((parent instanceof GrParameter &&
485          ((GrParameter)parent).getTypeElementGroovy() == null) ||
486         parent instanceof GrReferenceElement &&
487         !(parent.getParent() instanceof GrImportStatement) &&
488         !(parent.getParent() instanceof GrPackageDefinition) &&
489         !(parent.getParent() instanceof GrArgumentList)) {
490       PsiElement prevSibling = context.getPrevSibling();
491       if (parent instanceof GrReferenceElement && prevSibling != null && prevSibling.getNode() != null) {
492         ASTNode node = prevSibling.getNode();
493         return !TokenSets.DOTS.contains(node.getElementType());
494       } else {
495         return true;
496       }
497     }
498     if (PsiImplUtil.realPrevious(parent.getPrevSibling()) instanceof GrModifierList) {
499       return true;
500     }
501     if (PsiImplUtil.realPrevious(context.getPrevSibling()) instanceof GrModifierList) {
502       return true;
503     }
504     return parent instanceof GrExpression &&
505            parent.getParent() instanceof GroovyFile &&
506            GroovyCompletionUtil.isNewStatement(context, false);
507   }
508
509   private static boolean isInfixOperatorPosition(PsiElement context) {
510     if (context.getParent() != null &&
511         context.getParent() instanceof GrReferenceExpression &&
512         context.getParent().getParent() != null &&
513         context.getParent().getParent() instanceof GrCommandArgumentList) {
514       return true;
515     }
516     if (GroovyCompletionUtil.nearestLeftSibling(context) instanceof PsiErrorElement &&
517         GroovyCompletionUtil.endsWithExpression(GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling())) {
518       return true;
519     }
520     if (context.getParent() instanceof PsiErrorElement) {
521       PsiElement leftSibling = GroovyCompletionUtil.nearestLeftSibling(context.getParent());
522       if (leftSibling != null && leftSibling.getLastChild() instanceof GrExpression) {
523         return true;
524       }
525     }
526     if (context.getParent() instanceof GrReferenceExpression &&
527         GroovyCompletionUtil.nearestLeftSibling(context.getParent()) instanceof PsiErrorElement &&
528         GroovyCompletionUtil.endsWithExpression(GroovyCompletionUtil.nearestLeftSibling(context.getParent()).getPrevSibling())) {
529       return true;
530     }
531     if (context.getParent() instanceof PsiErrorElement &&
532         GroovyCompletionUtil.endsWithExpression(GroovyCompletionUtil.nearestLeftSibling(context.getParent()))) {
533       return true;
534     }
535
536     return false;
537   }
538
539   private static boolean suggestModifiers(PsiElement context) {
540     if (GroovyCompletionUtil.asSimpleVariable(context) || GroovyCompletionUtil.asTypedMethod(context)) {
541       return true;
542     }
543     if (GroovyCompletionUtil.isFirstElementAfterPossibleModifiersInVariableDeclaration(context, false) &&
544         !psiElement().afterLeaf("def").accepts(context)) {
545       return true;
546     }
547
548     if (psiElement().afterLeaf(MODIFIERS).accepts(context) || psiElement().afterLeaf("synchronized").accepts(context)) {
549       return true;
550     }
551
552     final PsiElement contextParent = context.getParent();
553     if (contextParent instanceof GrReferenceElement && contextParent.getParent() instanceof GrTypeElement) {
554       PsiElement parent = contextParent.getParent().getParent();
555       if (parent instanceof GrVariableDeclaration &&
556           (parent.getParent() instanceof GrTypeDefinitionBody || parent.getParent() instanceof GroovyFile) || parent instanceof GrMethod) {
557         return true;
558       }
559     }
560     if (contextParent instanceof GrField) {
561       final GrVariable variable = (GrVariable)contextParent;
562       if (variable.getTypeElementGroovy() == null) {
563         return true;
564       }
565     }
566     if (contextParent instanceof GrExpression &&
567         contextParent.getParent() instanceof GroovyFile &&
568         GroovyCompletionUtil.isNewStatement(context, false)) {
569       return true;
570     }
571     if (context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement)) {
572       return true;
573     }
574     final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context);
575     if (leaf != null && GroovyCompletionUtil.isNewStatement(context, false)) {
576       PsiElement parent = leaf.getParent();
577       if (parent instanceof GroovyFile) {
578         return true;
579       }
580     }
581     return contextParent instanceof GrExpression &&
582            contextParent.getParent() instanceof GrApplicationStatement &&
583            contextParent.getParent().getParent() instanceof GroovyFile &&
584            GroovyCompletionUtil.isNewStatement(context, false);
585   }
586
587   public static boolean suggestFinalDef(PsiElement context) {
588     if (GroovyCompletionUtil.asSimpleVariable(context) ||
589         GroovyCompletionUtil.asTypedMethod(context) ||
590         GroovyCompletionUtil.asVariableInBlock(context)) {
591       return true;
592     }
593     if ((context.getParent() instanceof GrParameter &&
594         ((GrParameter) context.getParent()).getTypeElementGroovy() == null) ||
595         context.getParent() instanceof GrReferenceElement &&
596             !(context.getParent() instanceof GrReferenceExpression) &&
597             !(context.getParent().getParent() instanceof GrImportStatement) &&
598             !(context.getParent().getParent() instanceof GrPackageDefinition)) {
599       return true;
600     }
601     if (PsiImplUtil.realPrevious(context.getParent().getPrevSibling()) instanceof GrModifierList) {
602       return true;
603     }
604     if (PsiImplUtil.realPrevious(context.getPrevSibling()) instanceof GrModifierList) {
605       return true;
606     }
607     return context.getParent() instanceof GrExpression &&
608         context.getParent().getParent() instanceof GroovyFile &&
609         GroovyCompletionUtil.isNewStatement(context, false);
610   }
611 }