cleanup
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / completion / DefaultInsertHandler.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.codeInsight.completion;
17
18 import com.intellij.codeInsight.AutoPopupController;
19 import com.intellij.codeInsight.CharTailType;
20 import com.intellij.codeInsight.TailType;
21 import com.intellij.codeInsight.TailTypes;
22 import com.intellij.codeInsight.lookup.Lookup;
23 import com.intellij.codeInsight.lookup.LookupElement;
24 import com.intellij.codeInsight.lookup.LookupItem;
25 import com.intellij.openapi.editor.Document;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.psi.PsiDocumentManager;
29 import com.intellij.psi.PsiFile;
30 import com.intellij.psi.codeStyle.CodeStyleSettings;
31 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
32 import org.jetbrains.annotations.NotNull;
33
34 public class DefaultInsertHandler extends TemplateInsertHandler implements Cloneable {
35
36   public static final DefaultInsertHandler NO_TAIL_HANDLER = new DefaultInsertHandler(){
37     @Override
38     protected TailType getTailType(char completionChar, LookupItem item) {
39       return TailType.NONE;
40     }
41   };
42
43   public void handleInsert(final InsertionContext context, LookupElement item) {
44     super.handleInsert(context, item);
45
46     handleInsertInner(context, (LookupItem)item, context.getCompletionChar());
47   }
48
49   private void handleInsertInner(InsertionContext context, LookupItem item, final char completionChar) {
50     final Project project = context.getProject();
51     final Editor editor = context.getEditor();
52     final Document document = editor.getDocument();
53     PsiDocumentManager.getInstance(project).commitDocument(document);
54
55     TailType tailType = getTailType(completionChar, item);
56
57     InsertHandlerState state = new InsertHandlerState(context.getSelectionEndOffset(), context.getSelectionEndOffset());
58
59     if (completionChar == Lookup.REPLACE_SELECT_CHAR) {
60       removeEndOfIdentifier(context);
61     }
62     else if(context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET) != context.getSelectionEndOffset()) {
63       JavaCompletionUtil.resetParensInfo(context.getOffsetMap());
64     }
65
66     handleParentheses(false, false, tailType, context, state);
67
68     context.setTailOffset(state.tailOffset);
69     state.caretOffset = processTail(tailType, state.caretOffset, state.tailOffset, editor);
70     editor.getSelectionModel().removeSelection();
71
72     if (tailType == TailType.DOT || context.getCompletionChar() == '.') {
73       AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null);
74     }
75
76   }
77
78   private static void handleParentheses(final boolean hasParams, final boolean needParenth, TailType tailType, InsertionContext context, InsertHandlerState myState){
79     final Document document = context.getEditor().getDocument();
80     boolean insertRightParenth = context.getCompletionChar() != Lookup.COMPLETE_STATEMENT_SELECT_CHAR;
81
82     if (needParenth){
83       if (context.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET) >= 0 && context.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET) >= 0){
84         myState.tailOffset = context.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET);
85         if (context.getOffsetMap().getOffset(JavaCompletionUtil.RPAREN_OFFSET) < 0 && insertRightParenth){
86           document.insertString(myState.tailOffset, ")");
87           myState.tailOffset += 1;
88         }
89         if (hasParams){
90           myState.caretOffset = context.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET) + 1;
91         }
92         else{
93           myState.caretOffset = context.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET);
94         }
95       }
96       else{
97         final CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(context.getProject());
98         myState.tailOffset = context.getSelectionEndOffset();
99         myState.caretOffset = context.getSelectionEndOffset();
100
101         if(styleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES){
102           document.insertString(myState.tailOffset++, " ");
103           myState.caretOffset ++;
104         }
105         if (insertRightParenth) {
106           final CharSequence charsSequence = document.getCharsSequence();
107           if (charsSequence.length() <= myState.tailOffset || charsSequence.charAt(myState.tailOffset) != '(') {
108             document.insertString(myState.tailOffset, "(");
109           }
110
111           document.insertString(myState.tailOffset + 1, ")");
112           if (hasParams){
113             myState.tailOffset += 2;
114             myState.caretOffset++;
115           }
116           else{
117             if (tailType != TailTypes.CALL_RPARENTH) {
118               myState.tailOffset += 2;
119               myState.caretOffset += 2;
120             }
121             else {
122               myState.tailOffset++;
123               myState.caretOffset++;
124             }
125           }
126         }
127         else{
128           document.insertString(myState.tailOffset++, "(");
129           myState.caretOffset ++;
130         }
131
132         if(hasParams && styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES){
133           document.insertString(myState.caretOffset++, " ");
134           myState.tailOffset++;
135         }
136       }
137     }
138   }
139
140   public static void removeEndOfIdentifier(InsertionContext context){
141     final Document document = context.getEditor().getDocument();
142     JavaCompletionUtil.initOffsets(context.getFile(), context.getProject(), context.getOffsetMap());
143     document.deleteString(context.getSelectionEndOffset(), context.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET));
144     if(context.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET) > 0){
145       document.deleteString(context.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET),
146                               context.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET));
147       JavaCompletionUtil.resetParensInfo(context.getOffsetMap());
148     }
149   }
150
151   protected TailType getTailType(final char completionChar, LookupItem item){
152     switch(completionChar){
153       case '.': return new CharTailType('.', false);
154       case ',': return TailType.COMMA;
155       case ';': return TailType.SEMICOLON;
156       case '=': return TailType.EQ;
157       case ' ': return TailType.SPACE;
158       case ':': return TailType.CASE_COLON; //?
159       case '<':
160       case '>':
161       case '\"':
162     }
163     final TailType attr = item.getTailType();
164     return attr == TailType.UNKNOWN ? TailType.NONE : attr;
165   }
166
167   private static int processTail(TailType tailType, int caretOffset, int tailOffset, Editor editor) {
168     editor.getCaretModel().moveToOffset(caretOffset);
169     tailType.processTail(editor, tailOffset);
170     return editor.getCaretModel().getOffset();
171   }
172
173   @Override
174   protected void populateInsertMap(@NotNull final PsiFile file, @NotNull final OffsetMap offsetMap) {
175     JavaCompletionUtil.initOffsets(file, file.getProject(), offsetMap);
176   }
177
178   public static class InsertHandlerState{
179     int tailOffset;
180     int caretOffset;
181
182     public InsertHandlerState(int caretOffset, int tailOffset){
183       this.caretOffset = caretOffset;
184       this.tailOffset = tailOffset;
185     }
186   }
187 }