/** * Aptana Studio * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.editor.php.formatter; import java.util.List; import org2.eclipse.php.internal.core.ast.nodes.Comment; import org2.eclipse.php.internal.core.ast.nodes.Program; import com.aptana.editor.php.formatter.nodes.FormatterPHPRootNode; import com.aptana.editor.php.internal.indexer.PHPDocUtils; import com.aptana.editor.php.internal.parser.nodes.PHPASTWrappingNode; import com.aptana.formatter.FormatterDocument; import com.aptana.formatter.nodes.AbstractFormatterNodeBuilder; import com.aptana.formatter.nodes.IFormatterContainerNode; import com.aptana.parsing.ast.IParseNode; import com.aptana.parsing.ast.ParseRootNode; /** * PHP formatter node builder.<br> * This builder generates the formatter nodes that will then be processed by the {@link PHPFormatterNodeRewriter} to * produce the output for the code formatting process. * * @author Shalom Gibly <sgibly@aptana.com> */ public class PHPFormatterNodeBuilder extends AbstractFormatterNodeBuilder { private boolean hasErrors; /** * @param parseResult * @param document * @return */ public IFormatterContainerNode build(IParseNode parseResult, FormatterDocument document) { final IFormatterContainerNode rootNode = new FormatterPHPRootNode(document); start(rootNode); ParseRootNode phpRootNode = (ParseRootNode) parseResult; // the root node should hold a single Program (AST) that was inserted as a ParseNode. if (phpRootNode.getChildCount() == 1) { IParseNode child = phpRootNode.getChild(0); if (child instanceof PHPASTWrappingNode) { Program ast = ((PHPASTWrappingNode) child).getAST(); PHPFormatterVisitor visitor = new PHPFormatterVisitor(document, this, ast.comments()); ast.accept(visitor); setOffOnRegions(visitor.getOnOffRegions()); } } checkedPop(rootNode, document.getLength()); return rootNode; } /** * @return True, in case the AST contains errors; False, otherwise. * @see #setHasErrors(boolean) */ public boolean hasErrors() { return hasErrors; } /** * Set the error-state of the AST. * * @param hasErrors * @see #hasErrors */ public void setHasErrors(boolean hasErrors) { this.hasErrors = hasErrors; } /** * Try to locate the given char by traversing backwards on the given document from the start offset.<br> * In case no match is located, the original start offset is returned. This function skips any char that is detected * inside a comment (any kind of PHP comment). * * @param document * @param c * @param start * @param comments * A comments list to skip when searching for the char. * @return The char offset, and if not found - the original start offset. */ public static int locateCharBackward(FormatterDocument document, char c, int start, List<Comment> comments) { int startOffset = start; int commentIndex = PHPDocUtils.findComment(comments, startOffset); Comment nextComment = null; int nextCommentIndex = -1; if (commentIndex >= 0) { Comment commentAtOffset = comments.get(commentIndex); // Adjust the start offset to the comment's start (we are searching backwards). startOffset = commentAtOffset.getStart() - 1; if (commentIndex > 0) { nextCommentIndex = commentIndex - 1; nextComment = comments.get(nextCommentIndex); } } else { // We got a negative position. The value represents a (-(insertion point) -1) in the comments list, so in // this case of searching backwards we would like to get the positive value of that result, minus 2. nextCommentIndex = -commentIndex - 2; if (nextCommentIndex >= 0 && nextCommentIndex < comments.size()) { nextComment = comments.get(nextCommentIndex); } } for (int offset = startOffset; offset >= 0; offset--) { if (nextComment != null && offset >= nextComment.getStart() && offset < nextComment.getEnd()) { // The offset is inside a comment, so we need to adjust it again to skip the characters in the comment. offset = nextComment.getStart(); nextCommentIndex--; if (nextCommentIndex >= 0) { nextComment = comments.get(nextCommentIndex); } else { nextComment = null; } } if (document.charAt(offset) == c) { return offset; } } // We did not locate the character, so we return the original start offset. return start; } /** * Try to locate the given char by traversing forward on the given document from the start offset.<br> * In case no match is located, the original start offset is returned. This function skips any char that is detected * inside a comment (any kind of PHP comment). * * @param document * @param c * @param start * @param comments * A comments list to skip when searching for the char. * @return The char offset, and if not found - the original start offset. */ public static int locateCharForward(FormatterDocument document, char c, int start, List<Comment> comments) { int startOffset = start; int commentIndex = PHPDocUtils.findComment(comments, startOffset); Comment nextComment = null; int nextCommentIndex = -1; if (commentIndex >= 0) { Comment commentAtOffset = comments.get(commentIndex); // Adjust the start offset to the comment's start (we are searching backwards). startOffset = commentAtOffset.getEnd() + 1; nextCommentIndex = commentIndex + 1; if (nextCommentIndex < comments.size()) { nextComment = comments.get(nextCommentIndex); } } else { // We got a negative position, which means it's the offset of the nearest comment that has a bigger start // offset than the offset we searched for. In that case, we set the next comment to be the one after, if // possible. nextCommentIndex = -commentIndex; if (nextCommentIndex < comments.size()) { nextComment = comments.get(nextCommentIndex); } } int length = document.getLength(); for (int offset = startOffset; offset < length; offset++) { if (nextComment != null && offset >= nextComment.getStart() && offset < nextComment.getEnd()) { // The offset is inside a comment, so we need to adjust it again to skip the characters in the comment. offset = nextComment.getEnd() + 1; nextCommentIndex++; if (nextCommentIndex < comments.size()) { nextComment = comments.get(nextCommentIndex); } else { nextComment = null; } } if (offset >= length) { break; } if (document.charAt(offset) == c) { return offset; } } // We did not locate the character, so we return the original start offset. return start; } }