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