IDEA-79862 Java: auto-insert space after completing "extends" and "implements"
[idea/community.git] / platform / lang-api / src / com / intellij / codeInsight / TailType.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;
17
18 import com.intellij.codeInsight.completion.InsertionContext;
19 import com.intellij.openapi.editor.CaretModel;
20 import com.intellij.openapi.editor.Document;
21 import com.intellij.openapi.editor.Editor;
22 import com.intellij.psi.codeStyle.CodeStyleSettings;
23 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
24 import org.jetbrains.annotations.NotNull;
25
26 /**
27  * @author peter
28  */
29 public abstract class TailType {
30
31   public static int insertChar(final Editor editor, final int tailOffset, final char c) {
32     return insertChar(editor, tailOffset, c, true);
33   }
34
35   public static int insertChar(Editor editor, int tailOffset, char c, boolean overwrite) {
36     Document document = editor.getDocument();
37     int textLength = document.getTextLength();
38     CharSequence chars = document.getCharsSequence();
39     if (tailOffset == textLength || !overwrite || chars.charAt(tailOffset) != c){
40       document.insertString(tailOffset, String.valueOf(c));
41     }
42     return moveCaret(editor, tailOffset, 1);
43   }
44
45   protected static int moveCaret(final Editor editor, final int tailOffset, final int delta) {
46     final CaretModel model = editor.getCaretModel();
47     if (model.getOffset() == tailOffset) {
48       model.moveToOffset(tailOffset + delta);
49     }
50     return tailOffset + delta;
51   }
52
53   public static final TailType UNKNOWN = new TailType(){
54     public int processTail(final Editor editor, final int tailOffset) {
55       return tailOffset;
56     }
57
58     public String toString() {
59       return "UNKNOWN";
60     }
61
62   };
63
64   public static final TailType NONE = new TailType(){
65     public int processTail(final Editor editor, final int tailOffset) {
66       return tailOffset;
67     }
68
69     public String toString() {
70       return "NONE";
71     }
72   };
73
74   public static final TailType SEMICOLON = new CharTailType(';');
75   public static final TailType EXCLAMATION = new CharTailType('!');
76
77   public static final TailType COMMA = new TailType(){
78     public int processTail(final Editor editor, int tailOffset) {
79       CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(editor.getProject());
80       if (styleSettings.SPACE_BEFORE_COMMA) tailOffset = insertChar(editor, tailOffset, ' ');
81       tailOffset = insertChar(editor, tailOffset, ',');
82       if (styleSettings.SPACE_AFTER_COMMA) tailOffset = insertChar(editor, tailOffset, ' ');
83       return tailOffset;
84     }
85
86     public String toString() {
87       return "COMMA";
88     }
89   };
90   /**
91    * insert a space, overtype if already present
92    */
93   public static final TailType SPACE = new CharTailType(' ');
94   /**
95    * always insert a space
96    */
97   public static final TailType INSERT_SPACE = new CharTailType(' ', false);
98   /**
99    * insert a space unless there's one at the caret position already, followed by a sowrd
100    */
101   public static final TailType HUMBLE_SPACE_BEFORE_WORD = new CharTailType(' ', false) {
102
103     @Override
104     public boolean isApplicable(@NotNull InsertionContext context) {
105       CharSequence text = context.getDocument().getCharsSequence();
106       int tail = context.getTailOffset();
107       if (text.length() > tail + 1 && text.charAt(tail) == ' ' && Character.isLetter(text.charAt(tail + 1))) {
108         return false;
109       }
110       return super.isApplicable(context);
111     }
112
113     @Override
114     public String toString() {
115       return "HUMBLE_SPACE_BEFORE_WORD";
116     }
117   };
118   public static final TailType DOT = new CharTailType('.');
119
120   public static final TailType CASE_COLON = new CharTailType(':');
121   public static final TailType COND_EXPR_COLON = new TailType(){
122     public int processTail(final Editor editor, final int tailOffset) {
123       Document document = editor.getDocument();
124       int textLength = document.getTextLength();
125       CharSequence chars = document.getCharsSequence();
126
127       if (tailOffset < textLength - 1 && chars.charAt(tailOffset) == ' ' && chars.charAt(tailOffset + 1) == ':') {
128         return moveCaret(editor, tailOffset, 2);
129       }
130       if (tailOffset < textLength && chars.charAt(tailOffset) == ':') {
131         return moveCaret(editor, tailOffset, 1);
132       }
133       document.insertString(tailOffset, " : ");
134       return moveCaret(editor, tailOffset, 3);
135     }
136
137     public String toString() {
138       return "COND_EXPR_COLON";
139     }
140   };
141
142   public static final TailType EQ = new TailTypeEQ();
143
144   public static class TailTypeEQ extends TailType {
145
146     protected boolean isSpaceAroundAssignmentOperators(Editor editor, int tailOffset) {
147       return CodeStyleSettingsManager.getSettings(editor.getProject()).SPACE_AROUND_ASSIGNMENT_OPERATORS;
148     }
149
150     public int processTail(final Editor editor, int tailOffset) {
151       Document document = editor.getDocument();
152       int textLength = document.getTextLength();
153       CharSequence chars = document.getCharsSequence();
154       if (tailOffset < textLength - 1 && chars.charAt(tailOffset) == ' ' && chars.charAt(tailOffset + 1) == '='){
155         return moveCaret(editor, tailOffset, 2);
156       }
157       if (tailOffset < textLength && chars.charAt(tailOffset) == '='){
158         return moveCaret(editor, tailOffset, 1);
159       }
160       if (isSpaceAroundAssignmentOperators(editor, tailOffset)) {
161         document.insertString(tailOffset, " =");
162         tailOffset = moveCaret(editor, tailOffset, 2);
163         tailOffset = insertChar(editor, tailOffset, ' ');
164       }
165       else{
166         document.insertString(tailOffset, "=");
167         tailOffset = moveCaret(editor, tailOffset, 1);
168       }
169       return tailOffset;
170     }
171   }
172
173   public static final TailType LPARENTH = new CharTailType('(');
174
175   public abstract int processTail(final Editor editor, int tailOffset);
176
177   public static TailType createSimpleTailType(final char c) {
178     return new CharTailType(c);
179   }
180
181   public boolean isApplicable(@NotNull final InsertionContext context) {
182     return true;
183   }
184 }