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.
16 package com.intellij.codeInsight.completion.util;
18 import com.intellij.codeInsight.completion.InsertHandler;
19 import com.intellij.codeInsight.completion.InsertionContext;
20 import com.intellij.codeInsight.lookup.LookupElement;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.psi.PsiElement;
25 import com.intellij.psi.PsiFile;
26 import com.intellij.psi.PsiWhiteSpace;
27 import org.jetbrains.annotations.Nullable;
32 public abstract class ParenthesesInsertHandler<T extends LookupElement> implements InsertHandler<T> {
33 public static final ParenthesesInsertHandler<LookupElement> WITH_PARAMETERS = new ParenthesesInsertHandler<LookupElement>() {
34 protected boolean placeCaretInsideParentheses(final InsertionContext context, final LookupElement item) {
38 public static final ParenthesesInsertHandler<LookupElement> NO_PARAMETERS = new ParenthesesInsertHandler<LookupElement>() {
39 protected boolean placeCaretInsideParentheses(final InsertionContext context, final LookupElement item) {
44 public static ParenthesesInsertHandler<LookupElement> getInstance(boolean hasParameters) {
45 return hasParameters ? WITH_PARAMETERS : NO_PARAMETERS;
48 public static ParenthesesInsertHandler<LookupElement> getInstance(final boolean hasParameters, final boolean spaceBeforeParentheses,
49 final boolean spaceBetweenParentheses,
50 final boolean insertRightParenthesis, boolean allowParametersOnNextLine) {
51 return new ParenthesesInsertHandler<LookupElement>(spaceBeforeParentheses, spaceBetweenParentheses, insertRightParenthesis, allowParametersOnNextLine) {
53 protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) {
59 private final boolean mySpaceBeforeParentheses;
60 private final boolean mySpaceBetweenParentheses;
61 private final boolean myMayInsertRightParenthesis;
62 private final boolean myAllowParametersOnNextLine;
64 protected ParenthesesInsertHandler(final boolean spaceBeforeParentheses,
65 final boolean spaceBetweenParentheses,
66 final boolean mayInsertRightParenthesis) {
67 this(spaceBeforeParentheses, spaceBetweenParentheses, mayInsertRightParenthesis, false);
70 protected ParenthesesInsertHandler(boolean spaceBeforeParentheses,
71 boolean spaceBetweenParentheses,
72 boolean mayInsertRightParenthesis,
73 boolean allowParametersOnNextLine) {
74 mySpaceBeforeParentheses = spaceBeforeParentheses;
75 mySpaceBetweenParentheses = spaceBetweenParentheses;
76 myMayInsertRightParenthesis = mayInsertRightParenthesis;
77 myAllowParametersOnNextLine = allowParametersOnNextLine;
80 protected ParenthesesInsertHandler() {
81 this(false, false, true);
84 private static boolean isToken(@Nullable final PsiElement element, final String text) {
85 return element != null && text.equals(element.getText());
88 protected abstract boolean placeCaretInsideParentheses(final InsertionContext context, final T item);
90 public void handleInsert(final InsertionContext context, final T item) {
91 final Editor editor = context.getEditor();
92 final Document document = editor.getDocument();
93 PsiElement element = findNextToken(context);
95 final char completionChar = context.getCompletionChar();
96 final boolean putCaretInside = completionChar == '(' || placeCaretInsideParentheses(context, item);
98 if (completionChar == '(') {
99 context.setAddCompletionChar(false);
102 if (isToken(element, "(")) {
103 int lparenthOffset = element.getTextRange().getStartOffset();
104 if (mySpaceBeforeParentheses && lparenthOffset == context.getTailOffset()) {
105 document.insertString(context.getTailOffset(), " ");
109 if (completionChar == '(' || completionChar == '\t') {
110 editor.getCaretModel().moveToOffset(lparenthOffset + 1);
112 editor.getCaretModel().moveToOffset(context.getTailOffset());
115 context.setTailOffset(lparenthOffset + 1);
117 PsiElement list = element.getParent();
118 PsiElement last = list.getLastChild();
119 if (isToken(last, ")")) {
120 int rparenthOffset = last.getTextRange().getStartOffset();
121 context.setTailOffset(rparenthOffset + 1);
122 if (!putCaretInside) {
123 for (int i = lparenthOffset + 1; i < rparenthOffset; i++) {
124 if (!Character.isWhitespace(document.getCharsSequence().charAt(i))) {
128 editor.getCaretModel().moveToOffset(context.getTailOffset());
129 } else if (mySpaceBetweenParentheses && document.getCharsSequence().charAt(lparenthOffset) == ' ') {
130 editor.getCaretModel().moveToOffset(lparenthOffset + 2);
132 editor.getCaretModel().moveToOffset(lparenthOffset + 1);
137 document.insertString(context.getTailOffset(), getSpace(mySpaceBeforeParentheses) + "(" + getSpace(mySpaceBetweenParentheses));
138 editor.getCaretModel().moveToOffset(context.getTailOffset());
141 if (!myMayInsertRightParenthesis) return;
143 if (context.getCompletionChar() == '(') {
144 //todo use BraceMatchingUtil.isPairedBracesAllowedBeforeTypeInFileType
145 int tail = context.getTailOffset();
146 if (tail < document.getTextLength() && StringUtil.isJavaIdentifierPart(document.getCharsSequence().charAt(tail))) {
151 document.insertString(context.getTailOffset(), getSpace(mySpaceBetweenParentheses) + ")");
152 if (!putCaretInside) {
153 editor.getCaretModel().moveToOffset(context.getTailOffset());
157 private static String getSpace(boolean needSpace) {
158 return needSpace ? " " : "";
162 protected PsiElement findNextToken(final InsertionContext context) {
163 final PsiFile file = context.getFile();
164 PsiElement element = file.findElementAt(context.getTailOffset());
165 if (element instanceof PsiWhiteSpace) {
166 if (!myAllowParametersOnNextLine && element.getText().contains("\n")) {
169 element = file.findElementAt(element.getTextRange().getEndOffset());