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