blob: 8e5d43cdac0544b1affd9b1e39148a55b9d2e7bb [file] [log] [blame]
/*******************************************************************************
* 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);
}
}