SubBlockVisitor.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.blocks; 
17    
18   import com.intellij.formatting.*; 
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.lang.javascript.formatter.Util; 
24   import com.intellij.lang.javascript.psi.JSAssignmentExpression; 
25   import com.intellij.lang.javascript.psi.JSBinaryExpression; 
26   import com.intellij.lang.javascript.psi.JSExpression; 
27   import com.intellij.lang.javascript.psi.JSLoopStatement; 
28   import com.intellij.psi.codeStyle.CodeStyleSettings; 
29   import com.intellij.psi.tree.IElementType; 
30    
31   import java.util.ArrayList; 
32   import java.util.List; 
33    
34   /** 
35    * @author ven 
36    */ 
37   public class SubBlockVisitor extends JSNodeVisitor { 
38     List<Block> myBlocks = new ArrayList<Block>(); 
39     private final CodeStyleSettings mySettings; 
40    
41     public SubBlockVisitor(CodeStyleSettings settings) { 
42       mySettings = settings; 
43     } 
44    
45     public List<Block> getBlocks() { 
46       return myBlocks; 
47     } 
48    
49     public void visitElement(final ASTNode node) { 
50       Alignment alignment = getDefaultAlignment(node); 
51    
52       ASTNode child = node.getFirstChildNode(); 
53       while(child != null) { 
54         if (child.getElementType() != JSTokenTypes.WHITE_SPACE && 
55           child.getTextRange().getLength() > 0) { 
56           Wrap wrap = getWrap(node, child); 
57           Alignment childAlignment = alignmentProjection(alignment, node, child); 
58           Indent childIndent = getIndent(node, child); 
59           myBlocks.add(new JSBlock(child, childAlignment, childIndent, wrap, mySettings)); 
60         } 
61         child = child.getTreeNext(); 
62       } 
63     } 
64    
65     static Alignment getDefaultAlignment(final ASTNode node) { 
66       if (node.getElementType() == JSElementTypes.FOR_STATEMENT || 
67           node.getElementType() == JSElementTypes.PARAMETER_LIST || 
68           node.getElementType() == JSElementTypes.BINARY_EXPRESSION || 
69           node.getElementType() == JSElementTypes.ASSIGNMENT_EXPRESSION || 
70           node.getElementType() == JSElementTypes.CONDITIONAL_EXPRESSION) { 
71         return Alignment.createAlignment(); 
72       } 
73    
74       return null; 
75     } 
76    
77     private Indent getIndent(final ASTNode node, final ASTNode child) { 
78       if (node.getElementType() == JSElementTypes.FILE || 
79           node.getElementType() == JSElementTypes.EMBEDDED_CONTENT) { 
80         return Indent.getNoneIndent(); 
81       } 
82    
83       if (child.getElementType() == JSElementTypes.BLOCK) { 
84         if (node.getElementType() == JSElementTypes.FUNCTION_DECLARATION && 
85           (mySettings.METHOD_BRACE_STYLE == CodeStyleSettings.NEXT_LINE_SHIFTED || 
86            mySettings.METHOD_BRACE_STYLE == CodeStyleSettings.NEXT_LINE_SHIFTED2)) { 
87           return Indent.getNormalIndent(); 
88         } 
89         if (mySettings.BRACE_STYLE == CodeStyleSettings.NEXT_LINE_SHIFTED || 
90           mySettings.BRACE_STYLE == CodeStyleSettings.NEXT_LINE_SHIFTED2) { 
91           return Indent.getNormalIndent(); 
92         } 
93         return Indent.getNoneIndent(); 
94       } 
95    
96       if (child.getElementType() == JSElementTypes.CATCH_BLOCK) { 
97         return Indent.getNoneIndent(); 
98       } 
99    
100      if (child.getElementType() == JSElementTypes.CASE_CLAUSE) { 
101        return mySettings.INDENT_CASE_FROM_SWITCH ? Indent.getNormalIndent() : Indent.getNoneIndent(); 
102      } 
103   
104      if (node.getElementType() == JSElementTypes.CASE_CLAUSE) { 
105        if (JSElementTypes.STATEMENTS.isInSet(child.getElementType())) { 
106          return Indent.getNormalIndent(); 
107        } 
108        return Indent.getNoneIndent(); 
109      } 
110   
111      if (node.getElementType() == JSElementTypes.SWITCH_STATEMENT && child.getElementType() == JSTokenTypes.RBRACE) { 
112        return Indent.getNoneIndent(); 
113      } 
114   
115      if (node.getElementType() == JSElementTypes.IF_STATEMENT) { 
116        if (child.getElementType() == JSTokenTypes.ELSE_KEYWORD) { 
117          return Indent.getNoneIndent(); 
118        } 
119        if (JSElementTypes.SOURCE_ELEMENTS.isInSet(child.getElementType())) { 
120          return Indent.getNormalIndent(); 
121        } 
122      } 
123   
124      if (node.getElementType() == JSElementTypes.WITH_STATEMENT && 
125          JSElementTypes.SOURCE_ELEMENTS.isInSet(child.getElementType())) { 
126        return Indent.getNormalIndent(); 
127      } 
128   
129      if (node.getElementType() == JSElementTypes.DOWHILE_STATEMENT && child.getElementType() == JSTokenTypes.WHILE_KEYWORD) { 
130        return Indent.getNoneIndent(); 
131      } 
132   
133      if (node.getElementType() == JSElementTypes.TRY_STATEMENT && child.getElementType() == JSTokenTypes.FINALLY_KEYWORD) { 
134        return Indent.getNoneIndent(); 
135      } 
136   
137      if (node.getElementType() == JSElementTypes.BLOCK) { 
138        final ASTNode parent = node.getTreeParent(); 
139        if (parent != null && parent.getElementType() == JSElementTypes.FUNCTION_DECLARATION && 
140          mySettings.METHOD_BRACE_STYLE == CodeStyleSettings.NEXT_LINE_SHIFTED) { 
141          return Indent.getNoneIndent(); 
142        } 
143        if (mySettings.BRACE_STYLE == CodeStyleSettings.NEXT_LINE_SHIFTED) { 
144          return Indent.getNoneIndent(); 
145        } 
146        if (JSElementTypes.SOURCE_ELEMENTS.isInSet(child.getElementType()) || 
147            JSTokenTypes.COMMENTS.isInSet(child.getElementType())) { 
148          return Indent.getNormalIndent(); 
149        } 
150        return Indent.getNoneIndent(); 
151      } 
152      else if (node.getPsi() instanceof JSLoopStatement) { 
153        if (child.getPsi() == ((JSLoopStatement)node.getPsi()).getBody()) { 
154          if (child.getElementType() == JSElementTypes.BLOCK) { 
155            return Indent.getNoneIndent(); 
156          } else { 
157            return Indent.getNormalIndent(); 
158          } 
159        } 
160      } 
161   
162      if (JSTokenTypes.COMMENTS.isInSet(child.getElementType()) || 
163          child.getElementType() == JSElementTypes.OBJECT_LITERAL_EXPRESSION) { 
164        return Indent.getNoneIndent(); 
165      } 
166   
167      if (node.getElementType() == JSElementTypes.OBJECT_LITERAL_EXPRESSION) { 
168        if (child.getElementType() == JSTokenTypes.LBRACE || 
169          child.getElementType() == JSTokenTypes.RBRACE) { 
170          return Indent.getNoneIndent(); 
171        } 
172        return Indent.getNormalIndent(); 
173      } 
174      return null; 
175    } 
176   
177    private Alignment alignmentProjection(final Alignment defaultAlignment, final ASTNode parent, final ASTNode child) { 
178      if (parent.getElementType() == JSElementTypes.FOR_STATEMENT && 
179          (JSElementTypes.EXPRESSIONS.isInSet(child.getElementType()) || 
180          child.getElementType() == JSElementTypes.VAR_STATEMENT)) { 
181        return defaultAlignment; 
182      } 
183      else if (parent.getElementType() == JSElementTypes.PARAMETER_LIST && 
184               child.getElementType() == JSElementTypes.FORMAL_PARAMETER) { 
185        return defaultAlignment; 
186      } 
187      else if (parent.getPsi() instanceof JSBinaryExpression && 
188               JSElementTypes.EXPRESSIONS.isInSet(child.getElementType())) { 
189        return defaultAlignment; 
190      } 
191      else if (parent.getElementType() == JSElementTypes.CONDITIONAL_EXPRESSION && 
192               JSElementTypes.EXPRESSIONS.isInSet(child.getElementType())) { 
193        return defaultAlignment; 
194      } 
195   
196      return null; 
197    } 
198   
199    private Wrap getWrap(final ASTNode node, final ASTNode child) { 
200      WrapType wrapType = null; 
201      if (node.getElementType() ==  JSElementTypes.ASSIGNMENT_EXPRESSION) { 
202        final JSAssignmentExpression assignment = (JSAssignmentExpression)node.getPsi(); 
203        if (child.getElementType() == assignment.getOperationSign() && mySettings.PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE || 
204            child.getPsi() == assignment.getROperand() && !mySettings.PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE) { 
205          wrapType = Util.getWrapType(mySettings.ASSIGNMENT_WRAP); 
206        } 
207      } else if (node.getElementType() ==  JSElementTypes.BINARY_EXPRESSION) { 
208        final JSBinaryExpression binary = (JSBinaryExpression)node.getPsi(); 
209        if (child.getElementType() == binary.getOperationSign() && mySettings.BINARY_OPERATION_SIGN_ON_NEXT_LINE || 
210            child.getPsi() == binary.getROperand() && !mySettings.BINARY_OPERATION_SIGN_ON_NEXT_LINE) { 
211          wrapType = Util.getWrapType(mySettings.BINARY_OPERATION_WRAP); 
212        } 
213      } else if (node.getElementType() ==  JSElementTypes.PARENTHESIZED_EXPRESSION) { 
214        if (child == node.findChildByType(JSTokenTypes.LPAR) && mySettings.PARENTHESES_EXPRESSION_LPAREN_WRAP) { 
215          wrapType = Wrap.NORMAL; 
216        } else if (child == node.findChildByType(JSTokenTypes.RPAR) && mySettings.PARENTHESES_EXPRESSION_RPAREN_WRAP) { 
217          wrapType = Wrap.ALWAYS; 
218        } 
219      } else if (node.getElementType() ==  JSElementTypes.ARRAY_LITERAL_EXPRESSION) { 
220        if (child == node.findChildByType(JSTokenTypes.LBRACE) && mySettings.ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE) { 
221          wrapType = Wrap.NORMAL; 
222        } else if (child == node.findChildByType(JSTokenTypes.RPAR) && mySettings.ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE) { 
223          wrapType = Wrap.ALWAYS; 
224        } 
225      } else if (node.getElementType() ==  JSElementTypes.CONDITIONAL_EXPRESSION) { 
226        final IElementType elementType = child.getElementType(); 
227        if ((mySettings.TERNARY_OPERATION_SIGNS_ON_NEXT_LINE && (elementType == JSTokenTypes.QUEST || elementType == JSTokenTypes.COLON)) || 
228            (!mySettings.TERNARY_OPERATION_SIGNS_ON_NEXT_LINE && child.getPsi() instanceof JSExpression)) { 
229          wrapType = Util.getWrapType(mySettings.TERNARY_OPERATION_WRAP); 
230        } 
231      } else if (node.getElementType() ==  JSElementTypes.CALL_EXPRESSION) { 
232        if (child == node.findChildByType(JSTokenTypes.LPAR) && mySettings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE) { 
233          wrapType = Wrap.NORMAL; 
234        } else if (child == node.findChildByType(JSTokenTypes.RPAR) && mySettings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE) { 
235          wrapType = Wrap.ALWAYS; 
236        } 
237      } 
238   
239      return wrapType == null ? null : Wrap.createWrap(wrapType, false); 
240    } 
241  } 
242