JSSpacingProcessor.java

1    /* 
2     * Copyright 2000-2005 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.lang.javascript.formatter; 
17    
18   import com.intellij.formatting.Spacing; 
19   import com.intellij.lang.ASTNode; 
20   import com.intellij.lang.javascript.JSElementTypes; 
21   import com.intellij.lang.javascript.JSNodeVisitor; 
22   import com.intellij.lang.javascript.JSTokenTypes; 
23   import com.intellij.psi.codeStyle.CodeStyleSettings; 
24   import com.intellij.psi.tree.IElementType; 
25    
26   /** 
27    * @author ven 
28    */ 
29   public class JSSpacingProcessor extends JSNodeVisitor { 
30     private final CodeStyleSettings mySettings; 
31     private Spacing myResult; 
32     private final IElementType type1; 
33     private final IElementType type2; 
34    
35     public JSSpacingProcessor(final ASTNode parent, final ASTNode child1, final ASTNode child2, final CodeStyleSettings settings) { 
36       mySettings = settings; 
37       type1 = child1.getElementType(); 
38       type2 = child2.getElementType(); 
39       visit(parent); 
40     } 
41    
42     public Spacing getResult() { 
43       return myResult; 
44     } 
45    
46     public void visitParameterList(final ASTNode node) { 
47       if (type1 == JSTokenTypes.LPAR && type2 == JSTokenTypes.RPAR) { 
48         setSingleSpace(false); 
49       } 
50       else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
51         setSingleSpace(mySettings.SPACE_WITHIN_METHOD_PARENTHESES); 
52       } 
53       else if (type1 == JSTokenTypes.COMMA) { 
54         setSingleSpace(mySettings.SPACE_AFTER_COMMA); 
55       } 
56       else if (type2 == JSTokenTypes.COMMA) { 
57         setSingleSpace(mySettings.SPACE_BEFORE_COMMA); 
58       } 
59     } 
60    
61     public void visitBlock(final ASTNode node) { 
62       if (JSElementTypes.SOURCE_ELEMENTS.isInSet(type1) || JSElementTypes.SOURCE_ELEMENTS.isInSet(type2) || 
63         type2 == JSTokenTypes.RBRACE) { 
64         myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
65       } 
66     } 
67    
68     public void visitFile(final ASTNode node) { 
69       if (JSElementTypes.SOURCE_ELEMENTS.isInSet(type1) || JSElementTypes.SOURCE_ELEMENTS.isInSet(type2)) { 
70         myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
71       } 
72     } 
73    
74     public void visitFunctionDeclaration(final ASTNode node) { 
75       if (type1 == JSTokenTypes.FUNCTION_KEYWORD && type2 == JSTokenTypes.IDENTIFIER) { 
76         setSingleSpace(true); 
77       } 
78       else if (type1 == JSTokenTypes.IDENTIFIER && type2 == JSElementTypes.PARAMETER_LIST) { 
79         setSingleSpace(mySettings.SPACE_BEFORE_METHOD_PARENTHESES); 
80       } 
81       else if (type1 == JSElementTypes.PARAMETER_LIST) { 
82         setBraceSpace(mySettings.SPACE_BEFORE_METHOD_LBRACE, mySettings.METHOD_BRACE_STYLE); 
83       } 
84     } 
85    
86    
87     public void visitFunctionExpression(final ASTNode node) { 
88       visitFunctionDeclaration(node); 
89     } 
90    
91     public void visitReferenceExpression(final ASTNode node) { 
92       if (type1 == JSTokenTypes.NEW_KEYWORD) { 
93         setSingleSpace(true); 
94       } 
95       else { 
96         setSingleSpace(false); // a.b should not have spaces before and after dot 
97       } 
98     } 
99    
100    public void visitIfStatement(final ASTNode node) { 
101      if (type1 == JSTokenTypes.IF_KEYWORD && type2 == JSTokenTypes.LPAR) { 
102        setSingleSpace(mySettings.SPACE_BEFORE_IF_PARENTHESES); 
103      } 
104      else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
105        setSingleSpace(mySettings.SPACE_WITHIN_IF_PARENTHESES); 
106      } 
107      else if (type1 == JSTokenTypes.RPAR && type2 == JSElementTypes.BLOCK) { 
108        setBraceSpace(mySettings.SPACE_BEFORE_IF_LBRACE, mySettings.BRACE_STYLE); 
109      } 
110      else if (type2 == JSTokenTypes.ELSE_KEYWORD) { 
111        setLineBreakSpace(mySettings.ELSE_ON_NEW_LINE); 
112      } 
113      else if (type1 == JSTokenTypes.ELSE_KEYWORD && type2 == JSElementTypes.BLOCK) { 
114        setBraceSpace(mySettings.SPACE_BEFORE_ELSE_LBRACE, mySettings.BRACE_STYLE); 
115      } 
116    } 
117   
118    public void visitCallExpression(final ASTNode node) { 
119      if (type2 == JSElementTypes.ARGUMENT_LIST) { 
120        setSingleSpace(mySettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES); 
121      } 
122    } 
123   
124    public void visitNewExpression(final ASTNode node) { 
125      if (type1 == JSTokenTypes.NEW_KEYWORD) { 
126        setSingleSpace(true); 
127      } 
128      else if (type2 == JSElementTypes.ARGUMENT_LIST) { 
129        setSingleSpace(mySettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES); 
130      } 
131    } 
132   
133    public void visitForStatement(final ASTNode node) { 
134      if (type1 == JSTokenTypes.SEMICOLON) { 
135        setSingleSpace(true); 
136      } 
137      else if (type2 == JSTokenTypes.SEMICOLON) { 
138        setSingleSpace(mySettings.SPACE_BEFORE_SEMICOLON); 
139      } 
140   
141      if (type1 == JSTokenTypes.FOR_KEYWORD && type2 == JSTokenTypes.LPAR) { 
142        setSingleSpace(mySettings.SPACE_BEFORE_FOR_PARENTHESES); 
143      } 
144      else if (type1 == JSTokenTypes.RPAR && type2 == JSElementTypes.BLOCK) { 
145        setBraceSpace(mySettings.SPACE_BEFORE_FOR_LBRACE, mySettings.BRACE_STYLE); 
146      } 
147      else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
148        setSingleSpace(mySettings.SPACE_WITHIN_FOR_PARENTHESES); 
149      } 
150    } 
151   
152    public void visitDoWhileStatement(final ASTNode node) { 
153      if (type2 == JSTokenTypes.WHILE_KEYWORD) { 
154        if (mySettings.WHILE_ON_NEW_LINE) { 
155          myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
156        } 
157        else { 
158          myResult = Spacing.createSpacing(1, 1,  0, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
159        } 
160      } else if (type2 == JSTokenTypes.LPAR) { 
161        setSingleSpace(mySettings.SPACE_BEFORE_WHILE_PARENTHESES); 
162      } else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
163        setSingleSpace(mySettings.SPACE_WITHIN_WHILE_PARENTHESES); 
164      } 
165    } 
166   
167    public void visitForInStatement(final ASTNode node) { 
168      if (type1 == JSTokenTypes.VAR_KEYWORD || type2 == JSTokenTypes.VAR_KEYWORD) { 
169        setSingleSpace(true); 
170      } 
171      else if (type1 == JSTokenTypes.FOR_KEYWORD && type2 == JSTokenTypes.LPAR) { 
172        setSingleSpace(mySettings.SPACE_BEFORE_FOR_PARENTHESES); 
173      } 
174      else if (type1 == JSTokenTypes.RPAR && type2 == JSElementTypes.BLOCK) { 
175        setBraceSpace(mySettings.SPACE_BEFORE_FOR_LBRACE, mySettings.BRACE_STYLE); 
176      } 
177      else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
178        setSingleSpace(mySettings.SPACE_WITHIN_FOR_PARENTHESES); 
179      } 
180    } 
181   
182    public void visitWhileStatement(final ASTNode node) { 
183      if (type1 == JSTokenTypes.WHILE_KEYWORD && type2 == JSTokenTypes.LPAR) { 
184        setSingleSpace(mySettings.SPACE_BEFORE_WHILE_PARENTHESES); 
185      } 
186      else if (type1 == JSTokenTypes.RPAR && type2 == JSElementTypes.BLOCK) { 
187        setBraceSpace(mySettings.SPACE_BEFORE_WHILE_LBRACE, mySettings.BRACE_STYLE); 
188      } 
189      else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
190        setSingleSpace(mySettings.SPACE_WITHIN_WHILE_PARENTHESES); 
191      } 
192    } 
193   
194    public void visitWithStatement(final ASTNode node) { 
195      if (type1 == JSTokenTypes.WITH_KEYWORD && type2 == JSTokenTypes.LPAR) { 
196        setSingleSpace(mySettings.SPACE_BEFORE_WHILE_PARENTHESES); 
197      } 
198      else if (type1 == JSTokenTypes.RPAR && type2 == JSElementTypes.BLOCK) { 
199        setBraceSpace(mySettings.SPACE_BEFORE_WHILE_LBRACE, mySettings.BRACE_STYLE); 
200      } 
201      else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
202        setSingleSpace(mySettings.SPACE_WITHIN_WHILE_PARENTHESES); 
203      } 
204    } 
205   
206    public void visitTryStatement(final ASTNode node) { 
207      if (type1 == JSTokenTypes.TRY_KEYWORD && type2 == JSElementTypes.BLOCK) { 
208        setBraceSpace(mySettings.SPACE_BEFORE_TRY_LBRACE, mySettings.BRACE_STYLE); 
209      } 
210      else if (type2 == JSElementTypes.CATCH_BLOCK) { 
211        setLineBreakSpace(mySettings.CATCH_ON_NEW_LINE); 
212      } 
213      else if (type2 == JSTokenTypes.FINALLY_KEYWORD) { 
214        setLineBreakSpace(mySettings.FINALLY_ON_NEW_LINE); 
215      } 
216      else if (type1 == JSTokenTypes.FINALLY_KEYWORD) { 
217        setBraceSpace(mySettings.SPACE_BEFORE_FINALLY_LBRACE, mySettings.BRACE_STYLE); 
218      } 
219    } 
220   
221    public void visitCatchBlock(final ASTNode node) { 
222      if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
223        setSingleSpace(mySettings.SPACE_WITHIN_CATCH_PARENTHESES); 
224      } 
225      if (type2 == JSElementTypes.BLOCK) { 
226        setBraceSpace(mySettings.SPACE_BEFORE_CATCH_LBRACE, mySettings.BRACE_STYLE); 
227      } 
228    } 
229   
230    public void visitSwitchStatement(final ASTNode node) { 
231      if (type1 == JSTokenTypes.SWITCH_KEYWORD && type2 == JSTokenTypes.LPAR) { 
232        setSingleSpace(mySettings.SPACE_BEFORE_SWITCH_PARENTHESES); 
233      } 
234      else if (type1 == JSTokenTypes.RPAR) { 
235        setBraceSpace(mySettings.SPACE_BEFORE_SWITCH_LBRACE, mySettings.BRACE_STYLE); 
236      } 
237      else if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
238        setSingleSpace(mySettings.SPACE_WITHIN_SWITCH_PARENTHESES); 
239      } 
240    } 
241   
242    public void visitArgumentList(final ASTNode node) { 
243      if (type1 == JSTokenTypes.LPAR || type2 == JSTokenTypes.RPAR) { 
244        setSingleSpace(false); 
245      } 
246      else if (type1 == JSTokenTypes.COMMA) { 
247        setSingleSpace(mySettings.SPACE_AFTER_COMMA); 
248      } 
249      else if (type2 == JSTokenTypes.COMMA) { 
250        setSingleSpace(mySettings.SPACE_BEFORE_COMMA); 
251      } 
252    } 
253   
254    public void visitStatement(final ASTNode node) { 
255      if (type2 == JSTokenTypes.SEMICOLON) { 
256        setSingleSpace(false); 
257      } 
258    } 
259   
260    public void visitVarStatement(final ASTNode node) { 
261      if (type1 == JSTokenTypes.VAR_KEYWORD) { 
262        setSingleSpace(true); 
263      } 
264    } 
265   
266    public void visitVariable(final ASTNode node) { 
267      if (type1 == JSTokenTypes.EQ || type2 == JSTokenTypes.EQ) { // Initializer 
268        setSingleSpace(mySettings.SPACE_AROUND_ASSIGNMENT_OPERATORS); 
269      } 
270    } 
271   
272    public void visitBinaryExpression(final ASTNode node) { 
273      IElementType opSign = null; 
274      if (JSTokenTypes.OPERATIONS.isInSet(type1)) { 
275        opSign = type1; 
276      } 
277      else if (JSTokenTypes.OPERATIONS.isInSet(type2)) { 
278        opSign = type2; 
279      } 
280   
281      if (opSign != null) { 
282        setSingleSpace(getSpaceAroundOption(opSign)); 
283      } 
284    } 
285   
286    private boolean getSpaceAroundOption(final IElementType opSign) { 
287      boolean option = false; 
288      if (JSTokenTypes.ADDITIVE_OPERATIONS.isInSet(opSign)) { 
289        option = mySettings.SPACE_AROUND_ADDITIVE_OPERATORS; 
290      } 
291      else if (JSTokenTypes.MULTIPLICATIVE_OPERATIONS.isInSet(opSign)) { 
292        option = mySettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS; 
293      } 
294      else if (JSTokenTypes.ASSIGNMENT_OPERATIONS.isInSet(opSign)) { 
295        option = mySettings.SPACE_AROUND_ASSIGNMENT_OPERATORS; 
296      } 
297      else if (JSTokenTypes.EQUALITY_OPERATIONS.isInSet(opSign)) { 
298        option = mySettings.SPACE_AROUND_EQUALITY_OPERATORS; 
299      } 
300      else if (JSTokenTypes.RELATIONAL_OPERATIONS.isInSet(opSign)) { 
301        option = mySettings.SPACE_AROUND_RELATIONAL_OPERATORS; 
302      } 
303      else if (JSTokenTypes.SHIFT_OPERATIONS.isInSet(opSign)) { 
304        option = mySettings.SPACE_AROUND_BITWISE_OPERATORS; 
305      } 
306      else if (opSign == JSTokenTypes.ANDAND || opSign == JSTokenTypes.OROR) { 
307        option = mySettings.SPACE_AROUND_LOGICAL_OPERATORS; 
308      } 
309      else if (opSign == JSTokenTypes.OR || opSign == JSTokenTypes.AND || opSign == JSTokenTypes.XOR) { 
310        option = mySettings.SPACE_AROUND_BITWISE_OPERATORS; 
311      } 
312      return option; 
313    } 
314   
315    private void setSingleSpace(boolean needSpace) { 
316      final int spaces = needSpace ? 1 : 0; 
317      myResult = Spacing.createSpacing(spaces, spaces, 0, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
318    } 
319   
320    private void setBraceSpace(final boolean needSpaceSetting, final int braceStyleSetting) { 
321      int spaces = needSpaceSetting ? 1 : 0; 
322      int lineBreaks = braceStyleSetting == CodeStyleSettings.END_OF_LINE ? 0 : 1; 
323      myResult = Spacing.createSpacing(spaces, spaces, lineBreaks, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
324    } 
325   
326    private void setLineBreakSpace(final boolean needLineBreak) { 
327      final int breaks = needLineBreak ? 1 : 0; 
328      myResult = Spacing.createSpacing(1, 1, breaks, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); 
329    } 
330   
331  } 
332