private String parseTokens()

in formatter/src/main/java/org/apache/royale/formatter/ASTokenFormatter.java [204:1141]


	private String parseTokens(List<IASToken> tokens, String fileText, String filePath) throws Exception {
		indent = 0;
		inCaseOrDefaultClause = false;
		inControlFlowStatement = false;
		inVarOrConstDeclaration = false;
		inFunctionDeclaration = false;
		inPackageDeclaration = false;
		inClassDeclaration = false;
		inInterfaceDeclaration = false;
		inConfigGateForDefinition = false;
		blockOpenPending = false;
		elseNoNewLinePending = false;
		indentedStatement = false;
		caseOrDefaultBlockOpenPending = false;
		skipFormatting = false;
		varOrConstChainLevel = -1;
		blockStack = new ArrayList<BlockStackItem>();
		controlFlowParenStack = 0;
		ternaryStack = 0;
		numRequiredNewLines = 0;
		requiredSpace = false;
		prevTokenNotComment = null;
		prevToken = null;
		prevTokenOrExtra = null;
		token = null;
		nextToken = null;
		nextTokenOrExtra = null;
		nextTokenNotComment = null;

		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < tokens.size(); i++) {
			token = tokens.get(i);
			if (inConfigGateForDefinition && token.getType() == ASTokenTypes.TOKEN_SEMICOLON && token.isImplicit())
			{
				// skip the implicit semicolon if we just saw a namespace
				// annotated config constant, like COMPILE:JS or COMPILE::SWF
				// and the next token starts a definition, like a class.
				// the implicit semicolon is only there because the ASParser
				// can't resolve the constant's value without the user providing
				// it manually.
				inConfigGateForDefinition = false;
				prevToken = token;
				prevTokenOrExtra = token;
				prevTokenNotComment = token;
				continue;
			}
			if (token.getType() == TOKEN_TYPE_EXTRA) {
				if (skipFormatting) {
					builder.append(token.getText());
				} else {
					if (i == (tokens.size() - 1)) {
						// if the last token is whitespace, include at most one
						// new line, but strip the rest
						numRequiredNewLines = Math.min(1, Math.max(0, countNewLinesInExtra(token)));
						appendNewLines(builder, numRequiredNewLines);
						break;
					}
					if (!blockOpenPending && !elseNoNewLinePending) {
						int newLinesInExtra = countNewLinesInExtra(token);
						if (prevToken != null && prevToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT) {
							newLinesInExtra++;
						}
						boolean oneLineBlock = prevToken != null && prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
								&& nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
						if (oneLineBlock && settings.collapseEmptyBlocks) {
							newLinesInExtra = 0;
						}
						numRequiredNewLines = Math.max(numRequiredNewLines, newLinesInExtra);
						if (!indentedStatement && numRequiredNewLines > 0 && prevTokenNotComment != null
								&& prevTokenNotComment.getType() != ASTokenTypes.TOKEN_SEMICOLON
								&& prevTokenNotComment.getType() != ASTokenTypes.TOKEN_BLOCK_CLOSE
								&& !(caseOrDefaultBlockOpenPending
										&& prevTokenNotComment.getType() == ASTokenTypes.TOKEN_COLON)
								&& !(prevTokenNotComment instanceof MetaDataPayloadToken)) {
							boolean needsIndent = prevTokenNotComment.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
									|| (!blockStack.isEmpty() && blockStack
											.get(blockStack.size() - 1) instanceof ObjectLiteralBlockStackItem);
							if (needsIndent) {
								startIndentedStatement();
							}
						}
					}
				}
				prevTokenOrExtra = token;
				continue;
			}
			nextTokenOrExtra = ((i + 1) < tokens.size()) ? tokens.get(i + 1) : null;
			nextToken = getNextTokenByOffset(tokens, i, 1, true, false);
			nextTokenNotComment = getNextTokenByOffset(tokens, i, 1, true, true);

			boolean skipWhitespaceBeforeNextSignificantToken = nextToken == null
					|| nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON
					|| nextToken.getType() == ASTokenTypes.TOKEN_COMMA
					|| nextToken.getType() == ASTokenTypes.TOKEN_COLON;

			// characters that must appear before the token
			if (token instanceof MetaDataPayloadToken) {
				numRequiredNewLines = Math.max(numRequiredNewLines, 1);
			} else {
				switch (token.getType()) {
					case ASTokenTypes.TOKEN_BLOCK_OPEN: {
						if (!blockOpenPending) {
							// detect some cases of blocks that may have been
							// missed earlier (these will be standalone blocks,
							// without a control flow statement or definition).
							// this should not detect object literals, though!
							blockOpenPending = prevTokenNotComment == null
									|| prevTokenNotComment.getType() == ASTokenTypes.TOKEN_SEMICOLON
									|| prevTokenNotComment.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
									|| prevTokenNotComment.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
							if (!blockOpenPending && prevTokenNotComment.getType() == ASTokenTypes.TOKEN_COLON
									&& !blockStack.isEmpty()) {
								IASToken blockToken = blockStack.get(blockStack.size() - 1).token;
								blockOpenPending = blockToken.getType() == ASTokenTypes.TOKEN_KEYWORD_DEFAULT
										|| blockToken.getType() == ASTokenTypes.TOKEN_KEYWORD_CASE;
							}
							if (blockOpenPending) {
								blockStack.add(new BlockStackItem(token));
							}
						}
						if (blockOpenPending) {
							boolean oneLineBlock = nextToken != null
									&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
							boolean needsNewLine = settings.placeOpenBraceOnNewLine
									&& (!settings.collapseEmptyBlocks || !oneLineBlock);
							if (needsNewLine) {
								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
							} else if (oneLineBlock && settings.collapseEmptyBlocks) {
								numRequiredNewLines = 0;
							}
							requiredSpace = true;
						} else {
							// probably an object literal
							blockStack.add(new ObjectLiteralBlockStackItem(token));
							// we will indent, but after appending the token

							requiredSpace = prevTokenNotComment == null || prevTokenNotComment.getType() != ASTokenTypes.TOKEN_PAREN_OPEN;
						}
						break;
					}
					case ASTokenTypes.TOKEN_BLOCK_CLOSE: {
						boolean checkNext = true;
						while (!blockStack.isEmpty() && checkNext) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem.controlFlow && !stackItem.braces) {
								// this block close token applies to a block
								// that contains nested control flow statements
								// without braces, so remove all of them
								blockStack.remove(blockStack.size() - 1);
								indent = decreaseIndent(indent);
								continue;
							}
							checkNext = false;
						}
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem.blockDepth <= 1) {
								boolean oneLineBlock = prevToken != null
										&& prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
										&& !(stackItem instanceof ObjectLiteralBlockStackItem);
								boolean allowPackageIndent = settings.indentPackageContents
										|| stackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_PACKAGE;
								boolean allowSwitchIndent = settings.indentSwitchContents
										|| stackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_SWITCH;
								boolean allowCaseOrDefaultIndent = !(stackItem instanceof CaseOrDefaultBlockStackItem)
										|| ((CaseOrDefaultBlockStackItem) stackItem).containsOnlyBlock;
								if ((!settings.collapseEmptyBlocks || !oneLineBlock)
										&& allowPackageIndent
										&& allowSwitchIndent
										&& allowCaseOrDefaultIndent) {
									indent = decreaseIndent(indent);
								}
								if (stackItem instanceof CaseOrDefaultBlockStackItem)
								{
									CaseOrDefaultBlockStackItem caseOrDefaultStackItem = (CaseOrDefaultBlockStackItem) stackItem;
									if (!caseOrDefaultStackItem.containsOnlyBlock) {
										blockStack.remove(blockStack.size() - 1);
										if (settings.indentSwitchContents) {
											indent = decreaseIndent(indent);
										}
									}
								}
							}
						}
						// check again because case/default might have been removed
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_SWITCH) {
								SwitchBlockStackItem switchStackItem = (SwitchBlockStackItem) stackItem;
								if (switchStackItem.clauseCount > 0
										&& (prevToken == null
												|| prevToken.getType() != ASTokenTypes.TOKEN_BLOCK_CLOSE)) {
									indent = decreaseIndent(indent);
								}
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_SQUARE_CLOSE: {
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem.token.getType() == ASTokenTypes.TOKEN_SQUARE_OPEN) {
								indent = decreaseIndent(indent);
								blockStack.remove(blockStack.size() - 1);
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_PAREN_CLOSE: {
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem.token.getType() == ASTokenTypes.TOKEN_PAREN_OPEN) {
								indent = decreaseIndent(indent);
								blockStack.remove(blockStack.size() - 1);
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_IN: {
						inVarOrConstDeclaration = false;
						// needs an extra space before the token
						requiredSpace = true;
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_AS:
					case ASTokenTypes.TOKEN_KEYWORD_IS:
					case ASTokenTypes.TOKEN_KEYWORD_INSTANCEOF:
					case ASTokenTypes.TOKEN_RESERVED_WORD_EACH:
					case ASTokenTypes.TOKEN_RESERVED_WORD_EXTENDS:
					case ASTokenTypes.TOKEN_RESERVED_WORD_IMPLEMENTS:
					case ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT:
					case ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT: {
						// needs an extra space before the token
						requiredSpace = true;
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_ELSE: {
						if (elseNoNewLinePending) {
							numRequiredNewLines = 0;
							elseNoNewLinePending = false;
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_NOT_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_STRICT_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_STRICT_NOT_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN:
					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN:
					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN_EQUALS:
					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN_EQUALS:
					case ASTokenTypes.TOKEN_OPERATOR_DIVISION:
					case ASTokenTypes.TOKEN_OPERATOR_MODULO:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR:
					case ASTokenTypes.TOKEN_OPERATOR_NULLISH_COALESCING:
					case ASTokenTypes.TOKEN_OPERATOR_PLUS_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_MINUS_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_MULTIPLICATION_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_DIVISION_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_MODULO_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT: {
						if (settings.insertSpaceBeforeAndAfterBinaryOperators) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_STAR: {
						boolean isAnyType = checkTokenBeforeAnyType(prevTokenNotComment);
						boolean isAnyVectorType = checkTokensForAnyVectorType(prevTokenNotComment, nextTokenNotComment);
						if (!isAnyType && !isAnyVectorType && settings.insertSpaceBeforeAndAfterBinaryOperators
								&& !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_PLUS:
					case ASTokenTypes.TOKEN_OPERATOR_MINUS: {
						boolean isUnary = checkTokenBeforePossibleUnaryOperator(prevTokenNotComment);
						if (!isUnary && settings.insertSpaceBeforeAndAfterBinaryOperators) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT: {
						inVarOrConstDeclaration = false;
						if (settings.insertSpaceBeforeAndAfterBinaryOperators) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_TERNARY: {
						ternaryStack++;
						if (settings.insertSpaceBeforeAndAfterBinaryOperators) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_ELLIPSIS: {
						boolean isFirstArg = prevToken == null || prevToken.getType() == ASTokenTypes.TOKEN_PAREN_OPEN;
						if (!isFirstArg) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_DEFAULT:
					case ASTokenTypes.TOKEN_KEYWORD_CASE: {
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							switch (stackItem.token.getType()) {
								case ASTokenTypes.TOKEN_KEYWORD_DEFAULT:
								case ASTokenTypes.TOKEN_KEYWORD_CASE: {
									blockStack.remove(blockStack.size() - 1);
									indent = decreaseIndent(indent);
									break;
								}
							}
						}
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_SWITCH) {
								SwitchBlockStackItem switchStackItem = (SwitchBlockStackItem) stackItem;
								switchStackItem.clauseCount++;
								inCaseOrDefaultClause = true;
								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
								blockStack.add(new CaseOrDefaultBlockStackItem(token));
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_COLON: {
						if (ternaryStack > 0) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_SEMICOLON: {
						endIndentedStatement();
						inVarOrConstDeclaration = false;
						varOrConstChainLevel = -1;
						break;
					}
					case ASTokenTypes.TOKEN_ASDOC_COMMENT: {
						if (prevToken != null && prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
						} else {
							// add an extra line before an asdoc comment
							numRequiredNewLines = Math.max(numRequiredNewLines, 2);
						}
						break;
					}
				}
			}
			if (!skipFormatting && prevToken != null) {
				if (numRequiredNewLines > 0) {
					appendNewLines(builder, numRequiredNewLines);
					appendIndent(builder, indent);
				} else if (requiredSpace) {
					builder.append(' ');
				}
			}
			switch (token.getType()) {
				case ASTokenTypes.TOKEN_BLOCK_OPEN: {
					if (blockOpenPending) {
						boolean oneLineBlock = nextToken != null
								&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
						BlockStackItem stackItem = blockStack.isEmpty() ? null : blockStack.get(blockStack.size() - 1);
						boolean allowPackageIndent = settings.indentPackageContents
								|| stackItem == null
								|| stackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_PACKAGE;
						boolean allowSwitchIndent = settings.indentSwitchContents
								|| stackItem == null
								|| stackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_SWITCH;
						if (settings.placeOpenBraceOnNewLine
								&& (!settings.collapseEmptyBlocks || !oneLineBlock)
								&& allowPackageIndent
								&& allowSwitchIndent) {
							indent = increaseIndent(indent);
						}
					}
					break;
				}
			}

			// include the token's own text
			builder.append(getTokenText(token, indent, skipFormatting, fileText));

			// characters that must appear after the token
			if (token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT
					&& token.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN) {
				blockOpenPending = false;
			}
			if (token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
				caseOrDefaultBlockOpenPending = false;
			}
			requiredSpace = false;
			numRequiredNewLines = 0;
			if (token instanceof MetaDataPayloadToken) {
				numRequiredNewLines = Math.max(numRequiredNewLines, 1);
			} else {
				switch (token.getType()) {
					case ASTokenTypes.TOKEN_SEMICOLON: {
						if (inControlFlowStatement && isInForStatement(blockStack)) {
							if (settings.insertSpaceAfterSemicolonInForStatements) {
								requiredSpace = true;
							}
							// else no space
						} else {
							boolean checkNext = true;
							int prevPoppedTokenType = -1;
							while (!blockStack.isEmpty() && checkNext) {
								checkNext = false;
								BlockStackItem prevStackItem = blockStack.get(blockStack.size() - 1);
								if (prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_CASE
										&& prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_DEFAULT
										&& prevStackItem.blockDepth <= 0) {
									if (prevPoppedTokenType == ASTokenTypes.TOKEN_KEYWORD_IF && prevStackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_IF && nextTokenNotComment != null && nextTokenNotComment.getType() == ASTokenTypes.TOKEN_KEYWORD_ELSE ) {
										// if we've already popped an if, and
										// we encounter another if, but the next
										// non-comment token is an else, then
										// we don't want to pop any more ifs
										break;
									}
									blockStack.remove(blockStack.size() - 1);
									if (prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_CLASS
											&& prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_INTERFACE
											&& prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_FUNCTION) {
										checkNext = !prevStackItem.braces;
										indent = decreaseIndent(indent);
									}
								}
								prevPoppedTokenType = prevStackItem.token.getType();
							}
						}
						if (!inControlFlowStatement) {
							if (nextToken != null
									&& (nextToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
											|| nextToken.getType() == ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT)) {
								requiredSpace = true;
							} else {
								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_BLOCK_OPEN: {
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							if (stackItem instanceof ObjectLiteralBlockStackItem) {
								indent = increaseIndent(indent);
							}
						}
						if (blockOpenPending) {
							blockOpenPending = false;
							BlockStackItem stackItem = null;
							if (!blockStack.isEmpty()) {
								stackItem = blockStack.get(blockStack.size() - 1);
								stackItem.blockDepth++;
							}
							boolean oneLineBlock = nextToken != null
									&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
							boolean allowPackageIndent = settings.indentPackageContents
									|| stackItem == null
									|| stackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_PACKAGE;
							boolean allowSwitchIndent = settings.indentSwitchContents
									|| stackItem == null
									|| stackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_SWITCH;
							if ((!settings.collapseEmptyBlocks || !oneLineBlock)
									&& allowPackageIndent
									&& allowSwitchIndent) {
								if (!settings.placeOpenBraceOnNewLine) {
									indent = increaseIndent(indent);
								}
								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_BLOCK_CLOSE: {
						if (!blockStack.isEmpty()) {
							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
							stackItem.blockDepth--;
							if (stackItem.blockDepth <= 0) {
								blockStack.remove(blockStack.size() - 1);
							}
							if (!(stackItem instanceof ObjectLiteralBlockStackItem)) {
								if (nextToken != null && nextTokenNotComment != null && nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT && nextTokenNotComment.getType() == ASTokenTypes.TOKEN_KEYWORD_ELSE && !settings.insertNewLineElse) {
									elseNoNewLinePending = true;
								}
								else if (nextToken == null || (nextToken.getType() != ASTokenTypes.TOKEN_SEMICOLON
											&& nextToken.getType() != ASTokenTypes.TOKEN_PAREN_CLOSE
											&& nextToken.getType() != ASTokenTypes.TOKEN_COMMA
											&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
											&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT)) {
									numRequiredNewLines = Math.max(numRequiredNewLines, 1);
								}
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_SQUARE_OPEN: {
						blockStack.add(new BlockStackItem(token));
						indent = increaseIndent(indent);
						break;
					}
					case ASTokenTypes.TOKEN_PAREN_OPEN: {
						blockStack.add(new BlockStackItem(token));
						indent = increaseIndent(indent);
						if (inControlFlowStatement) {
							controlFlowParenStack++;
						}
						break;
					}
					case ASTokenTypes.TOKEN_IDENTIFIER: {
						// look for a config constant, like COMPILE:JS or COMPILE::SWF
						if (prevToken != null && prevToken.getType() == ASTokenTypes.TOKEN_OPERATOR_NS_QUALIFIER) {
							boolean isConfigBlock = nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN;
							if (isConfigBlock) {
								// this is a config constant block
								blockStack.add(new BlockStackItem(prevToken));
								blockOpenPending = true;
							}
							else if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON && nextToken.isImplicit())
							{
								// if it's not before the start of a block, it
								// might be before the start of a definition
								IASToken prevPrevToken = getPrevTokenByOffset(tokens, i, 3, true, true);
								if (prevPrevToken == null
										|| prevPrevToken.getType() == ASTokenTypes.TOKEN_SEMICOLON
										|| prevPrevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
										|| prevPrevToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE) {
									IASToken nextNextToken = getNextTokenByOffset(tokens, i, 2, true, true);
									if (nextNextToken != null) {
										if (nextNextToken.getType() == ASTokenTypes.TOKEN_NAMESPACE_ANNOTATION
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_KEYWORD_CLASS
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_KEYWORD_INTERFACE
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_KEYWORD_FUNCTION
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_KEYWORD_VAR
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_KEYWORD_CONST
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_ABSTRACT
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_DYNAMIC
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_FINAL
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_NATIVE
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_OVERRIDE
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_STATIC
											|| nextNextToken.getType() == ASTokenTypes.TOKEN_MODIFIER_VIRTUAL) {
												// this config constant is gating a definition
												// like COMPILE::JS or COMPILE::SWF before a class
												inConfigGateForDefinition = true;
											}
									}
								}
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_PAREN_CLOSE: {
						if (inControlFlowStatement) {
							controlFlowParenStack--;
							if (controlFlowParenStack <= 0) {
								endIndentedStatement();
								inControlFlowStatement = false;
								// if a variable was declared inside the
								// parentheses, such as in a for() loop, this
								// should already be false (if it's not, there's
								// a bug somewhere).
								// but this will add a little extra safety.
								inVarOrConstDeclaration = false;
								if (!blockStack.isEmpty()) {
									BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
									stackItem.controlFlowEnd = token.getEnd();
								}
								controlFlowParenStack = 0;
								blockOpenPending = true;
								if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON) {
									blockStack.remove(blockStack.size() - 1);
									if (!skipWhitespaceBeforeNextSignificantToken) {
										numRequiredNewLines = Math.max(numRequiredNewLines, 1);
									}
								} else if (nextTokenNotComment != null && nextTokenNotComment.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
										&& !skipWhitespaceBeforeNextSignificantToken) {
									indent = increaseIndent(indent);
									BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
									stackItem.braces = false;
									// if a comment is on the same line
									// don't add a line break yet
									if (nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
											&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
											&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_COMMENT) {
										numRequiredNewLines = Math.max(numRequiredNewLines, 1);
									}
								}
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_INCREMENT:
					case ASTokenTypes.TOKEN_OPERATOR_DECREMENT: {
						if (!inControlFlowStatement
								&& !checkTokenBeforePossibleUnaryOperator(prevTokenNotComment)
								&& !checkTokenAfterPossibleUnaryOperator(nextTokenNotComment)) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_CONTINUE:
					case ASTokenTypes.TOKEN_KEYWORD_BREAK:
					case ASTokenTypes.TOKEN_KEYWORD_RETURN: {
						if (!skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_PACKAGE: {
						blockStack.add(new BlockStackItem(token));
						requiredSpace = true;
						inPackageDeclaration = true;
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_CLASS: {
						blockStack.add(new BlockStackItem(token));
						requiredSpace = true;
						inClassDeclaration = true;
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_INTERFACE: {
						blockStack.add(new BlockStackItem(token));
						requiredSpace = true;
						inInterfaceDeclaration = true;
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_FUNCTION: {
						blockStack.add(new BlockStackItem(token));
						inFunctionDeclaration = true;
						boolean skipSpace = !settings.insertSpaceAfterFunctionKeywordForAnonymousFunctions
								&& (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_PAREN_OPEN);
						if (!skipSpace) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_VAR:
					case ASTokenTypes.TOKEN_KEYWORD_CONST: {
						inVarOrConstDeclaration = true;
						requiredSpace = true;
						varOrConstChainLevel = blockStack.size();
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_CATCH:
					case ASTokenTypes.TOKEN_KEYWORD_FOR:
					case ASTokenTypes.TOKEN_KEYWORD_IF:
					case ASTokenTypes.TOKEN_KEYWORD_WHILE:
					case ASTokenTypes.TOKEN_KEYWORD_WITH: {
						inControlFlowStatement = true;
						BlockStackItem stackItem = new BlockStackItem(token);
						stackItem.controlFlow = true;
						blockStack.add(stackItem);
						if (settings.insertSpaceAfterKeywordsInControlFlowStatements
								&& !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_SWITCH: {
						inControlFlowStatement = true;
						blockStack.add(new SwitchBlockStackItem(token));
						if (settings.insertSpaceAfterKeywordsInControlFlowStatements
								&& !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_TRY:
					case ASTokenTypes.TOKEN_KEYWORD_FINALLY: {
						blockStack.add(new BlockStackItem(token));
						if (!skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						blockOpenPending = true;
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_ELSE: {
						if (nextTokenNotComment != null
								&& nextTokenNotComment.getType() == ASTokenTypes.TOKEN_KEYWORD_IF) {
							requiredSpace = true;
						} else {
							BlockStackItem stackItem = new BlockStackItem(token);
							stackItem.controlFlow = true;
							blockStack.add(stackItem);
							blockOpenPending = true;
							if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON) {
								blockStack.remove(blockStack.size() - 1);
								if (!skipWhitespaceBeforeNextSignificantToken) {
									numRequiredNewLines = Math.max(numRequiredNewLines, 1);
								}
							} else if (nextToken != null && nextToken.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
									&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
									&& !skipWhitespaceBeforeNextSignificantToken) {
								indent = increaseIndent(indent);
								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
								stackItem.braces = false;
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_DO: {
						blockStack.add(new BlockStackItem(token));
						blockOpenPending = true;
						if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON) {
							blockStack.remove(blockStack.size() - 1);
							if (!skipWhitespaceBeforeNextSignificantToken) {
								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
							}
						} else if (nextToken != null && nextToken.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
								&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
								&& !skipWhitespaceBeforeNextSignificantToken) {
							indent = increaseIndent(indent);
							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
						}
						break;
					}
					case ASTokenTypes.TOKEN_COLON: {
						if (ternaryStack > 0) {
							ternaryStack--;
							requiredSpace = true;
						} else if (!inControlFlowStatement && !inVarOrConstDeclaration && !inFunctionDeclaration) {
							if (inCaseOrDefaultClause) {
								inCaseOrDefaultClause = false;
								caseOrDefaultBlockOpenPending = true;
								boolean nextIsBlock = nextTokenNotComment != null
										&& nextTokenNotComment.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN;
								boolean caseOrDefaultContainsOnlyBlock = false;
								if (nextIsBlock) {
									IASToken afterBlockClose = findTokenAfterBlock(nextTokenNotComment, tokens);
									BlockStackItem stackItem = !blockStack.isEmpty() ? blockStack.get(blockStack.size() - 1) : null;
									if (stackItem instanceof CaseOrDefaultBlockStackItem) {
										CaseOrDefaultBlockStackItem caseOrDefaultStackItem = (CaseOrDefaultBlockStackItem) stackItem;
										caseOrDefaultContainsOnlyBlock = afterBlockClose != null
												&& (afterBlockClose.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE
														|| afterBlockClose.getType() == ASTokenTypes.TOKEN_KEYWORD_CASE
														|| afterBlockClose.getType() == ASTokenTypes.TOKEN_KEYWORD_DEFAULT);
										caseOrDefaultStackItem.containsOnlyBlock = caseOrDefaultContainsOnlyBlock;
										if (caseOrDefaultContainsOnlyBlock) {
											blockOpenPending = true;
										}
										requiredSpace = true;
									}
								}
								if (!caseOrDefaultContainsOnlyBlock || !blockOpenPending) {
									indent = increaseIndent(indent);
									if (nextToken != null && (nextToken
											.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
											|| nextToken.getType() == ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT)) {
										requiredSpace = true;
									} else {
										numRequiredNewLines = Math.max(numRequiredNewLines, 1);
									}
								}
							} else {
								requiredSpace = true;
							}
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_CASE: {
						if (!skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_DEFAULT: {
						if (!inCaseOrDefaultClause && !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_KEYWORD_AS:
					case ASTokenTypes.TOKEN_KEYWORD_DELETE:
					case ASTokenTypes.TOKEN_KEYWORD_IMPORT:
					case ASTokenTypes.TOKEN_KEYWORD_IN:
					case ASTokenTypes.TOKEN_KEYWORD_INCLUDE:
					case ASTokenTypes.TOKEN_KEYWORD_INSTANCEOF:
					case ASTokenTypes.TOKEN_KEYWORD_IS:
					case ASTokenTypes.TOKEN_KEYWORD_NEW:
					case ASTokenTypes.TOKEN_KEYWORD_THROW:
					case ASTokenTypes.TOKEN_KEYWORD_TYPEOF:
					case ASTokenTypes.TOKEN_KEYWORD_USE:
					case ASTokenTypes.TOKEN_RESERVED_WORD_CONFIG:
					case ASTokenTypes.TOKEN_RESERVED_WORD_EXTENDS:
					case ASTokenTypes.TOKEN_RESERVED_WORD_GET:
					case ASTokenTypes.TOKEN_RESERVED_WORD_GOTO:
					case ASTokenTypes.TOKEN_RESERVED_WORD_IMPLEMENTS:
					case ASTokenTypes.TOKEN_RESERVED_WORD_NAMESPACE:
					case ASTokenTypes.TOKEN_RESERVED_WORD_SET:
					case ASTokenTypes.TOKEN_MODIFIER_ABSTRACT:
					case ASTokenTypes.TOKEN_MODIFIER_DYNAMIC:
					case ASTokenTypes.TOKEN_MODIFIER_FINAL:
					case ASTokenTypes.TOKEN_MODIFIER_NATIVE:
					case ASTokenTypes.TOKEN_MODIFIER_OVERRIDE:
					case ASTokenTypes.TOKEN_MODIFIER_STATIC:
					case ASTokenTypes.TOKEN_MODIFIER_VIRTUAL:
					case ASTokenTypes.TOKEN_NAMESPACE_ANNOTATION: {
						if (!skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_NOT_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_STRICT_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_STRICT_NOT_EQUAL:
					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN:
					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN:
					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN_EQUALS:
					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN_EQUALS:
					case ASTokenTypes.TOKEN_OPERATOR_DIVISION:
					case ASTokenTypes.TOKEN_OPERATOR_MODULO:
					case ASTokenTypes.TOKEN_OPERATOR_TERNARY:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR:
					case ASTokenTypes.TOKEN_OPERATOR_NULLISH_COALESCING:
					case ASTokenTypes.TOKEN_OPERATOR_PLUS_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_MINUS_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_MULTIPLICATION_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_DIVISION_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_MODULO_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT:
					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT: {
						if (settings.insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_STAR: {
						boolean isAnyType = checkTokenBeforeAnyType(prevTokenNotComment);
						boolean isAnyVectorType = checkTokensForAnyVectorType(prevTokenNotComment, nextTokenNotComment);
						if (!isAnyType && !isAnyVectorType && settings.insertSpaceBeforeAndAfterBinaryOperators
								&& !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_OPERATOR_PLUS:
					case ASTokenTypes.TOKEN_OPERATOR_MINUS: {
						boolean isUnary = checkTokenBeforePossibleUnaryOperator(prevTokenNotComment);
						if (!isUnary && settings.insertSpaceBeforeAndAfterBinaryOperators
								&& !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.TOKEN_COMMA: {
						if (varOrConstChainLevel == blockStack.size()) {
							inVarOrConstDeclaration = true;
						}
						if (settings.insertSpaceAfterCommaDelimiter && !skipWhitespaceBeforeNextSignificantToken) {
							requiredSpace = true;
						}
						break;
					}
					case ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT: {
						numRequiredNewLines = Math.max(numRequiredNewLines, 1);

						String trimmed = token.getText().substring(2).trim();
						if (!skipFormatting && FORMATTER_TAG_OFF.equals(trimmed)) {
							skipFormatting = true;
							appendNewLines(builder, 1);
						} else if (skipFormatting && FORMATTER_TAG_ON.equals(trimmed)) {
							skipFormatting = false;
							numRequiredNewLines = 0;
						}
						break;
					}
					case ASTokenTypes.TOKEN_ASDOC_COMMENT:
					case ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT: {
						if (!skipWhitespaceBeforeNextSignificantToken) {
							if (nextTokenOrExtra != null && nextTokenOrExtra.getType() == TOKEN_TYPE_EXTRA) {
								numRequiredNewLines = Math.max(0, countNewLinesInExtra(nextTokenOrExtra));
							}
							requiredSpace = true;
						}
						break;
					}
				}
			}
			if ((inPackageDeclaration || inClassDeclaration || inInterfaceDeclaration || inFunctionDeclaration)
					&& nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
				blockOpenPending = true;
				endIndentedStatement();
				inPackageDeclaration = false;
				inClassDeclaration = false;
				inInterfaceDeclaration = false;
				inFunctionDeclaration = false;
			}
			prevToken = token;
			prevTokenOrExtra = prevToken;
			if (prevToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
					&& prevToken.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
					&& prevToken.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
				prevTokenNotComment = prevToken;
			}
		}
		if (blockStack.size() > 0) {
			throw new Exception("Unexpected end of file <" + filePath + ">. Blocks still considered open by formatter. This is a bug.");
		}
		return builder.toString();
	}