lang-api
[idea/community.git] / platform / lang-api / src / com / intellij / codeInsight / completion / util / ParenthesesInsertHandler.java
1 /*
2  * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
3  * Use is subject to license terms.
4  */
5 package com.intellij.codeInsight.completion.util;
6
7 import com.intellij.codeInsight.TailType;
8 import com.intellij.codeInsight.completion.InsertHandler;
9 import com.intellij.codeInsight.completion.InsertionContext;
10 import com.intellij.codeInsight.lookup.LookupElement;
11 import com.intellij.openapi.editor.Document;
12 import com.intellij.openapi.editor.Editor;
13 import com.intellij.psi.PsiElement;
14 import com.intellij.psi.PsiFile;
15 import com.intellij.psi.PsiWhiteSpace;
16 import org.jetbrains.annotations.Nullable;
17
18 /**
19  * @author peter
20  */
21 public abstract class ParenthesesInsertHandler<T extends LookupElement> implements InsertHandler<T> {
22   public static final ParenthesesInsertHandler<LookupElement> WITH_PARAMETERS = new ParenthesesInsertHandler<LookupElement>() {
23     protected boolean placeCaretInsideParentheses(final InsertionContext context, final LookupElement item) {
24       return true;
25     }
26   };
27   public static final ParenthesesInsertHandler<LookupElement> NO_PARAMETERS = new ParenthesesInsertHandler<LookupElement>() {
28     protected boolean placeCaretInsideParentheses(final InsertionContext context, final LookupElement item) {
29       return false;
30     }
31   };
32
33   private final boolean mySpaceBeforeParentheses;
34   private final boolean mySpaceBetweenParentheses;
35   private final boolean myInsertRightParenthesis;
36
37   protected ParenthesesInsertHandler(final boolean spaceBeforeParentheses,
38                                      final boolean spaceBetweenParentheses,
39                                      final boolean insertRightParenthesis) {
40     mySpaceBeforeParentheses = spaceBeforeParentheses;
41     mySpaceBetweenParentheses = spaceBetweenParentheses;
42     myInsertRightParenthesis = insertRightParenthesis;
43   }
44
45   protected ParenthesesInsertHandler() {
46     this(false, false, true);
47   }
48
49   private static boolean isToken(@Nullable final PsiElement element, final String text) {
50     return element != null && text.equals(element.getText());
51   }
52
53   protected abstract boolean placeCaretInsideParentheses(final InsertionContext context, final T item);
54
55   public void handleInsert(final InsertionContext context, final T item) {
56     final Editor editor = context.getEditor();
57     final Document document = editor.getDocument();
58     PsiElement element = findNextToken(context);
59
60     final boolean hasParams = placeCaretInsideParentheses(context, item);
61
62     final char completionChar = context.getCompletionChar();
63     if (completionChar == '(') {
64       context.setAddCompletionChar(false);
65     }
66
67     if (isToken(element, "(")) {
68       int lparenthOffset = element.getTextRange().getStartOffset();
69       if (mySpaceBeforeParentheses && lparenthOffset == context.getTailOffset()) {
70         document.insertString(context.getTailOffset(), " ");
71         lparenthOffset++;
72       }
73
74       if (completionChar == '(' || completionChar == '\t') {
75         editor.getCaretModel().moveToOffset(lparenthOffset + 1);
76       } else {
77         editor.getCaretModel().moveToOffset(context.getTailOffset());
78       }
79
80       context.setTailOffset(lparenthOffset + 1);
81
82       PsiElement list = element.getParent();
83       PsiElement last = list.getLastChild();
84       if (isToken(last, ")")) {
85         int rparenthOffset = last.getTextRange().getStartOffset();
86         context.setTailOffset(rparenthOffset + 1);
87         if (!hasParams) {
88           for (int i = lparenthOffset + 1; i < rparenthOffset; i++) {
89             if (!Character.isWhitespace(document.getCharsSequence().charAt(i))) {
90               return;
91             }
92           }
93           editor.getCaretModel().moveToOffset(context.getTailOffset());
94         } else if (mySpaceBetweenParentheses && document.getCharsSequence().charAt(lparenthOffset) == ' ') {
95           editor.getCaretModel().moveToOffset(lparenthOffset + 2);
96         } else {
97           editor.getCaretModel().moveToOffset(lparenthOffset + 1);
98         }
99         return;
100       }
101     } else {
102       int tailOffset = context.getTailOffset();
103       if (mySpaceBeforeParentheses) {
104         tailOffset = TailType.insertChar(editor, tailOffset, ' ');
105       }
106       tailOffset = TailType.insertChar(editor, tailOffset, '(');
107       if (mySpaceBetweenParentheses) {
108         tailOffset = TailType.insertChar(editor, tailOffset, ' ');
109       }
110     }
111
112     if (!myInsertRightParenthesis) return;
113
114     int tailOffset = context.getTailOffset();
115     int caret = tailOffset;
116     if (mySpaceBetweenParentheses) {
117       tailOffset = TailType.insertChar(editor, tailOffset, ' ');
118     }
119     document.insertString(tailOffset, ")");
120     editor.getCaretModel().moveToOffset(hasParams ? caret : context.getTailOffset());
121   }
122
123   @Nullable
124   protected PsiElement findNextToken(final InsertionContext context) {
125     final PsiFile file = context.getFile();
126     PsiElement element = file.findElementAt(context.getTailOffset());
127     if (element instanceof PsiWhiteSpace) {
128       element = file.findElementAt(element.getTextRange().getEndOffset());
129     }
130     return element;
131   }
132
133 }