| /* |
| * 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.AutoPopupController; |
| import com.intellij.codeInsight.TailType; |
| import com.intellij.codeInsight.completion.InsertHandler; |
| import com.intellij.codeInsight.completion.InsertionContext; |
| import com.intellij.codeInsight.completion.util.MethodParenthesesHandler; |
| import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; |
| import com.intellij.codeInsight.lookup.Lookup; |
| import com.intellij.codeInsight.lookup.LookupElement; |
| import com.intellij.openapi.editor.CaretModel; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsManager; |
| import com.intellij.psi.codeStyle.CommonCodeStyleSettings; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.text.CharArrayUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.plugins.groovy.codeStyle.GroovyCodeStyleSettings; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; |
| |
| /** |
| * @author ven |
| */ |
| public class GroovyInsertHandler implements InsertHandler<LookupElement> { |
| public static final GroovyInsertHandler INSTANCE = new GroovyInsertHandler(); |
| |
| @Override |
| public void handleInsert(InsertionContext context, LookupElement item) { |
| @NonNls Object obj = item.getObject(); |
| |
| PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; |
| if (obj instanceof GroovyResolveResult) { |
| substitutor = ((GroovyResolveResult)obj).getSubstitutor(); |
| obj = ((GroovyResolveResult)obj).getElement(); |
| } |
| |
| if (obj instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)obj; |
| PsiParameter[] parameters = method.getParameterList().getParameters(); |
| Editor editor = context.getEditor(); |
| Document document = editor.getDocument(); |
| if (context.getCompletionChar() == Lookup.REPLACE_SELECT_CHAR) { |
| handleOverwrite(editor.getCaretModel().getOffset(), document); |
| } |
| |
| CaretModel caretModel = editor.getCaretModel(); |
| int offset = context.getTailOffset(); |
| PsiFile file = context.getFile(); |
| PsiElement elementAt = file.findElementAt(context.getStartOffset()); |
| assert elementAt != null; |
| PsiElement parent = elementAt.getParent(); |
| if (parent instanceof GrReferenceExpression && ((GrReferenceExpression)parent).getDotTokenType() == GroovyTokenTypes.mMEMBER_POINTER) { |
| return; |
| } |
| |
| CharSequence charsSequence = document.getCharsSequence(); |
| if (isAnnotationNameValuePair(obj, parent)) { |
| int endOffset = offset; |
| if (context.getCompletionChar() == Lookup.REPLACE_SELECT_CHAR) { |
| endOffset = CharArrayUtil.shiftForward(charsSequence, offset, " \t"); |
| if (charsSequence.length() > endOffset && charsSequence.charAt(endOffset) == '=') { |
| endOffset++; |
| endOffset = CharArrayUtil.shiftForward(charsSequence, endOffset, " \t"); |
| } |
| } |
| document.replaceString(offset, endOffset, " = "); |
| caretModel.moveToOffset(offset + 3); |
| return; |
| } |
| |
| if (PsiTreeUtil.getParentOfType(elementAt, GrImportStatement.class) != null) return; |
| |
| if (parameters.length == 1) { |
| if ((context.getCompletionChar() != '(' && context.getCompletionChar() != ' ') && |
| TypesUtil.isClassType(parameters[0].getType(), GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) { |
| int afterBrace; |
| final int nonWs = CharArrayUtil.shiftForward(charsSequence, offset, " \t"); |
| if (nonWs < document.getTextLength() && charsSequence.charAt(nonWs) == '{') { |
| afterBrace = nonWs + 1; |
| } |
| else { |
| if (isSpaceBeforeClosure(file)) { |
| document.insertString(offset, " "); |
| offset++; |
| } |
| if (ClosureCompleter.runClosureCompletion(context, method, substitutor, document, offset, parent)) return; |
| if (context.getCompletionChar() == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) { |
| //smart enter invoked |
| document.insertString(offset, "{\n}"); |
| afterBrace = offset + 1; //position caret before '{' for smart enter |
| context.setTailOffset(afterBrace); |
| } |
| else { |
| document.insertString(offset, "{}"); |
| afterBrace = offset + 1; |
| } |
| } |
| caretModel.moveToOffset(afterBrace); |
| return; |
| } |
| } |
| |
| context.commitDocument(); |
| |
| if (context.getCompletionChar() == ' ' && MethodParenthesesHandler.hasParams(item, context.getElements(), true, method)) { |
| return; |
| } |
| |
| |
| CommonCodeStyleSettings settings = context.getCodeStyleSettings(); |
| ParenthesesInsertHandler.getInstance(MethodParenthesesHandler.hasParams(item, context.getElements(), true, method), |
| settings.SPACE_BEFORE_METHOD_CALL_PARENTHESES, |
| settings.SPACE_WITHIN_METHOD_CALL_PARENTHESES, |
| true, true).handleInsert(context, item); |
| |
| AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(editor, method); |
| return; |
| } |
| |
| if (obj instanceof PsiClass) { |
| final PsiClass clazz = (PsiClass)obj; |
| Editor editor = context.getEditor(); |
| Document document = editor.getDocument(); |
| PsiFile file = PsiDocumentManager.getInstance(clazz.getProject()).getPsiFile(document); |
| assert file != null; |
| PsiElement elementAt = file.findElementAt(context.getStartOffset()); |
| assert elementAt != null; |
| CaretModel caretModel = editor.getCaretModel(); |
| int offset = context.getStartOffset() + elementAt.getTextLength(); |
| |
| final String text = document.getText(); |
| final PsiElement parent = elementAt.getParent(); |
| if (parent instanceof GrCodeReferenceElement && |
| parent.getParent() instanceof GrNewExpression && |
| (offset == text.length() || !text.substring(offset).trim().startsWith("("))) { |
| document.insertString(offset, "()"); |
| if (GroovyCompletionUtil.hasConstructorParameters(clazz, parent)) { |
| caretModel.moveToOffset(offset + 1); |
| return; |
| } |
| caretModel.moveToOffset(offset + 2); |
| return; |
| } |
| } |
| |
| if (context.getCompletionChar() == '=') { |
| context.setAddCompletionChar(false); |
| TailType.EQ.processTail(context.getEditor(), context.getTailOffset()); |
| return; |
| } |
| |
| if (obj instanceof PsiPackage) { |
| AutoPopupController.getInstance(context.getProject()).scheduleAutoPopup(context.getEditor()); |
| } |
| } |
| |
| private static boolean isSpaceBeforeClosure(PsiFile file) { |
| return CodeStyleSettingsManager.getSettings(file.getProject()) |
| .getCustomSettings(GroovyCodeStyleSettings.class).SPACE_BEFORE_CLOSURE_LBRACE; |
| } |
| |
| private static boolean isAnnotationNameValuePair(Object obj, PsiElement parent) { |
| if (parent instanceof GrAnnotationNameValuePair || parent != null && parent.getParent() instanceof GrAnnotationNameValuePair) { |
| if (obj instanceof PsiMethod) { |
| PsiClass aClass = ((PsiMethod)obj).getContainingClass(); |
| if (aClass != null && aClass.isAnnotationType()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private static void handleOverwrite(final int offset, final Document document) { |
| final CharSequence sequence = document.getCharsSequence(); |
| int i = offset; |
| while (i < sequence.length() && Character.isJavaIdentifierPart(sequence.charAt(i))) i++; |
| document.deleteString(offset, i); |
| } |
| } |