| /******************************************************************************* |
| * Copyright (c) 2009, 2012 xored software, Inc., NumberFour AG and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * xored software, Inc. - initial API and Implementation (Vladimir Belov) |
| * NumberFour AG - miscellaneous improvements (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.javascript.formatter.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.eclipse.dltk.ast.ASTNode; |
| import org.eclipse.dltk.formatter.AbstractFormatterNodeBuilder; |
| import org.eclipse.dltk.formatter.FormatterBlockNode; |
| import org.eclipse.dltk.formatter.FormatterEmptyNode; |
| import org.eclipse.dltk.formatter.FormatterIndentedBlockNode; |
| import org.eclipse.dltk.formatter.FormatterUtils; |
| import org.eclipse.dltk.formatter.IFormatterContainerNode; |
| import org.eclipse.dltk.formatter.IFormatterDocument; |
| import org.eclipse.dltk.formatter.IFormatterNode; |
| import org.eclipse.dltk.formatter.IFormatterTextNode; |
| import org.eclipse.dltk.javascript.ast.ASTVisitor; |
| import org.eclipse.dltk.javascript.ast.Argument; |
| import org.eclipse.dltk.javascript.ast.ArrayInitializer; |
| import org.eclipse.dltk.javascript.ast.AsteriskExpression; |
| import org.eclipse.dltk.javascript.ast.BinaryOperation; |
| import org.eclipse.dltk.javascript.ast.BooleanLiteral; |
| import org.eclipse.dltk.javascript.ast.BreakStatement; |
| import org.eclipse.dltk.javascript.ast.CallExpression; |
| import org.eclipse.dltk.javascript.ast.CaseClause; |
| import org.eclipse.dltk.javascript.ast.CatchClause; |
| import org.eclipse.dltk.javascript.ast.CommaExpression; |
| import org.eclipse.dltk.javascript.ast.ConditionalOperator; |
| import org.eclipse.dltk.javascript.ast.ConstStatement; |
| import org.eclipse.dltk.javascript.ast.ContinueStatement; |
| import org.eclipse.dltk.javascript.ast.DecimalLiteral; |
| import org.eclipse.dltk.javascript.ast.DefaultClause; |
| import org.eclipse.dltk.javascript.ast.DefaultXmlNamespaceStatement; |
| import org.eclipse.dltk.javascript.ast.DoWhileStatement; |
| import org.eclipse.dltk.javascript.ast.EmptyExpression; |
| import org.eclipse.dltk.javascript.ast.EmptyStatement; |
| import org.eclipse.dltk.javascript.ast.Expression; |
| import org.eclipse.dltk.javascript.ast.FinallyClause; |
| import org.eclipse.dltk.javascript.ast.ForEachInStatement; |
| import org.eclipse.dltk.javascript.ast.ForInStatement; |
| import org.eclipse.dltk.javascript.ast.ForStatement; |
| import org.eclipse.dltk.javascript.ast.FunctionStatement; |
| import org.eclipse.dltk.javascript.ast.GetAllChildrenExpression; |
| import org.eclipse.dltk.javascript.ast.GetArrayItemExpression; |
| import org.eclipse.dltk.javascript.ast.GetLocalNameExpression; |
| import org.eclipse.dltk.javascript.ast.GetMethod; |
| import org.eclipse.dltk.javascript.ast.ISemicolonStatement; |
| import org.eclipse.dltk.javascript.ast.IVariableStatement; |
| import org.eclipse.dltk.javascript.ast.Identifier; |
| import org.eclipse.dltk.javascript.ast.IfStatement; |
| import org.eclipse.dltk.javascript.ast.JSNode; |
| import org.eclipse.dltk.javascript.ast.Keyword; |
| import org.eclipse.dltk.javascript.ast.LabelledStatement; |
| import org.eclipse.dltk.javascript.ast.Method; |
| import org.eclipse.dltk.javascript.ast.NewExpression; |
| import org.eclipse.dltk.javascript.ast.NullExpression; |
| import org.eclipse.dltk.javascript.ast.ObjectInitializer; |
| import org.eclipse.dltk.javascript.ast.ParenthesizedExpression; |
| import org.eclipse.dltk.javascript.ast.PropertyExpression; |
| import org.eclipse.dltk.javascript.ast.PropertyInitializer; |
| import org.eclipse.dltk.javascript.ast.RegExpLiteral; |
| import org.eclipse.dltk.javascript.ast.ReturnStatement; |
| import org.eclipse.dltk.javascript.ast.Script; |
| import org.eclipse.dltk.javascript.ast.SetMethod; |
| import org.eclipse.dltk.javascript.ast.Statement; |
| import org.eclipse.dltk.javascript.ast.StatementBlock; |
| import org.eclipse.dltk.javascript.ast.StringLiteral; |
| import org.eclipse.dltk.javascript.ast.SwitchComponent; |
| import org.eclipse.dltk.javascript.ast.SwitchStatement; |
| import org.eclipse.dltk.javascript.ast.ThisExpression; |
| import org.eclipse.dltk.javascript.ast.ThrowStatement; |
| import org.eclipse.dltk.javascript.ast.TryStatement; |
| import org.eclipse.dltk.javascript.ast.UnaryOperation; |
| import org.eclipse.dltk.javascript.ast.VariableDeclaration; |
| import org.eclipse.dltk.javascript.ast.VariableStatement; |
| import org.eclipse.dltk.javascript.ast.VoidExpression; |
| import org.eclipse.dltk.javascript.ast.WhileStatement; |
| import org.eclipse.dltk.javascript.ast.WithStatement; |
| import org.eclipse.dltk.javascript.ast.XmlAttributeIdentifier; |
| import org.eclipse.dltk.javascript.ast.XmlLiteral; |
| import org.eclipse.dltk.javascript.ast.YieldOperator; |
| import org.eclipse.dltk.javascript.formatter.JavaScriptFormatterConstants; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ArrayBracketsConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.BinaryOperationPinctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.BlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.BracesNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.BracketsNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.CallExpressionPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.CallParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.CaseBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.CatchBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.CatchParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ColonNodeWrapper; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.CommaPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ConditionalOperatorPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.DoLoopWhileWrapper; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.DoWhileBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ElseBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ElseIfBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ElseIfElseBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.EmptyArrayBracketsConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.EmptyObjectInitializerBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ExpressionParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FinallyBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ForEmptySemicolonPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ForParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ForSemicolonPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterBinaryOperationNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterBreakNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterCaseNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterCatchClauseNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterContinueNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterElseIfNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterElseKeywordNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterElseNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterFinallyClauseNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterForInStatementNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterForStatementNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterFunctionNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterLabelledStatementNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterObjectInitializerNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterRootNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterScriptNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterStringNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterSwitchNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterThrowNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterVariableDeclarationNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterVoidExpressionNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FormatterYieldOperatorNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FunctionArgumentsParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FunctionArgumentsPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FunctionBodyBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FunctionExpressionBodyBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.FunctionNoArgumentsParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.GetItemArrayBracketsConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.IBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.IBracketsConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.IParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.IPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.IfConditionParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.LineBreakFormatterNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.MultiLineObjectInitializerBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.OperationOrPunctuationNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ParensNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.PropertyExpressionPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.PropertyInitializerPunctuationConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.SemicolonNode; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.SingleLineObjectInitializerBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.SpaceAfterKeyword; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.StatementBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.SwitchBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.SwitchConditionParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.ThenBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.TryBodyConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.WhileBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.WhileConditionParensConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.WithBlockBracesConfiguration; |
| import org.eclipse.dltk.javascript.formatter.internal.nodes.WithConditionParensConfiguration; |
| import org.eclipse.dltk.utils.IntList; |
| |
| public class FormatterNodeBuilder extends AbstractFormatterNodeBuilder { |
| |
| private static class XmlParensConfiguration implements IParensConfiguration { |
| public boolean getSpaceBeforeLeftParen() { |
| return false; |
| } |
| |
| public boolean getSpaceAfterLeftParen() { |
| return false; |
| } |
| |
| public boolean getSpaceBeforeRightParen() { |
| return false; |
| } |
| } |
| |
| protected final IFormatterDocument document; |
| |
| public FormatterNodeBuilder(IFormatterDocument document) { |
| this.document = document; |
| } |
| |
| private Stack<ASTNode> nodes = new Stack<ASTNode>(); |
| |
| @Override |
| protected void start(IFormatterContainerNode root) { |
| super.start(root); |
| nodes.clear(); |
| processed.clear(); |
| } |
| |
| private boolean isBlock(IFormatterContainerNode node) { |
| return node instanceof FormatterBlockNode |
| && !(node instanceof BracesNode) |
| && !(node instanceof ParensNode); |
| } |
| |
| private boolean isStatement(ASTNode node) { |
| if (isMultiLineObjectInitializerComponent(node)) { |
| return true; |
| } |
| if (node instanceof SwitchComponent) { |
| return true; |
| } |
| if (!(node instanceof Statement)) { |
| return false; |
| } |
| final JSNode statement = (JSNode) node; |
| final ASTNode parent = statement.getParent(); |
| if (parent instanceof ForStatement || parent instanceof IfStatement |
| || parent instanceof WhileStatement) { |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean isMultiLineObjectInitializerComponent(ASTNode node) { |
| if (node instanceof PropertyInitializer) { |
| final PropertyInitializer initializer = (PropertyInitializer) node; |
| if (initializer.getValue() instanceof FunctionStatement) { |
| return true; |
| } |
| } |
| if (node instanceof Method) { |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean isMultiLineObjectInitializer(ObjectInitializer initializer) { |
| for (ASTNode component : initializer.getInitializers()) { |
| if (isMultiLineObjectInitializerComponent(component)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private final Map<ASTNode, Boolean> processed = new IdentityHashMap<ASTNode, Boolean>(); |
| |
| @Override |
| protected void push(IFormatterContainerNode node) { |
| if (document |
| .getBoolean(JavaScriptFormatterConstants.STATEMENT_NEW_LINE) |
| && isBlock(node)) { |
| final ASTNode astNode = nodes.peek(); |
| if (isStatement(astNode) |
| && processed.put(astNode, Boolean.TRUE) == null) { |
| super.push(new LineBreakFormatterNode(node)); |
| return; |
| } |
| } |
| super.push(node); |
| } |
| |
| public IFormatterContainerNode build(Script astRoot) { |
| |
| final IFormatterContainerNode root = new FormatterRootNode(document); |
| start(root); |
| |
| astRoot.visitAll(new ASTVisitor<IFormatterNode>() { |
| |
| @Override |
| public IFormatterNode visit(ASTNode node) { |
| if (node instanceof Keyword) { |
| // for(each) *in* |
| // catch *if* |
| return addChild(createTextNode(document, node)); |
| } else if (node instanceof PropertyInitializer) { |
| nodes.push(node); |
| final IFormatterNode result = formatPropertyInitializer((PropertyInitializer) node); |
| nodes.pop(); |
| return result; |
| } else if (node instanceof GetMethod) { |
| nodes.push(node); |
| final IFormatterNode result = formatGetMethod((GetMethod) node); |
| nodes.pop(); |
| return result; |
| } else if (node instanceof SetMethod) { |
| nodes.push(node); |
| final IFormatterNode result = formatSetMethod((SetMethod) node); |
| nodes.pop(); |
| return result; |
| } else { |
| nodes.push(node); |
| final IFormatterNode result = super.visit(node); |
| nodes.pop(); |
| return result; |
| } |
| } |
| |
| @Override |
| public IFormatterNode visitArrayInitializer(ArrayInitializer node) { |
| |
| IBracketsConfiguration configuration; |
| if (node.getItems().size() > 0) |
| configuration = new ArrayBracketsConfiguration(document, |
| node); |
| else |
| configuration = new EmptyArrayBracketsConfiguration( |
| document, node); |
| |
| return processBrackets(node.getLB(), node.getRB(), |
| node.getItems(), node.getCommas(), configuration); |
| } |
| |
| @Override |
| public IFormatterNode visitBinaryOperation(BinaryOperation node) { |
| |
| FormatterBinaryOperationNode formatterNode = new FormatterBinaryOperationNode( |
| document); |
| |
| formatterNode.setBegin(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getLeftExpression()); |
| |
| skipSpaces(formatterNode, node.getOperationPosition()); |
| |
| processPunctuation(node.getOperationPosition(), node |
| .getOperationText().length(), |
| new BinaryOperationPinctuationConfiguration()); |
| |
| skipSpaces(formatterNode, node.getRightExpression() |
| .sourceStart()); |
| |
| visit(node.getRightExpression()); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitBooleanLiteral(BooleanLiteral node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitBreakStatement(BreakStatement node) { |
| |
| FormatterBreakNode formatterNode = new FormatterBreakNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getBreakKeyword())); |
| |
| push(formatterNode); |
| |
| if (node.getLabel() != null) { |
| addChild(new FormatterStringNode(document, node.getLabel())); |
| } |
| |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| @Override |
| public IFormatterNode visitCallExpression(CallExpression node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getExpression()); |
| |
| processParens(node.getLP(), node.getRP(), node.getArguments(), |
| new CallParensConfiguration(document), |
| node.getCommas(), |
| new CallExpressionPunctuationConfiguration(), node |
| .getArguments().size() > 1); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private IFormatterNode processSwitchComponent( |
| FormatterCaseNode caseNode, SwitchComponent node) { |
| if (node.getStatements().size() == 1 |
| && node.getStatements().get(0) instanceof StatementBlock) { |
| CaseBracesConfiguration configuration = new CaseBracesConfiguration( |
| document); |
| caseNode.setIndenting(false); |
| processBraces(node.getStatements().get(0), configuration); |
| } else { |
| visit(node.getStatements()); |
| } |
| checkedPop(caseNode, node.sourceEnd()); |
| return caseNode; |
| } |
| |
| private IFormatterNode visitCombinedNodeList( |
| List<? extends ASTNode> nodes, IntList punctuations, |
| List<IPunctuationConfiguration> configurations) { |
| |
| if (nodes.isEmpty()) |
| return null; |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| nodes.get(0).sourceStart())); |
| |
| push(formatterNode); |
| |
| for (int i = 0; i < nodes.size(); i++) { |
| visit(nodes.get(i)); |
| if (i < punctuations.size() && i + 1 < nodes.size()) { |
| int position = punctuations.get(i); |
| skipSpacesOnly(formatterNode, position); |
| processPunctuation(position, 1, configurations.get(i)); |
| skipSpacesOnly(formatterNode, nodes.get(i + 1) |
| .sourceStart()); |
| } |
| } |
| checkedPop(formatterNode, nodes.get(nodes.size() - 1) |
| .sourceEnd()); |
| return formatterNode; |
| } |
| |
| private IFormatterNode visitCombinedNodeList( |
| List<? extends ASTNode> nodes, IntList punctuations, |
| IPunctuationConfiguration configuration) { |
| return visitCombinedNodeList(nodes, punctuations, |
| Collections.nCopies(punctuations.size(), configuration)); |
| } |
| |
| @Override |
| public IFormatterNode visitCommaExpression(CommaExpression node) { |
| return visitCombinedNodeList(node.getItems(), node.getCommas(), |
| new CommaPunctuationConfiguration()); |
| } |
| |
| @Override |
| public IFormatterNode visitConditionalOperator( |
| ConditionalOperator node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getCondition()); |
| |
| skipSpaces(formatterNode, node.getQuestionPosition()); |
| processPunctuation(node.getQuestionPosition(), 1, |
| new ConditionalOperatorPunctuationConfiguration()); |
| skipSpaces(formatterNode, node.getTrueValue().sourceStart()); |
| |
| visit(node.getTrueValue()); |
| |
| skipSpaces(formatterNode, node.getColonPosition()); |
| processPunctuation(node.getColonPosition(), 1, |
| new ConditionalOperatorPunctuationConfiguration()); |
| skipSpaces(formatterNode, node.getFalseValue().sourceStart()); |
| |
| visit(node.getFalseValue()); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitConstDeclaration(ConstStatement node) { |
| |
| FormatterVariableDeclarationNode formatterNode = new FormatterVariableDeclarationNode( |
| document, isIndentVariableStatement(node)); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getConstKeyword())); |
| |
| push(formatterNode); |
| |
| processVariableDeclarations(node); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitContinueStatement(ContinueStatement node) { |
| |
| FormatterContinueNode formatterNode = new FormatterContinueNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getContinueKeyword())); |
| |
| push(formatterNode); |
| |
| if (node.getLabel() != null) { |
| addChild(new FormatterStringNode(document, node.getLabel())); |
| } |
| |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| private IFormatterNode processOptionalSemicolon( |
| IFormatterContainerNode formatterNode, |
| ISemicolonStatement node) { |
| int semicolonPosition = node.getSemicolonPosition(); |
| if (semicolonPosition > -1) { |
| checkedPop(formatterNode, semicolonPosition /*- 1*/); |
| if (semicolonPosition >= formatterNode.getEndOffset()) { |
| addChild(createSemicolonNode(document, |
| semicolonPosition)); |
| } |
| } else { |
| checkedPop(formatterNode, node.sourceEnd()); |
| } |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitDecimalLiteral(DecimalLiteral node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitDoWhileStatement(DoWhileStatement node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getDoKeyword())); |
| |
| push(formatterNode); |
| |
| processBraces(node.getBody(), |
| new DoWhileBlockBracesConfiguration(document)); |
| |
| formatterNode.addChild(new DoLoopWhileWrapper(createTextNode( |
| document, node.getWhileKeyword()))); |
| |
| processParens(node.getLP(), node.getRP(), node.getCondition(), |
| new WhileConditionParensConfiguration(document)); |
| |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| @Override |
| public IFormatterNode visitEmptyExpression(EmptyExpression node) { |
| // nothing |
| return null; |
| } |
| |
| @Override |
| public IFormatterNode visitForEachInStatement( |
| ForEachInStatement node) { |
| |
| FormatterForInStatementNode formatterNode = new FormatterForInStatementNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getForKeyword())); |
| |
| push(formatterNode); |
| |
| List<ASTNode> nodes = new ArrayList<ASTNode>(); |
| |
| nodes.add(node.getItem()); |
| nodes.add(node.getInKeyword()); |
| nodes.add(node.getIterator()); |
| |
| processParens(node.getLP(), node.getRP(), nodes, |
| new ForParensConfiguration(document)); |
| |
| if (node.getBody() != null) |
| processBraces(node.getBody(), new BlockBracesConfiguration( |
| document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitForInStatement(ForInStatement node) { |
| |
| FormatterForInStatementNode formatterNode = new FormatterForInStatementNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getForKeyword())); |
| |
| push(formatterNode); |
| |
| List<ASTNode> nodes = new ArrayList<ASTNode>(); |
| |
| nodes.add(node.getItem()); |
| nodes.add(node.getInKeyword()); |
| nodes.add(node.getIterator()); |
| |
| processParens(node.getLP(), node.getRP(), nodes, |
| new ForParensConfiguration(document)); |
| |
| if (node.getBody() != null) |
| processBraces(node.getBody(), new BlockBracesConfiguration( |
| document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitForStatement(ForStatement node) { |
| |
| FormatterForStatementNode formatterNode = new FormatterForStatementNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getForKeyword())); |
| |
| push(formatterNode); |
| |
| List<ASTNode> nodes = new ArrayList<ASTNode>(); |
| |
| nodes.add(node.getInitial()); |
| nodes.add(node.getCondition()); |
| nodes.add(node.getStep()); |
| |
| IntList semicolons = new IntList(); |
| semicolons.add(node.getInitialSemicolonPosition()); |
| semicolons.add(node.getConditionalSemicolonPosition()); |
| |
| List<IPunctuationConfiguration> semicolonConfigurations = new ArrayList<IPunctuationConfiguration>(); |
| |
| if (node.getCondition() instanceof EmptyExpression) |
| semicolonConfigurations |
| .add(new ForEmptySemicolonPunctuationConfiguration()); |
| else |
| semicolonConfigurations |
| .add(new ForSemicolonPunctuationConfiguration()); |
| |
| if (node.getStep() instanceof EmptyExpression) |
| semicolonConfigurations |
| .add(new ForEmptySemicolonPunctuationConfiguration()); |
| else |
| semicolonConfigurations |
| .add(new ForSemicolonPunctuationConfiguration()); |
| |
| processParens(node.getLP(), node.getRP(), nodes, |
| new ForParensConfiguration(document), semicolons, |
| semicolonConfigurations); |
| |
| if (node.getBody() != null) |
| processBraces(node.getBody(), new BlockBracesConfiguration( |
| document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitFunctionStatement(FunctionStatement node) { |
| |
| FormatterFunctionNode formatterNode = new FormatterFunctionNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getFunctionKeyword())); |
| |
| push(formatterNode); |
| |
| if (node.getName() != null) |
| visit(node.getName()); |
| |
| final IParensConfiguration parensConf; |
| if (node.getArguments().isEmpty()) { |
| parensConf = new FunctionNoArgumentsParensConfiguration( |
| document); |
| } else { |
| parensConf = new FunctionArgumentsParensConfiguration( |
| document); |
| } |
| final ParensNode parens = new ParensNode( |
| document, |
| parensConf, |
| false, |
| node.getName() == null |
| && document |
| .getBoolean(JavaScriptFormatterConstants.INSERT_SPACE_BEFORE_PARENS_ANONYMOUS_FUNCTION)); |
| parens.setBegin(createCharNode(document, node.getLP())); |
| push(parens); |
| if (!node.getArguments().isEmpty()) { |
| final Argument arg0 = node.getArguments().get(0); |
| skipSpaces(parens, arg0.sourceStart()); |
| } |
| for (Argument argument : node.getArguments()) { |
| visit(argument.getIdentifier()); |
| if (argument.getCommaPosition() != -1) { |
| int position = argument.getCommaPosition(); |
| skipSpacesOnly(parens, position); |
| processPunctuation(position, 1, |
| new FunctionArgumentsPunctuationConfiguration()); |
| } |
| } |
| checkedPop(parens, node.getRP()); |
| parens.setEnd(createCharNode(document, node.getRP())); |
| |
| boolean emptyBody = node.getBody() == null |
| || isEmptyBody(node.getBody()); |
| IBracesConfiguration bodyConfiguration; |
| |
| if (node.isDeclaration()) |
| bodyConfiguration = new FunctionBodyBracesConfiguration( |
| document, emptyBody); |
| else |
| bodyConfiguration = new FunctionExpressionBodyBracesConfiguration( |
| document, emptyBody); |
| |
| processBraces(node.getBody(), bodyConfiguration); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private boolean isEmptyBody(StatementBlock block) { |
| if (block.getStatements().isEmpty()) { |
| for (int i = block.getLC() + 1; i < block.getRC(); ++i) { |
| if (!Character.isWhitespace(document.charAt(i))) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private boolean isEmpty(ObjectInitializer initializer) { |
| if (initializer.getInitializers().isEmpty()) { |
| for (int i = initializer.getLC() + 1; i < initializer |
| .getRC(); ++i) { |
| if (!Character.isWhitespace(document.charAt(i))) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @Override |
| public IFormatterNode visitGetArrayItemExpression( |
| GetArrayItemExpression node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getArray()); |
| |
| processBrackets( |
| node.getLB(), |
| node.getRB(), |
| Collections.<Expression> singletonList(node.getIndex()), |
| IntList.EMPTY_LIST, |
| new GetItemArrayBracketsConfiguration(document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private IFormatterNode formatGetMethod(GetMethod node) { |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getGetKeyword())); |
| |
| push(formatterNode); |
| |
| visit(node.getName()); |
| |
| processParens(node.getLP(), node.getRP(), (ASTNode) null, |
| new FunctionNoArgumentsParensConfiguration(document)); |
| |
| boolean emptyBody = node.getBody() == null |
| || isEmptyBody(node.getBody()); |
| |
| processBraces( |
| node.getBody(), |
| new FunctionBodyBracesConfiguration(document, emptyBody)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitIdentifier(Identifier node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| private IFormatterNode processParens(int leftParen, int rightParen, |
| ASTNode expression, IParensConfiguration configuration) { |
| ParensNode parens = new ParensNode(document, configuration); |
| parens.setBegin(createCharNode(document, leftParen)); |
| push(parens); |
| if (expression != null) { |
| skipSpaces(parens, expression.sourceStart()); |
| visit(expression); |
| } |
| checkedPop(parens, rightParen); |
| parens.setEnd(createCharNode(document, rightParen)); |
| return parens; |
| } |
| |
| /** |
| * process function declaration parameters and call arguments |
| */ |
| private void processParens(int leftParen, int rightParen, |
| List<? extends ASTNode> expressions, |
| IParensConfiguration configuration, IntList punctuations, |
| IPunctuationConfiguration punctuationConfiguration, |
| boolean indenting) { |
| ParensNode parens = new ParensNode(document, configuration, |
| indenting); |
| parens.setBegin(createCharNode(document, leftParen)); |
| push(parens); |
| if (!expressions.isEmpty()) { |
| final ASTNode expression0 = expressions.get(0); |
| skipSpaces(parens, expression0.sourceStart()); |
| } |
| visitCombinedNodeList(expressions, punctuations, |
| punctuationConfiguration); |
| checkedPop(parens, rightParen); |
| parens.setEnd(createCharNode(document, rightParen)); |
| } |
| |
| private void processParens(int leftParen, int rightParen, |
| List<ASTNode> expressions, |
| IParensConfiguration configuration, IntList punctuations, |
| List<IPunctuationConfiguration> punctuationConfigurations) { |
| ParensNode parens = new ParensNode(document, configuration); |
| parens.setBegin(createCharNode(document, leftParen)); |
| push(parens); |
| if (!expressions.isEmpty()) { |
| final ASTNode expression0 = expressions.get(0); |
| skipSpaces(parens, expression0.sourceStart()); |
| } |
| visitCombinedNodeList(expressions, punctuations, |
| punctuationConfigurations); |
| checkedPop(parens, rightParen); |
| parens.setEnd(createCharNode(document, rightParen)); |
| } |
| |
| private void processParens(int leftParen, int rightParen, |
| List<ASTNode> expressions, |
| IParensConfiguration configuration) { |
| ParensNode parens = new ParensNode(document, configuration); |
| parens.setBegin(createCharNode(document, leftParen)); |
| push(parens); |
| if (!expressions.isEmpty()) { |
| final ASTNode expression0 = expressions.get(0); |
| skipSpaces(parens, expression0.sourceStart()); |
| } |
| visitNodeList(expressions); |
| checkedPop(parens, rightParen); |
| parens.setEnd(createCharNode(document, rightParen)); |
| } |
| |
| private void skipSpacesOnly(IFormatterContainerNode formatterNode, |
| int end) { |
| final int prev = formatterNode.getEndOffset(); |
| int pos = prev; |
| while (pos < end |
| && FormatterUtils.isSpace(document.charAt(pos))) { |
| ++pos; |
| } |
| if (pos > prev) { |
| formatterNode.addChild(createEmptyTextNode(document, pos)); |
| } |
| } |
| |
| private void skipSpaces(IFormatterContainerNode formatterNode, |
| int end) { |
| final int prev = formatterNode.getEndOffset(); |
| int pos = prev; |
| while (pos < end |
| && Character.isWhitespace(document.charAt(pos))) { |
| ++pos; |
| } |
| if (pos > prev) { |
| formatterNode.addChild(createEmptyTextNode(document, pos)); |
| } |
| } |
| |
| private void processPunctuation(int position, int length, |
| IPunctuationConfiguration configuration) { |
| addChild(new OperationOrPunctuationNode(createTextNode( |
| document, position, position + length), configuration)); |
| } |
| |
| private IFormatterNode processBraces(ASTNode node, |
| IBracesConfiguration configuration) { |
| if (node instanceof StatementBlock) { |
| StatementBlock block = (StatementBlock) node; |
| |
| if (block.getLC() > -1 && block.getRC() > -1) { |
| |
| BracesNode braces = new BracesNode(document, |
| configuration); |
| |
| braces.setBegin(createCharNode(document, block.getLC())); |
| push(braces); |
| skipSpacesOnly(braces, block.getRC()); |
| visitNodeList(block.getStatements()); |
| checkedPop(braces, block.getRC()); |
| braces.setEnd(createCharNode(document, block.getRC())); |
| return braces; |
| } else { |
| final FormatterBlockNode formatter = new FormatterIndentedBlockNode( |
| document, configuration.isIndenting()); |
| formatter.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| push(formatter); |
| visitNodeList(block.getStatements()); |
| checkedPop(formatter, node.sourceEnd()); |
| return formatter; |
| |
| } |
| } else { |
| final FormatterBlockNode block = new FormatterIndentedBlockNode( |
| document, configuration.isIndenting()); |
| block.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| push(block); |
| visit(node); |
| checkedPop(block, node.sourceEnd()); |
| return block; |
| } |
| } |
| |
| /** |
| * process array initialization |
| */ |
| private IFormatterNode processBrackets(int leftBracket, |
| int rightBracket, List<Expression> nodes, IntList commas, |
| IBracketsConfiguration configuration) { |
| BracketsNode brackets = new BracketsNode(document, |
| configuration); |
| |
| brackets.setBegin(createCharNode(document, leftBracket)); |
| push(brackets); |
| if (!nodes.isEmpty()) { |
| // TODO introduce option for: spaces after opening bracket |
| skipSpaces(brackets, nodes.get(0).sourceStart()); |
| } |
| if (!commas.isEmpty()) { |
| // TODO introduce option for spaces between omitted values |
| visitCombinedNodeList(nodes, commas, Collections.nCopies( |
| commas.size(), |
| (IPunctuationConfiguration) configuration)); |
| } else { |
| visitCombinedNodeList(nodes, commas, |
| Collections.<IPunctuationConfiguration> emptyList()); |
| } |
| if (!nodes.isEmpty()) { |
| // TODO introduce option for: spaces before closing bracket |
| skipSpaces(brackets, rightBracket); |
| } |
| checkedPop(brackets, rightBracket); |
| brackets.setEnd(createCharNode(document, rightBracket)); |
| return brackets; |
| } |
| |
| private void processElseIf(ASTNode node, |
| IBracesConfiguration configuration) { |
| BracesNode braces = new BracesNode(document, configuration); |
| braces.setBegin(createEmptyTextNode(document, |
| node.sourceStart())); |
| push(braces); |
| visit(node); |
| checkedPop(braces, node.sourceEnd()); |
| } |
| |
| @Override |
| public IFormatterNode visitIfStatement(IfStatement node) { |
| final FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getIfKeyword())); |
| push(formatterNode); |
| |
| processParens(node.getLP(), node.getRP(), node.getCondition(), |
| new IfConditionParensConfiguration(document)); |
| |
| if (node.getThenStatement() != null) { |
| final IBracesConfiguration thenConf; |
| if (node.getElseStatement() != null) |
| thenConf = new ThenBlockBracesConfiguration(document); |
| else |
| thenConf = new BlockBracesConfiguration(document); |
| processBraces(node.getThenStatement(), thenConf); |
| checkedPop(formatterNode, node.getThenStatement() |
| .sourceEnd()); |
| } else { |
| checkedPop(formatterNode, node.sourceEnd()); |
| } |
| |
| if (node.getElseStatement() != null) { |
| |
| boolean lineBreakBeforeElse = node.getThenStatement() == null |
| || !(node.getThenStatement() instanceof StatementBlock); |
| |
| IBracesConfiguration elseConfiguration; |
| FormatterElseNode elseNode = null; |
| |
| if (node.getElseStatement() instanceof IfStatement) { |
| IfStatement elseStatement = (IfStatement) node |
| .getElseStatement(); |
| |
| if (elseStatement.getElseStatement() == null) { |
| elseConfiguration = new ElseIfBlockBracesConfiguration( |
| document); |
| } else { |
| elseConfiguration = new ElseIfElseBlockBracesConfiguration( |
| document); |
| } |
| elseNode = new FormatterElseIfNode(document, |
| lineBreakBeforeElse); |
| } else { |
| elseConfiguration = new ElseBlockBracesConfiguration( |
| document); |
| elseNode = new FormatterElseNode(document, |
| lineBreakBeforeElse); |
| } |
| |
| elseNode.addChild(new FormatterElseKeywordNode(document, |
| node.getElseKeyword().sourceStart(), node |
| .getElseKeyword().sourceEnd())); |
| |
| push(elseNode); |
| |
| if (node.getElseStatement() instanceof IfStatement) |
| processElseIf(node.getElseStatement(), |
| elseConfiguration); |
| else |
| processBraces(node.getElseStatement(), |
| elseConfiguration); |
| |
| checkedPop(elseNode, node.getElseStatement().sourceEnd()); |
| } |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitLabelledStatement(LabelledStatement node) { |
| |
| FormatterLabelledStatementNode formatterNode = new FormatterLabelledStatementNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, node.getLabel())); |
| formatterNode.addChild(createCharNode(document, |
| node.getColonPosition())); |
| |
| push(formatterNode); |
| |
| // TODO introduce options for it |
| processBraces(node.getStatement(), new CaseBracesConfiguration( |
| document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitNewExpression(NewExpression node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| formatterNode.addChild(createTextNode(document, |
| node.getNewKeyword())); |
| push(formatterNode); |
| visit(node.getObjectClass()); |
| checkedPop(formatterNode, node.sourceEnd()); |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitNullExpression(NullExpression node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitObjectInitializer(ObjectInitializer node) { |
| |
| final IBracesConfiguration configuration; |
| |
| if (isEmpty(node)) { |
| configuration = new EmptyObjectInitializerBracesConfiguration( |
| document); |
| } else if (node.isMultiline() |
| || document |
| .getBoolean(JavaScriptFormatterConstants.STATEMENT_NEW_LINE) |
| && isMultiLineObjectInitializer(node)) |
| configuration = new MultiLineObjectInitializerBracesConfiguration( |
| document, node); |
| else |
| configuration = new SingleLineObjectInitializerBracesConfiguration( |
| document); |
| |
| FormatterObjectInitializerNode formatterNode = new FormatterObjectInitializerNode( |
| document, configuration); |
| |
| formatterNode.setBegin(createCharNode(document, node.getLC())); |
| |
| push(formatterNode); |
| skipSpacesOnly(formatterNode, node.getRC()); |
| |
| visitCombinedNodeList(node.getInitializers(), node.getCommas(), |
| new PropertyInitializerPunctuationConfiguration()); |
| |
| checkedPop(formatterNode, node.sourceEnd() - 1); |
| |
| formatterNode.setEnd(createCharNode(document, node.getRC())); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitParenthesizedExpression( |
| ParenthesizedExpression node) { |
| final IParensConfiguration conf = isInXml(node) ? new XmlParensConfiguration() |
| : new ExpressionParensConfiguration(document); |
| return processParens(node.getLP(), node.getRP(), |
| node.getExpression(), conf); |
| } |
| |
| private boolean isInXml(ParenthesizedExpression node) { |
| return node.getParent() instanceof PropertyExpression |
| && ((PropertyExpression) node.getParent()) |
| .getProperty() == node; |
| } |
| |
| @Override |
| public IFormatterNode visitPropertyExpression( |
| PropertyExpression node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getObject()); |
| |
| skipSpaces(formatterNode, node.getDotPosition()); |
| |
| processPunctuation(node.getDotPosition(), 1, |
| new PropertyExpressionPunctuationConfiguration()); |
| |
| skipSpaces(formatterNode, node.getProperty().sourceStart()); |
| |
| visit(node.getProperty()); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private IFormatterNode formatPropertyInitializer( |
| PropertyInitializer node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getName()); |
| |
| skipSpaces(formatterNode, node.getColon()); |
| |
| processPunctuation(node.getColon(), 1, |
| new PropertyInitializerPunctuationConfiguration()); |
| |
| skipSpaces(formatterNode, node.getValue().sourceStart()); |
| |
| visit(node.getValue()); |
| |
| checkedPop(formatterNode, node.getValue().sourceStart()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitRegExpLiteral(RegExpLiteral node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitReturnStatement(ReturnStatement node) { |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| final IFormatterTextNode keyword = createTextNode(document, |
| node.getReturnKeyword()); |
| formatterNode.addChild(node.getValue() != null ? new SpaceAfterKeyword( |
| keyword) : keyword); |
| |
| push(formatterNode); |
| |
| if (node.getValue() != null) |
| visit(node.getValue()); |
| |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| @Override |
| public IFormatterNode visitEmptyStatement(EmptyStatement node) { |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| push(formatterNode); |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| @Override |
| public IFormatterNode visitScript(Script node) { |
| FormatterScriptNode scriptNode = new FormatterScriptNode( |
| document); |
| |
| push(scriptNode); |
| |
| visitNodeList(node.getStatements()); |
| |
| checkedPop(scriptNode, node.sourceEnd()); |
| |
| return scriptNode; |
| } |
| |
| private IFormatterNode formatSetMethod(SetMethod node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getSetKeyword())); |
| |
| push(formatterNode); |
| |
| visit(node.getName()); |
| |
| processParens(node.getLP(), node.getRP(), node.getArgument(), |
| new FunctionArgumentsParensConfiguration(document)); |
| |
| boolean emptyBody = node.getBody() == null |
| || isEmptyBody(node.getBody()); |
| |
| processBraces( |
| node.getBody(), |
| new FunctionBodyBracesConfiguration(document, emptyBody)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitStatementBlock(StatementBlock node) { |
| return processBraces(node, |
| new StatementBlockBracesConfiguration(document)); |
| } |
| |
| @Override |
| public IFormatterNode visitStringLiteral(StringLiteral node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitSwitchStatement(SwitchStatement node) { |
| |
| FormatterSwitchNode switchNode = new FormatterSwitchNode( |
| document); |
| |
| switchNode.setBegin(createTextNode(document, |
| node.getSwitchKeyword())); |
| |
| push(switchNode); |
| |
| processParens(node.getLP(), node.getRP(), node.getCondition(), |
| new SwitchConditionParensConfiguration(document)); |
| BracesNode braces = new BracesNode(document, |
| new SwitchBracesConfiguration(document)); |
| |
| braces.setBegin(createCharNode(document, node.getLC())); |
| push(braces); |
| for (SwitchComponent component : node.getCaseClauses()) { |
| nodes.push(component); |
| if (component instanceof CaseClause) { |
| final CaseClause caseClause = (CaseClause) component; |
| FormatterCaseNode caseNode = new FormatterCaseNode( |
| document); |
| caseNode.setBegin(new SpaceAfterKeyword(createTextNode( |
| document, caseClause.getKeyword()))); |
| push(caseNode); |
| visit(caseClause.getCondition()); |
| caseNode.addChild(new ColonNodeWrapper(createCharNode( |
| document, caseClause.getColonPosition()))); |
| processSwitchComponent(caseNode, caseClause); |
| } else { |
| final DefaultClause defaultClause = (DefaultClause) component; |
| FormatterCaseNode defaultNode = new FormatterCaseNode( |
| document); |
| defaultNode.setBegin(createTextNode(document, |
| defaultClause.getKeyword())); |
| push(defaultNode); |
| defaultNode.addChild(new ColonNodeWrapper( |
| createCharNode(document, |
| defaultClause.getColonPosition()))); |
| processSwitchComponent(defaultNode, defaultClause); |
| } |
| nodes.pop(); |
| } |
| checkedPop(braces, node.getRC()); |
| braces.setEnd(createCharNode(document, node.getRC())); |
| |
| checkedPop(switchNode, node.sourceEnd()); |
| |
| return switchNode; |
| } |
| |
| @Override |
| public IFormatterNode visitThisExpression(ThisExpression node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitThrowStatement(ThrowStatement node) { |
| |
| FormatterThrowNode formatterNode = new FormatterThrowNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getThrowKeyword())); |
| |
| push(formatterNode); |
| |
| if (node.getException() != null) |
| visit(node.getException()); |
| |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| @Override |
| public IFormatterNode visitTryStatement(TryStatement node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getTryKeyword())); |
| |
| push(formatterNode); |
| |
| processBraces(node.getBody(), |
| new TryBodyConfiguration(document)); |
| |
| for (CatchClause catchClause : node.getCatches()) { |
| processCatch(catchClause); |
| } |
| if (node.getFinally() != null) { |
| processFinally(node.getFinally()); |
| } |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private void processCatch(CatchClause catchClause) { |
| FormatterCatchClauseNode formatterNode = new FormatterCatchClauseNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| catchClause.getCatchKeyword())); |
| |
| push(formatterNode); |
| |
| List<ASTNode> exceptionNodes = new ArrayList<ASTNode>(); |
| exceptionNodes.add(catchClause.getException()); |
| if (catchClause.getIfKeyword() != null) { |
| exceptionNodes.add(catchClause.getIfKeyword()); |
| } |
| if (catchClause.getFilterExpression() != null) { |
| exceptionNodes.add(catchClause.getFilterExpression()); |
| } |
| |
| processParens(catchClause.getLP(), catchClause.getRP(), |
| exceptionNodes, new CatchParensConfiguration(document)); |
| |
| processBraces(catchClause.getStatement(), |
| new CatchBracesConfiguration(document)); |
| |
| checkedPop(formatterNode, catchClause.sourceEnd()); |
| } |
| |
| private void processFinally(FinallyClause node) { |
| FormatterFinallyClauseNode formatterNode = new FormatterFinallyClauseNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getFinallyKeyword())); |
| |
| push(formatterNode); |
| |
| processBraces(node.getStatement(), |
| new FinallyBracesConfiguration(document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| } |
| |
| @Override |
| public IFormatterNode visitUnaryOperation(UnaryOperation node) { |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| if (!node.isPostfix()) { |
| addChild(createTextNode(document, |
| node.getOperationPosition(), |
| node.getOperationPosition() |
| + node.getOperationText().length())); |
| if (!node.isTextOperator()) { |
| skipSpaces(formatterNode, node.getExpression() |
| .sourceStart()); |
| } |
| } |
| |
| visit(node.getExpression()); |
| |
| if (node.isPostfix()) { |
| skipSpaces(formatterNode, node.getOperationPosition()); |
| addChild(createTextNode(document, |
| node.getOperationPosition(), |
| node.getOperationPosition() |
| + node.getOperationText().length())); |
| } |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitVariableStatement(VariableStatement node) { |
| FormatterVariableDeclarationNode formatterNode = new FormatterVariableDeclarationNode( |
| document, isIndentVariableStatement(node)); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getVarKeyword())); |
| |
| push(formatterNode); |
| |
| processVariableDeclarations(node); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private boolean isIndentVariableStatement(IVariableStatement node) { |
| if (node.getVariables().size() == 1) { |
| final Expression expression = node.getVariables().get(0) |
| .getInitializer(); |
| if (expression instanceof FunctionStatement |
| || expression instanceof ObjectInitializer |
| || (expression instanceof NewExpression && ((NewExpression) expression) |
| .getObjectClass() instanceof FunctionStatement) |
| || expression instanceof CallExpression) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void processVariableDeclarations(IVariableStatement node) { |
| final List<VariableDeclaration> vars = node.getVariables(); |
| if (vars.isEmpty()) { |
| return; |
| } |
| final FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| formatterNode.addChild(createEmptyTextNode(document, vars |
| .get(0).sourceStart())); |
| push(formatterNode); |
| for (VariableDeclaration var : vars) { |
| visit(var.getIdentifier()); |
| if (var.getInitializer() != null) { |
| int position = var.getAssignPosition(); |
| skipSpaces(formatterNode, position); |
| processPunctuation(position, 1, |
| new BinaryOperationPinctuationConfiguration()); |
| visit(var.getInitializer()); |
| } |
| if (var.getCommaPosition() != -1) { |
| int position = var.getCommaPosition(); |
| skipSpacesOnly(formatterNode, position); |
| processPunctuation(position, 1, |
| new CommaPunctuationConfiguration()); |
| } |
| } |
| checkedPop(formatterNode, vars.get(vars.size() - 1).sourceEnd()); |
| } |
| |
| @Override |
| public IFormatterNode visitVoidExpression(VoidExpression node) { |
| FormatterVoidExpressionNode formatterNode = new FormatterVoidExpressionNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getExpression()); |
| |
| return processOptionalSemicolon(formatterNode, node); |
| } |
| |
| @Override |
| public IFormatterNode visitWhileStatement(WhileStatement node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getWhileKeyword())); |
| |
| push(formatterNode); |
| |
| processParens(node.getLP(), node.getRP(), node.getCondition(), |
| new WhileConditionParensConfiguration(document)); |
| |
| if (node.getBody() != null) { |
| processBraces(node.getBody(), |
| new WhileBlockBracesConfiguration(document)); |
| } |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitWithStatement(WithStatement node) { |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createTextNode(document, |
| node.getWithKeyword())); |
| |
| push(formatterNode); |
| |
| processParens(node.getLP(), node.getRP(), node.getExpression(), |
| new WithConditionParensConfiguration(document)); |
| |
| processBraces(node.getStatement(), |
| new WithBlockBracesConfiguration(document)); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| private void visitNodeList(List<? extends ASTNode> nodes) { |
| for (int i = 0; i < nodes.size(); i++) { |
| visit(nodes.get(i)); |
| } |
| } |
| |
| @Override |
| public IFormatterNode visitYieldOperator(YieldOperator node) { |
| FormatterYieldOperatorNode formatterNode = new FormatterYieldOperatorNode( |
| document); |
| |
| formatterNode.setBegin(createTextNode(document, |
| node.getYieldKeyword())); |
| |
| push(formatterNode); |
| |
| visit(node.getExpression()); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitXmlLiteral(XmlLiteral node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitDefaultXmlNamespace( |
| DefaultXmlNamespaceStatement node) { |
| |
| FormatterBlockNode formatter = new FormatterBlockNode(document); |
| |
| formatter.addChild(createTextNode(document, |
| node.getDefaultKeyword())); |
| |
| push(formatter); |
| |
| addChild(createTextNode(document, node.getXmlKeyword())); |
| addChild(createTextNode(document, node.getNamespaceKeyword())); |
| visit(node.getValue()); |
| |
| checkedPop(formatter, node.sourceEnd()); |
| |
| return formatter; |
| } |
| |
| @Override |
| public IFormatterNode visitXmlPropertyIdentifier( |
| XmlAttributeIdentifier node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitAsteriskExpression( |
| AsteriskExpression node) { |
| return addChild(new FormatterStringNode(document, node)); |
| } |
| |
| @Override |
| public IFormatterNode visitGetLocalNameExpression( |
| GetLocalNameExpression node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getNamespace()); |
| visit(node.getLocalName()); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| @Override |
| public IFormatterNode visitGetAllChildrenExpression( |
| GetAllChildrenExpression node) { |
| |
| FormatterBlockNode formatterNode = new FormatterBlockNode( |
| document); |
| |
| formatterNode.addChild(createEmptyTextNode(document, |
| node.sourceStart())); |
| |
| push(formatterNode); |
| |
| visit(node.getObject()); |
| visit(node.getProperty()); |
| |
| checkedPop(formatterNode, node.sourceEnd()); |
| |
| return formatterNode; |
| } |
| |
| }); |
| |
| checkedPop(root, document.getLength()); |
| return root; |
| |
| } |
| |
| private IFormatterTextNode createTextNode(IFormatterDocument document, |
| ASTNode node) { |
| return createTextNode(document, node.sourceStart(), node.sourceEnd()); |
| } |
| |
| private IFormatterTextNode createCharNode(IFormatterDocument document, |
| int startPos) { |
| return createTextNode(document, startPos, startPos + 1); |
| } |
| |
| private IFormatterTextNode createEmptyTextNode(IFormatterDocument document, |
| int pos) { |
| return new FormatterEmptyNode(document, pos); |
| } |
| |
| private IFormatterTextNode createSemicolonNode(IFormatterDocument document, |
| int offset) { |
| return new SemicolonNode(document, offset); |
| } |
| |
| } |