| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.jetbrains.plugins.groovy.lang.completion; |
| |
| |
| import com.intellij.codeInsight.TailType; |
| import com.intellij.codeInsight.TailTypes; |
| import com.intellij.codeInsight.completion.CompletionParameters; |
| import com.intellij.codeInsight.completion.CompletionResultSet; |
| import com.intellij.codeInsight.completion.JavaCompletionData; |
| import com.intellij.codeInsight.completion.ModifierChooser; |
| import com.intellij.codeInsight.lookup.LookupElement; |
| import com.intellij.codeInsight.lookup.LookupElementBuilder; |
| import com.intellij.codeInsight.lookup.TailTypeDecorator; |
| import com.intellij.lang.ASTNode; |
| import com.intellij.patterns.ElementPattern; |
| import com.intellij.patterns.PlatformPatterns; |
| import com.intellij.patterns.PsiJavaPatterns; |
| import com.intellij.patterns.StandardPatterns; |
| import com.intellij.psi.*; |
| import com.intellij.psi.templateLanguages.OuterLanguageElement; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; |
| import org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocInlinedTag; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; |
| import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair; |
| import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAnnotationMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| |
| /** |
| * @author ilyas |
| */ |
| public class GroovyCompletionData { |
| public static final String[] BUILT_IN_TYPES = {"boolean", "byte", "char", "short", "int", "float", "long", "double", "void"}; |
| public static final String[] MODIFIERS = new String[]{"private", "public", "protected", "transient", "abstract", "native", "volatile", "strictfp", "static"}; |
| public static final ElementPattern<PsiElement> IN_CAST_TYPE_ELEMENT = StandardPatterns.or( |
| PsiJavaPatterns.psiElement().afterLeaf(PsiJavaPatterns.psiElement().withText("(").withParent( |
| PsiJavaPatterns.psiElement(GrParenthesizedExpression.class, GrTypeCastExpression.class))), |
| PsiJavaPatterns |
| .psiElement().afterLeaf(PsiJavaPatterns.psiElement().withElementType(GroovyTokenTypes.kAS).withParent(GrSafeCastExpression.class)) |
| ); |
| static final String[] INLINED_DOC_TAGS = {"code", "docRoot", "inheritDoc", "link", "linkplain", "literal"}; |
| static final String[] DOC_TAGS = {"author", "deprecated", "exception", "param", "return", "see", "serial", "serialData", |
| "serialField", "since", "throws", "version"}; |
| |
| public static void addGroovyKeywords(CompletionParameters parameters, CompletionResultSet result) { |
| PsiElement position = parameters.getPosition(); |
| PsiElement parent = position.getParent(); |
| if (parent instanceof GrLiteral) { |
| return; |
| } |
| |
| final String[] extendsImplements = addExtendsImplements(position); |
| for (String keyword : extendsImplements) { |
| result.addElement(keyword(keyword, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| if (extendsImplements.length > 0) { |
| return; |
| } |
| |
| |
| if (parent instanceof GrExpression && parent.getParent() instanceof GrAnnotationNameValuePair) { |
| addKeywords(result, false, PsiKeyword.TRUE, PsiKeyword.FALSE, PsiKeyword.NULL); |
| return; |
| } |
| |
| if (afterAtInType(position)) { |
| result.addElement(keyword(PsiKeyword.INTERFACE, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| |
| if (!PlatformPatterns.psiElement().afterLeaf(".", ".&", "@", "*.", "?.").accepts(position)) { |
| if (afterAbstractMethod(position, false, true)) { |
| result.addElement(keyword(PsiKeyword.THROWS, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| if (afterAbstractMethod(position, false, false)) return; |
| } |
| |
| if (suggestPackage(position)) { |
| result.addElement(keyword(PsiKeyword.PACKAGE, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| if (suggestImport(position)) { |
| result.addElement(keyword(PsiKeyword.IMPORT, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| |
| addTypeDefinitionKeywords(result, position); |
| |
| if (isAfterAnnotationMethodIdentifier(position)) { |
| result.addElement(keyword(PsiKeyword.DEFAULT, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| |
| addExtendsForTypeParams(position, result); |
| |
| registerControlCompletion(position, result); |
| |
| if (parent instanceof GrExpression || isInfixOperatorPosition(position)) { |
| addKeywords(result, false, PsiKeyword.TRUE, PsiKeyword.FALSE, PsiKeyword.NULL, PsiKeyword.SUPER, PsiKeyword.THIS); |
| result.addElement(keyword(PsiKeyword.NEW, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| |
| if (isAfterForParameter(position)) { |
| result.addElement(keyword("in", TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| if (isInfixOperatorPosition(position)) { |
| addKeywords(result, true, "as", "in", PsiKeyword.INSTANCEOF); |
| } |
| if (suggestPrimitiveTypes(position)) { |
| final boolean addSpace = !IN_CAST_TYPE_ELEMENT.accepts(position) && !GroovySmartCompletionContributor.AFTER_NEW.accepts(position) && !isInExpression(position); |
| addKeywords(result, addSpace, BUILT_IN_TYPES); |
| } |
| |
| if (PsiJavaPatterns.psiElement(GrReferenceExpression.class).inside( |
| StandardPatterns.or(PsiJavaPatterns.psiElement(GrWhileStatement.class), PsiJavaPatterns.psiElement(GrForStatement.class))).accepts(parent)) { |
| addKeywords(result, false, PsiKeyword.BREAK, PsiKeyword.CONTINUE); |
| } |
| else if (PsiJavaPatterns.psiElement(GrReferenceExpression.class).inside(GrCaseSection.class).accepts(parent)) { |
| addKeywords(result, false, PsiKeyword.BREAK); |
| } |
| |
| if (PsiJavaPatterns.psiElement().withSuperParent(2, GrImportStatement.class).accepts(position)) { |
| if (PsiJavaPatterns.psiElement().afterLeaf(PsiKeyword.IMPORT).accepts(position)) { |
| addKeywords(result, true, PsiKeyword.STATIC); |
| } |
| } else { |
| if (suggestModifiers(position)) { |
| addModifiers(position, result); |
| } |
| if (PsiJavaPatterns.psiElement().afterLeaf(MODIFIERS).accepts(position) || |
| GroovyCompletionUtil.isInTypeDefinitionBody(position) && GroovyCompletionUtil.isNewStatement(position, true)) { |
| addKeywords(result, true, PsiKeyword.SYNCHRONIZED); |
| } |
| if (suggestFinalDef(position) || PsiJavaPatterns |
| .psiElement().afterLeaf(PsiJavaPatterns.psiElement().withText("(").withParent(GrForStatement.class)).accepts(position)) { |
| addKeywords(result, true, PsiKeyword.FINAL, "def"); |
| } |
| } |
| } |
| } |
| |
| private static boolean isAfterAnnotationMethodIdentifier(@NotNull PsiElement position) { |
| final PsiElement parent = position.getParent(); |
| |
| if (parent instanceof GrTypeDefinitionBody) { |
| final GrTypeDefinition containingClass = (GrTypeDefinition)parent.getParent(); |
| if (containingClass.isAnnotationType()) { |
| PsiElement sibling = PsiUtil.skipWhitespacesAndComments(position.getPrevSibling(), false); |
| if (sibling instanceof PsiErrorElement) { |
| sibling = PsiUtil.skipWhitespacesAndComments(sibling.getPrevSibling(), false); |
| } |
| return sibling instanceof GrAnnotationMethod && ((GrAnnotationMethod)sibling).getDefaultValue() == null; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * checks whether promitive type used in expression |
| */ |
| private static boolean isInExpression(PsiElement position) { |
| final PsiElement actual = position.getParent(); |
| final PsiElement parent = actual.getParent(); |
| return parent instanceof GrArgumentList || parent instanceof GrBinaryExpression; |
| } |
| |
| private static void addExtendsForTypeParams(PsiElement position, CompletionResultSet result) { |
| if (GroovyCompletionUtil.isWildcardCompletion(position)) { |
| addKeywords(result, true, PsiKeyword.EXTENDS, PsiKeyword.SUPER); |
| } |
| } |
| |
| private static boolean isAfterForParameter(PsiElement position) { |
| ElementPattern<PsiElement> forParameter = |
| PsiJavaPatterns.psiElement().withParents(GrParameter.class, GrTraditionalForClause.class, GrForStatement.class); |
| return PsiJavaPatterns.psiElement().withParent(GrReferenceExpression.class).afterLeaf(forParameter).accepts(position) || |
| forParameter.accepts(position) && PsiJavaPatterns.psiElement().afterLeaf(PsiJavaPatterns.psiElement(GroovyTokenTypes.mIDENT)).accepts(position); |
| } |
| |
| public static void addModifiers(PsiElement position, CompletionResultSet result) { |
| PsiClass scope = PsiTreeUtil.getParentOfType(position, PsiClass.class); |
| PsiModifierList modifierList = ModifierChooser.findModifierList(position); |
| addKeywords(result, true, ModifierChooser.addMemberModifiers(modifierList, scope != null && scope.isInterface())); |
| } |
| |
| private static void addTypeDefinitionKeywords(CompletionResultSet result, PsiElement position) { |
| if (suggestClassInterfaceEnum(position)) { |
| addKeywords(result, true, PsiKeyword.CLASS, PsiKeyword.INTERFACE, PsiKeyword.ENUM, GroovyTokenTypes.kTRAIT.toString()); |
| } |
| } |
| |
| @NotNull |
| private static String[] addExtendsImplements(PsiElement context) { |
| if (context.getParent() == null) { |
| return ArrayUtil.EMPTY_STRING_ARRAY; |
| } |
| |
| PsiElement elem = context.getParent(); |
| boolean ext = !(elem instanceof GrExtendsClause); |
| boolean impl = !(elem instanceof GrImplementsClause); |
| |
| if (elem instanceof GrTypeDefinitionBody) { //inner class |
| elem = PsiUtil.skipWhitespacesAndComments(context.getPrevSibling(), false); |
| } |
| else { |
| if (elem instanceof GrReferenceExpression && PsiUtil.skipWhitespacesAndComments(elem.getPrevSibling(), false) instanceof GrTypeDefinition) { |
| elem = PsiUtil.skipWhitespacesAndComments(elem.getPrevSibling(), false); |
| } |
| else if (elem.getParent() != null) { |
| elem = PsiUtil.skipWhitespacesAndComments(elem.getParent().getPrevSibling(), false); |
| } |
| } |
| |
| ext &= elem instanceof GrInterfaceDefinition || elem instanceof GrClassDefinition || elem instanceof GrTraitTypeDefinition; |
| impl &= elem instanceof GrEnumTypeDefinition || elem instanceof GrClassDefinition || elem instanceof GrTraitTypeDefinition; |
| if (!ext && !impl) return ArrayUtil.EMPTY_STRING_ARRAY; |
| |
| PsiElement[] children = elem.getChildren(); |
| for (PsiElement child : children) { |
| ext &= !(child instanceof GrExtendsClause && ((GrExtendsClause)child).getKeyword() != null); |
| if (child instanceof GrImplementsClause && ((GrImplementsClause)child).getKeyword() != null || child instanceof GrTypeDefinitionBody) { |
| return ArrayUtil.EMPTY_STRING_ARRAY; |
| } |
| } |
| if (ext && impl) { |
| return new String[]{PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS}; |
| } |
| |
| return new String[]{ext ? PsiKeyword.EXTENDS : PsiKeyword.IMPLEMENTS}; |
| } |
| |
| public static void addKeywords(CompletionResultSet result, boolean space, String... keywords) { |
| for (String s : keywords) { |
| result.addElement(keyword(s, space ? TailType.HUMBLE_SPACE_BEFORE_WORD : TailType.NONE)); |
| } |
| } |
| |
| private static LookupElement keyword(final String keyword, @NotNull TailType tail) { |
| LookupElementBuilder element = LookupElementBuilder.create(keyword).bold(); |
| return tail != TailType.NONE ? new JavaCompletionData.OverrideableSpace(element, tail) : element; |
| } |
| |
| private static void registerControlCompletion(PsiElement context, CompletionResultSet result) { |
| if (isControlStructure(context)) { |
| result.addElement(keyword(PsiKeyword.TRY, TailTypes.TRY_LBRACE)); |
| result.addElement(keyword(PsiKeyword.WHILE, TailTypes.WHILE_LPARENTH)); |
| result.addElement(keyword(PsiKeyword.SWITCH, TailTypes.SWITCH_LPARENTH)); |
| result.addElement(keyword(PsiKeyword.FOR, TailTypes.FOR_LPARENTH)); |
| result.addElement(keyword(PsiKeyword.THROW, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| result.addElement(keyword(PsiKeyword.ASSERT, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| result.addElement(keyword(PsiKeyword.SYNCHRONIZED, TailTypes.SYNCHRONIZED_LPARENTH)); |
| result.addElement(keyword(PsiKeyword.RETURN, hasReturnValue(context) ? TailType.HUMBLE_SPACE_BEFORE_WORD : TailType.NONE)); |
| } |
| if (inCaseSection(context)) { |
| result.addElement(keyword("case", TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| result.addElement(keyword("default", TailType.CASE_COLON)); |
| } |
| if (afterTry(context)) { |
| result.addElement(keyword(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH)); |
| result.addElement(keyword(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE)); |
| } |
| if (afterIfOrElse(context)) { |
| result.addElement(keyword(PsiKeyword.ELSE, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| |
| if (isCommandCallWithOneArg(context)) { |
| result.addElement(keyword(PsiKeyword.ASSERT, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| if (hasReturnValue(context)) { |
| result.addElement(keyword(PsiKeyword.RETURN, TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| } |
| } |
| |
| private static boolean isCommandCallWithOneArg(PsiElement context) { |
| return context.getParent() instanceof GrReferenceExpression && |
| context.getParent().getParent() instanceof GrApplicationStatement && |
| ((GrApplicationStatement)context.getParent().getParent()).getExpressionArguments().length == 1 && |
| !PsiImplUtil.hasNamedArguments(((GrApplicationStatement)context.getParent().getParent()).getArgumentList()); |
| } |
| |
| private static boolean hasReturnValue(PsiElement context) { |
| GrControlFlowOwner flowOwner = ControlFlowUtils.findControlFlowOwner(context); |
| if (flowOwner instanceof GrClosableBlock) return true; |
| if (flowOwner instanceof GroovyFile) return true; |
| if (flowOwner == null) return true; |
| |
| PsiElement parent = flowOwner.getParent(); |
| if (parent instanceof GrMethod) { |
| return ((GrMethod)parent).getReturnType() != PsiType.VOID; |
| } |
| else if (parent instanceof GrClassInitializer) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| public static void addGroovyDocKeywords(CompletionParameters parameters, CompletionResultSet result) { |
| PsiElement position = parameters.getPosition(); |
| if (PlatformPatterns.psiElement(GroovyDocTokenTypes.mGDOC_TAG_NAME).andNot(PlatformPatterns.psiElement().afterLeaf(".")).accepts( |
| position)) { |
| String[] tags = position.getParent() instanceof GrDocInlinedTag ? INLINED_DOC_TAGS : DOC_TAGS; |
| for (String docTag : tags) { |
| result.addElement(TailTypeDecorator.withTail(LookupElementBuilder.create(docTag), TailType.HUMBLE_SPACE_BEFORE_WORD)); |
| } |
| } |
| } |
| |
| private static boolean suggestPackage(PsiElement context) { |
| if (context.getParent() != null && |
| !(context.getParent() instanceof PsiErrorElement) && |
| context.getParent().getParent() instanceof GroovyFile && |
| ((GroovyFile) context.getParent().getParent()).getPackageDefinition() == null) { |
| if (context.getParent() instanceof GrReferenceExpression) { |
| return true; |
| } |
| if (context.getParent() instanceof GrApplicationStatement && |
| ((GrApplicationStatement) context.getParent()).getExpressionArguments()[0] instanceof GrReferenceExpression) { |
| return true; |
| } |
| return false; |
| } |
| if (context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement)) { |
| return true; |
| } |
| |
| final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context); |
| if (leaf != null) { |
| PsiElement parent = leaf.getParent(); |
| if (parent instanceof GroovyFile) { |
| GroovyFile groovyFile = (GroovyFile) parent; |
| if (groovyFile.getPackageDefinition() == null) { |
| return GroovyCompletionUtil.isNewStatement(context, false); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| private static boolean suggestImport(PsiElement context) { |
| if (context.getParent() != null && |
| !(context.getParent() instanceof PsiErrorElement) && |
| GroovyCompletionUtil.isNewStatement(context, false) && |
| context.getParent().getParent() instanceof GroovyFile) { |
| return true; |
| } |
| final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context); |
| if (leaf != null) { |
| PsiElement parent = leaf.getParent(); |
| if (parent instanceof GroovyFile) { |
| return GroovyCompletionUtil.isNewStatement(context, false); |
| } |
| } |
| return context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement); |
| } |
| |
| public static boolean suggestClassInterfaceEnum(PsiElement context) { |
| PsiElement nextNonSpace = PsiUtil.getNextNonSpace(context); |
| if (nextNonSpace instanceof PsiErrorElement) nextNonSpace = PsiUtil.getNextNonSpace(nextNonSpace); |
| if (afterAbstractMethod(context, true, false) && nextNonSpace != null && nextNonSpace.getText().startsWith("{") || addExtendsImplements(context).length > 0) { |
| return false; |
| } |
| |
| PsiElement parent = context.getParent(); |
| if (parent instanceof GrTypeDefinitionBody) { |
| return true; |
| } |
| |
| if (parent instanceof GrReferenceExpression) { |
| if (parent.getParent() instanceof GroovyFile) { |
| return true; |
| } |
| if ((parent.getParent() instanceof GrApplicationStatement || |
| parent.getParent() instanceof GrCall) && |
| parent.getParent().getParent() instanceof GroovyFile) { |
| return true; |
| } |
| } |
| |
| /* |
| @Anno |
| cl<caret> |
| */ |
| if (parent instanceof GrVariable && context == ((GrVariable)parent).getNameIdentifierGroovy()) { |
| final PsiElement decl = parent.getParent(); |
| if (decl instanceof GrVariableDeclaration && |
| !((GrVariableDeclaration)decl).isTuple() && |
| ((GrVariableDeclaration)decl).getTypeElementGroovy() == null && |
| (decl.getParent() instanceof GrTypeDefinitionBody || decl.getParent() instanceof GroovyFile)) { |
| return true; |
| } |
| } |
| |
| final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(context.getTextRange().getStartOffset() - 1, context); |
| if (leaf != null) { |
| PsiElement prev = leaf; |
| prev = PsiImplUtil.realPrevious(prev); |
| if (prev instanceof GrModifierList && |
| prev.getParent() != null && |
| prev.getParent().getParent() instanceof GroovyFile) { |
| return true; |
| } |
| |
| if (leaf.getParent() instanceof GroovyFile) { |
| return GroovyCompletionUtil.isNewStatement(context, false); |
| } |
| } |
| |
| return false; |
| } |
| |
| private static boolean afterAtInType(PsiElement context) { |
| PsiElement previous = PsiImplUtil.realPrevious(PsiTreeUtil.prevLeaf(context)); |
| if (previous != null && |
| GroovyTokenTypes.mAT.equals(previous.getNode().getElementType()) && |
| (context.getParent() != null && context.getParent().getParent() instanceof GroovyFile || |
| context.getParent() instanceof GrCodeReferenceElement && context.getParent().getParent() instanceof GrAnnotation)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean isControlStructure(PsiElement context) { |
| final int offset = context.getTextRange().getStartOffset(); |
| PsiElement prevSibling = context.getPrevSibling(); |
| if (context.getParent() instanceof GrReferenceElement && prevSibling != null && prevSibling.getNode() != null) { |
| ASTNode node = prevSibling.getNode(); |
| return !TokenSets.DOTS.contains(node.getElementType()); |
| } |
| if (GroovyCompletionUtil.isNewStatement(context, true)) { |
| final PsiElement leaf = GroovyCompletionUtil.getLeafByOffset(offset - 1, context); |
| if (leaf != null && (leaf.getParent() instanceof GrStatementOwner || leaf.getParent() instanceof GrLabeledStatement)) { |
| return true; |
| } |
| } |
| |
| if (context.getParent() != null) { |
| PsiElement parent = context.getParent(); |
| |
| if (parent instanceof GrExpression && |
| parent.getParent() instanceof GroovyFile) { |
| return true; |
| } |
| |
| if (parent instanceof GrReferenceExpression) { |
| |
| PsiElement superParent = parent.getParent(); |
| |
| if (superParent instanceof GrStatementOwner || |
| superParent instanceof GrLabeledStatement || |
| superParent instanceof GrControlStatement || |
| superParent instanceof GrMethodCall) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| return false; |
| } |
| |
| private static boolean inCaseSection(PsiElement context) { |
| if (context.getParent() instanceof GrReferenceExpression && |
| context.getParent().getParent() instanceof GrCaseSection) { |
| return true; |
| } |
| |
| final GrSwitchStatement switchStatement = PsiTreeUtil.getParentOfType(context, GrSwitchStatement.class, true, GrCodeBlock.class); |
| if (switchStatement == null) return false; |
| |
| final GrExpression condition = switchStatement.getCondition(); |
| return condition == null || !PsiTreeUtil.isAncestor(condition, context, false); |
| } |
| |
| private static boolean afterTry(PsiElement context) { |
| if (context != null && |
| GroovyCompletionUtil.nearestLeftSibling(context) instanceof GrTryCatchStatement) { |
| GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context); |
| if (tryStatement == null) return false; |
| if (tryStatement.getFinallyClause() == null) { |
| return true; |
| } |
| } |
| if (context != null && |
| GroovyCompletionUtil.nearestLeftSibling(context) instanceof PsiErrorElement && |
| GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling() instanceof GrTryCatchStatement) { |
| GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling(); |
| if (tryStatement == null) return false; |
| if (tryStatement.getFinallyClause() == null) { |
| return true; |
| } |
| } |
| if (context != null && |
| (context.getParent() instanceof GrReferenceExpression || context.getParent() instanceof PsiErrorElement) && |
| GroovyCompletionUtil.nearestLeftSibling(context.getParent()) instanceof GrTryCatchStatement) { |
| GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context.getParent()); |
| if (tryStatement == null) return false; |
| if (tryStatement.getFinallyClause() == null) { |
| return true; |
| } |
| } |
| |
| if (context != null && |
| (context.getParent() instanceof GrReferenceExpression) && |
| (context.getParent().getParent() instanceof GrMethodCall) && |
| GroovyCompletionUtil.nearestLeftSibling(context.getParent().getParent()) instanceof GrTryCatchStatement) { |
| GrTryCatchStatement tryStatement = (GrTryCatchStatement) GroovyCompletionUtil.nearestLeftSibling(context.getParent().getParent()); |
| if (tryStatement == null) return false; |
| if (tryStatement.getFinallyClause() == null) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private static boolean afterIfOrElse(PsiElement context) { |
| if (context.getParent() != null && |
| GroovyCompletionUtil.nearestLeftSibling(context.getParent()) instanceof GrIfStatement) { |
| return true; |
| } |
| |
| if (context.getParent() != null && |
| GroovyCompletionUtil.nearestLeftSibling(context.getParent()) instanceof PsiErrorElement && |
| GroovyCompletionUtil.nearestLeftSibling(GroovyCompletionUtil.nearestLeftSibling(context.getParent())) instanceof GrIfStatement) { |
| return true; |
| } |
| |
| if (context.getParent() != null && |
| GroovyCompletionUtil.nearestLeftSibling(context) != null && |
| GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling() instanceof GrIfStatement) { |
| GrIfStatement statement = (GrIfStatement) GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling(); |
| if (statement.getElseBranch() == null) { |
| return true; |
| } |
| } |
| if (context.getParent() != null && |
| context.getParent().getParent() instanceof GrCommandArgumentList && |
| context.getParent().getParent().getParent().getParent() instanceof GrIfStatement) { |
| GrIfStatement statement = (GrIfStatement) context.getParent().getParent().getParent().getParent(); |
| if (statement.getElseBranch() == null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean afterAbstractMethod(PsiElement context, boolean acceptAnnotationMethods, boolean skipNLs) { |
| PsiElement candidate; |
| if (GroovyCompletionUtil.isInTypeDefinitionBody(context)) { |
| PsiElement run = context; |
| while(!(run.getParent() instanceof GrTypeDefinitionBody)) { |
| run = run.getParent(); |
| assert run != null; |
| } |
| candidate = PsiUtil.skipWhitespacesAndComments(run.getPrevSibling(), false, skipNLs); |
| } |
| else { |
| candidate = PsiUtil.skipWhitespacesAndComments(PsiTreeUtil.prevLeaf(context), false); |
| } |
| if (candidate instanceof PsiErrorElement) candidate = candidate.getPrevSibling(); |
| |
| return candidate instanceof GrMethod && |
| ((GrMethod)candidate).getBlock() == null && |
| (acceptAnnotationMethods || !(candidate instanceof GrAnnotationMethod)); |
| } |
| |
| private static boolean suggestPrimitiveTypes(PsiElement context) { |
| if (isInfixOperatorPosition(context)) return false; |
| if (isAfterForParameter(context)) return false; |
| |
| final PsiElement parent = context.getParent(); |
| if (parent == null) return false; |
| |
| PsiElement previous = PsiImplUtil.realPrevious(parent.getPrevSibling()); |
| if (parent instanceof GrReferenceElement && parent.getParent() instanceof GrArgumentList) { |
| PsiElement prevSibling = context.getPrevSibling(); |
| if (prevSibling != null && prevSibling.getNode() != null) { |
| if (!TokenSets.DOTS.contains(prevSibling.getNode().getElementType())) { |
| return true; |
| } |
| } else if (!(previous != null && GroovyTokenTypes.mAT.equals(previous.getNode().getElementType()))) { |
| return true; |
| } |
| |
| } |
| |
| if (GroovyCompletionUtil.isTupleVarNameWithoutTypeDeclared(context)) return true; |
| |
| if (previous != null && GroovyTokenTypes.mAT.equals(previous.getNode().getElementType())) { |
| return false; |
| } |
| if (GroovyCompletionUtil.asSimpleVariable(context) || |
| GroovyCompletionUtil.asTypedMethod(context) || |
| GroovyCompletionUtil.asVariableInBlock(context) || |
| asVariableAfterModifiers(context)) { |
| return true; |
| } |
| if ((parent instanceof GrParameter && |
| ((GrParameter)parent).getTypeElementGroovy() == null) || |
| parent instanceof GrReferenceElement && |
| !(parent.getParent() instanceof GrImportStatement) && |
| !(parent.getParent() instanceof GrPackageDefinition) && |
| !(parent.getParent() instanceof GrArgumentList)) { |
| PsiElement prevSibling = context.getPrevSibling(); |
| if (parent instanceof GrReferenceElement && prevSibling != null && prevSibling.getNode() != null) { |
| ASTNode node = prevSibling.getNode(); |
| return !TokenSets.DOTS.contains(node.getElementType()); |
| } else { |
| return true; |
| } |
| } |
| if (PsiImplUtil.realPrevious(parent.getPrevSibling()) instanceof GrModifierList) { |
| return true; |
| } |
| if (PsiImplUtil.realPrevious(context.getPrevSibling()) instanceof GrModifierList) { |
| return true; |
| } |
| return parent instanceof GrExpression && |
| parent.getParent() instanceof GroovyFile && |
| GroovyCompletionUtil.isNewStatement(context, false); |
| } |
| |
| private static boolean asVariableAfterModifiers(PsiElement context) { |
| final PsiElement parent = context.getParent(); |
| if (parent instanceof GrVariable && context == ((GrVariable)parent).getNameIdentifierGroovy()) { |
| final PsiElement decl = parent.getParent(); |
| if (decl instanceof GrVariableDeclaration && |
| !((GrVariableDeclaration)decl).isTuple() && |
| ((GrVariableDeclaration)decl).getTypeElementGroovy() == null) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private static boolean isInfixOperatorPosition(PsiElement context) { |
| if (context.getParent() != null && |
| context.getParent() instanceof GrReferenceExpression && |
| context.getParent().getParent() != null && |
| context.getParent().getParent() instanceof GrCommandArgumentList) { |
| return true; |
| } |
| if (GroovyCompletionUtil.nearestLeftSibling(context) instanceof PsiErrorElement && |
| GroovyCompletionUtil.endsWithExpression(GroovyCompletionUtil.nearestLeftSibling(context).getPrevSibling())) { |
| return true; |
| } |
| if (context.getParent() instanceof GrReferenceExpression && |
| GroovyCompletionUtil.nearestLeftLeaf(context) instanceof PsiErrorElement && |
| GroovyCompletionUtil.endsWithExpression(GroovyCompletionUtil.nearestLeftLeaf(context).getPrevSibling())) { |
| return true; |
| } |
| if (context.getParent() instanceof PsiErrorElement && |
| GroovyCompletionUtil.endsWithExpression(GroovyCompletionUtil.nearestLeftSibling(context.getParent()))) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static boolean suggestModifiers(PsiElement context) { |
| if (GroovyCompletionUtil.asSimpleVariable(context) || |
| GroovyCompletionUtil.asTypedMethod(context) || |
| GroovyCompletionUtil.isNewStatementInScript(context)) { |
| return true; |
| } |
| if (GroovyCompletionUtil.isFirstElementAfterPossibleModifiersInVariableDeclaration(context, false) && |
| !PsiJavaPatterns.psiElement().afterLeaf("def").accepts(context)) { |
| return true; |
| } |
| |
| if (PsiJavaPatterns.psiElement().afterLeaf(MODIFIERS).accepts(context) || PsiJavaPatterns.psiElement().afterLeaf("synchronized").accepts(context)) { |
| return true; |
| } |
| |
| final PsiElement contextParent = context.getParent(); |
| if (contextParent instanceof GrReferenceElement && contextParent.getParent() instanceof GrTypeElement) { |
| PsiElement parent = contextParent.getParent().getParent(); |
| if (parent instanceof GrVariableDeclaration && |
| (parent.getParent() instanceof GrTypeDefinitionBody || parent.getParent() instanceof GroovyFile) || parent instanceof GrMethod) { |
| return true; |
| } |
| } |
| if (contextParent instanceof GrField) { |
| final GrVariable variable = (GrVariable)contextParent; |
| if (variable.getTypeElementGroovy() == null) { |
| return true; |
| } |
| } |
| if (contextParent instanceof GrExpression && |
| contextParent.getParent() instanceof GroovyFile && |
| GroovyCompletionUtil.isNewStatement(context, false)) { |
| return true; |
| } |
| if (context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement)) { |
| return true; |
| } |
| return contextParent instanceof GrExpression && |
| contextParent.getParent() instanceof GrApplicationStatement && |
| contextParent.getParent().getParent() instanceof GroovyFile && |
| GroovyCompletionUtil.isNewStatement(context, false); |
| } |
| |
| public static boolean suggestFinalDef(PsiElement context) { |
| if (GroovyCompletionUtil.asSimpleVariable(context) || |
| GroovyCompletionUtil.asTypedMethod(context) || |
| GroovyCompletionUtil.asVariableInBlock(context) || |
| GroovyCompletionUtil.isNewStatementInScript(context) && !GroovyCompletionUtil.isReferenceElementInNewExpr(context) || |
| GroovyCompletionUtil.isTypelessParameter(context) || |
| GroovyCompletionUtil.isCodeReferenceElementApplicableToModifierCompletion(context)) { |
| return true; |
| } |
| if (PsiImplUtil.realPrevious(context.getParent().getPrevSibling()) instanceof GrModifierList) { |
| return true; |
| } |
| if (PsiImplUtil.realPrevious(context.getPrevSibling()) instanceof GrModifierList) { |
| return true; |
| } |
| return context.getParent() instanceof GrExpression && |
| context.getParent().getParent() instanceof GroovyFile && |
| GroovyCompletionUtil.isNewStatement(context, false); |
| } |
| } |