2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.jetbrains.plugins.groovy.lang.completion;
19 import com.intellij.codeInsight.TailType;
20 import com.intellij.codeInsight.completion.InsertHandler;
21 import com.intellij.codeInsight.completion.InsertionContext;
22 import com.intellij.codeInsight.completion.util.MethodParenthesesHandler;
23 import com.intellij.codeInsight.lookup.Lookup;
24 import com.intellij.codeInsight.lookup.LookupElement;
25 import com.intellij.openapi.editor.CaretModel;
26 import com.intellij.openapi.editor.Document;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.psi.*;
29 import com.intellij.psi.util.PsiTreeUtil;
30 import org.jetbrains.annotations.NonNls;
31 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
32 import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
33 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
34 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
35 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
40 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
41 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
42 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
43 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
44 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
46 import java.util.Arrays;
51 public class GroovyInsertHandler implements InsertHandler<LookupElement> {
52 public static final GroovyInsertHandler INSTANCE = new GroovyInsertHandler();
53 private static final String CLOSURE_CLASS = "groovy.lang.Closure";
55 public void handleInsert(InsertionContext context, LookupElement item) {
56 @NonNls Object obj = item.getObject();
58 if (obj instanceof GroovyResolveResult) {
59 obj = ((GroovyResolveResult)obj).getElement();
62 if (obj instanceof PsiMethod) {
63 PsiMethod method = (PsiMethod)obj;
64 PsiParameter[] parameters = method.getParameterList().getParameters();
65 Editor editor = context.getEditor();
66 Document document = editor.getDocument();
67 if (context.getCompletionChar() == Lookup.REPLACE_SELECT_CHAR) {
68 handleOverwrite(editor.getCaretModel().getOffset(), document);
71 CaretModel caretModel = editor.getCaretModel();
72 int offset = context.getStartOffset() + method.getName().length();
73 PsiFile file = PsiDocumentManager.getInstance(method.getProject()).getPsiFile(document);
74 PsiElement elementAt = file.findElementAt(context.getStartOffset());
75 PsiElement parent = elementAt != null ? elementAt.getParent() : null;
76 if (parent instanceof GrReferenceExpression &&
77 ((GrReferenceExpression)parent).getDotTokenType() == GroovyElementTypes.mMEMBER_POINTER) {
81 if (parent instanceof GrAnnotationNameValuePair || parent.getParent() instanceof GrAnnotationNameValuePair) {
82 document.insertString(offset, " = ");
83 caretModel.moveToOffset(offset + 3);
87 if (PsiTreeUtil.getParentOfType(elementAt, GrImportStatement.class) != null) return;
89 if (parameters.length == 1) {
90 final PsiType type = parameters[0].getType();
91 if (type instanceof PsiClassType) {
92 final PsiClass psiClass = ((PsiClassType)type).resolve();
93 if (psiClass != null && CLOSURE_CLASS.equals(psiClass.getQualifiedName())) {
94 document.insertString(offset, " {}");
95 caretModel.moveToOffset(offset + 2);
101 PsiDocumentManager docManager = PsiDocumentManager.getInstance(method.getProject());
102 docManager.commitDocument(document);
103 PsiFile psiFile = docManager.getPsiFile(document);
104 if (method instanceof GrMethod &&
105 method.getParameterList().getParametersCount() > 0 &&
106 method.getContainingClass() instanceof GroovyScriptClass &&
107 isExpressionStatement(psiFile, context.getStartOffset())) {
110 if (isExpressionStatement(psiFile, context.getStartOffset()) &&
111 (PsiType.VOID.equals(PsiUtil.getSmartReturnType(method)) ||
112 method instanceof GrMethod && ((GrMethod)method).getReturnTypeElementGroovy() == null) &&
113 '(' != context.getCompletionChar() &&
114 parameters.length > 0) {
115 TailType.insertChar(editor, offset, ' ');
119 new MethodParenthesesHandler(method, true).handleInsert(context, item);
123 if (obj instanceof String && !"assert".equals(obj)) {
124 Editor editor = context.getEditor();
125 Document document = editor.getDocument();
126 if (context.getCompletionChar() == Lookup.REPLACE_SELECT_CHAR) {
127 handleOverwrite(editor.getCaretModel().getOffset(), document);
129 } else if (obj instanceof PsiClass) {
130 final PsiClass clazz = (PsiClass)obj;
131 Editor editor = context.getEditor();
132 Document document = editor.getDocument();
133 PsiFile file = PsiDocumentManager.getInstance(clazz.getProject()).getPsiFile(document);
134 PsiElement elementAt = file.findElementAt(context.getStartOffset());
135 CaretModel caretModel = editor.getCaretModel();
136 int offset = context.getStartOffset() + elementAt.getTextLength();
138 final String text = document.getText();
139 final PsiElement parent = elementAt.getParent();
140 if (parent instanceof GrCodeReferenceElement &&
141 parent.getParent() instanceof GrNewExpression &&
142 (offset == text.length() || !text.substring(offset).trim().startsWith("("))) {
143 document.insertString(offset, "()");
144 final PsiMethod[] methods = ResolveUtil.getAllClassConstructors(clazz, (GroovyPsiElement)parent, PsiSubstitutor.EMPTY);
145 for (PsiMethod method : methods) {
146 if (method.getParameterList().getParameters().length > 0) {
147 caretModel.moveToOffset(offset + 1);
151 caretModel.moveToOffset(offset + 2);
156 addTailType(item).processTail(context.getEditor(), context.getTailOffset());
159 private static boolean isExpressionStatement(PsiFile psiFile, int offset) {
160 PsiElement elementAt = psiFile.findElementAt(offset);
161 if (elementAt == null) return false;
162 GrExpression expr = PsiTreeUtil.getParentOfType(elementAt, GrExpression.class);
163 if (expr == null) return false;
164 return expr.getParent() instanceof GrControlFlowOwner;
167 private static void handleOverwrite(final int offset, final Document document) {
168 final CharSequence sequence = document.getCharsSequence();
170 while (i < sequence.length() && (Character.isJavaIdentifierPart(sequence.charAt(i)) || sequence.charAt(i) == '\'')) i++;
171 document.deleteString(offset, i);
174 private static TailType addTailType(LookupElement item) {
175 if ("default".equals(item.toString())) {
176 return TailType.CASE_COLON;
178 @NonNls String[] withSpace =
179 {"private", "public", "protected", "static", "transient", "abstract", "native", "volatile", "strictfp", "boolean", "byte", "char",
180 "short", "int", "float", "long", "double", "void", "new", "try", "while", "with", "switch", "for", "return", "throw", "throws",
181 "assert", "synchronized", "package", "class", "interface", "enum", "extends", "implements", "case", "catch", "finally", "else",
182 "instanceof", "import", "final", "def"};
183 if (Arrays.asList(withSpace).contains(item.toString())) {
184 return TailType.SPACE;
186 return TailType.NONE;