+// ASFormatter.cpp
+// Copyright (c) 2017 by Jim Pattee <jimp03@email.com>.
+// This code is licensed under the MIT License.
+// License.md describes the conditions under which this software may be distributed.
+
+//-----------------------------------------------------------------------------
+// headers
+//-----------------------------------------------------------------------------
+
+#include "astyle.h"
+
+#include <algorithm>
+#include <fstream>
+
+//-----------------------------------------------------------------------------
+// astyle namespace
+//-----------------------------------------------------------------------------
+
+namespace astyle {
+//
+//-----------------------------------------------------------------------------
+// ASFormatter class
+//-----------------------------------------------------------------------------
+
+/**
+ * Constructor of ASFormatter
+ */
+ASFormatter::ASFormatter()
+{
+ sourceIterator = nullptr;
+ enhancer = new ASEnhancer;
+ preBraceHeaderStack = nullptr;
+ braceTypeStack = nullptr;
+ parenStack = nullptr;
+ structStack = nullptr;
+ questionMarkStack = nullptr;
+ lineCommentNoIndent = false;
+ formattingStyle = STYLE_NONE;
+ braceFormatMode = NONE_MODE;
+ pointerAlignment = PTR_ALIGN_NONE;
+ referenceAlignment = REF_SAME_AS_PTR;
+ objCColonPadMode = COLON_PAD_NO_CHANGE;
+ lineEnd = LINEEND_DEFAULT;
+ maxCodeLength = string::npos;
+ shouldPadCommas = false;
+ shouldPadOperators = false;
+ shouldPadParensOutside = false;
+ shouldPadFirstParen = false;
+ shouldPadParensInside = false;
+ shouldPadHeader = false;
+ shouldStripCommentPrefix = false;
+ shouldUnPadParens = false;
+ attachClosingBraceMode = false;
+ shouldBreakOneLineBlocks = true;
+ shouldBreakOneLineHeaders = false;
+ shouldBreakOneLineStatements = true;
+ shouldConvertTabs = false;
+ shouldIndentCol1Comments = false;
+ shouldIndentPreprocBlock = false;
+ shouldCloseTemplates = false;
+ shouldAttachExternC = false;
+ shouldAttachNamespace = false;
+ shouldAttachClass = false;
+ shouldAttachClosingWhile = false;
+ shouldAttachInline = false;
+ shouldBreakBlocks = false;
+ shouldBreakClosingHeaderBlocks = false;
+ shouldBreakClosingHeaderBraces = false;
+ shouldDeleteEmptyLines = false;
+ shouldBreakElseIfs = false;
+ shouldBreakLineAfterLogical = false;
+ shouldAddBraces = false;
+ shouldAddOneLineBraces = false;
+ shouldRemoveBraces = false;
+ shouldPadMethodColon = false;
+ shouldPadMethodPrefix = false;
+ shouldUnPadMethodPrefix = false;
+ shouldPadReturnType = false;
+ shouldUnPadReturnType = false;
+ shouldPadParamType = false;
+ shouldUnPadParamType = false;
+
+ // initialize ASFormatter member vectors
+ formatterFileType = 9; // reset to an invalid type
+ headers = new vector<const string*>;
+ nonParenHeaders = new vector<const string*>;
+ preDefinitionHeaders = new vector<const string*>;
+ preCommandHeaders = new vector<const string*>;
+ operators = new vector<const string*>;
+ assignmentOperators = new vector<const string*>;
+ castOperators = new vector<const string*>;
+
+ // initialize ASEnhancer member vectors
+ indentableMacros = new vector<const pair<const string, const string>* >;
+}
+
+/**
+ * Destructor of ASFormatter
+ */
+ASFormatter::~ASFormatter()
+{
+ // delete ASFormatter stack vectors
+ deleteContainer(preBraceHeaderStack);
+ deleteContainer(braceTypeStack);
+ deleteContainer(parenStack);
+ deleteContainer(structStack);
+ deleteContainer(questionMarkStack);
+
+ // delete ASFormatter member vectors
+ formatterFileType = 9; // reset to an invalid type
+ delete headers;
+ delete nonParenHeaders;
+ delete preDefinitionHeaders;
+ delete preCommandHeaders;
+ delete operators;
+ delete assignmentOperators;
+ delete castOperators;
+
+ // delete ASEnhancer member vectors
+ delete indentableMacros;
+
+ // must be done when the ASFormatter object is deleted (not ASBeautifier)
+ // delete ASBeautifier member vectors
+ ASBeautifier::deleteBeautifierVectors();
+
+ delete enhancer;
+}
+
+/**
+ * initialize the ASFormatter.
+ *
+ * init() should be called every time a ASFormatter object is to start
+ * formatting a NEW source file.
+ * init() receives a pointer to a ASSourceIterator object that will be
+ * used to iterate through the source code.
+ *
+ * @param si a pointer to the ASSourceIterator or ASStreamIterator object.
+ */
+void ASFormatter::init(ASSourceIterator* si)
+{
+ buildLanguageVectors();
+ fixOptionVariableConflicts();
+ ASBeautifier::init(si);
+ sourceIterator = si;
+
+ enhancer->init(getFileType(),
+ getIndentLength(),
+ getTabLength(),
+ getIndentString() == "\t",
+ getForceTabIndentation(),
+ getNamespaceIndent(),
+ getCaseIndent(),
+ shouldIndentPreprocBlock,
+ getPreprocDefineIndent(),
+ getEmptyLineFill(),
+ indentableMacros);
+
+ initContainer(preBraceHeaderStack, new vector<const string*>);
+ initContainer(parenStack, new vector<int>);
+ initContainer(structStack, new vector<bool>);
+ initContainer(questionMarkStack, new vector<bool>);
+ parenStack->emplace_back(0); // parenStack must contain this default entry
+ initContainer(braceTypeStack, new vector<BraceType>);
+ braceTypeStack->emplace_back(NULL_TYPE); // braceTypeStack must contain this default entry
+ clearFormattedLineSplitPoints();
+
+ currentHeader = nullptr;
+ currentLine = "";
+ readyFormattedLine = "";
+ formattedLine = "";
+ verbatimDelimiter = "";
+ currentChar = ' ';
+ previousChar = ' ';
+ previousCommandChar = ' ';
+ previousNonWSChar = ' ';
+ quoteChar = '"';
+ preprocBlockEnd = 0;
+ charNum = 0;
+ checksumIn = 0;
+ checksumOut = 0;
+ currentLineFirstBraceNum = string::npos;
+ formattedLineCommentNum = 0;
+ leadingSpaces = 0;
+ previousReadyFormattedLineLength = string::npos;
+ preprocBraceTypeStackSize = 0;
+ spacePadNum = 0;
+ nextLineSpacePadNum = 0;
+ objCColonAlign = 0;
+ templateDepth = 0;
+ squareBracketCount = 0;
+ runInIndentChars = 0;
+ tabIncrementIn = 0;
+ previousBraceType = NULL_TYPE;
+
+ isVirgin = true;
+ isInVirginLine = true;
+ isInLineComment = false;
+ isInComment = false;
+ isInCommentStartLine = false;
+ noTrimCommentContinuation = false;
+ isInPreprocessor = false;
+ isInPreprocessorBeautify = false;
+ doesLineStartComment = false;
+ lineEndsInCommentOnly = false;
+ lineIsCommentOnly = false;
+ lineIsLineCommentOnly = false;
+ lineIsEmpty = false;
+ isImmediatelyPostCommentOnly = false;
+ isImmediatelyPostEmptyLine = false;
+ isInClassInitializer = false;
+ isInQuote = false;
+ isInVerbatimQuote = false;
+ haveLineContinuationChar = false;
+ isInQuoteContinuation = false;
+ isHeaderInMultiStatementLine = false;
+ isSpecialChar = false;
+ isNonParenHeader = false;
+ foundNamespaceHeader = false;
+ foundClassHeader = false;
+ foundStructHeader = false;
+ foundInterfaceHeader = false;
+ foundPreDefinitionHeader = false;
+ foundPreCommandHeader = false;
+ foundPreCommandMacro = false;
+ foundTrailingReturnType = false;
+ foundCastOperator = false;
+ foundQuestionMark = false;
+ isInLineBreak = false;
+ endOfAsmReached = false;
+ endOfCodeReached = false;
+ isFormattingModeOff = false;
+ isInEnum = false;
+ isInExecSQL = false;
+ isInAsm = false;
+ isInAsmOneLine = false;
+ isInAsmBlock = false;
+ isLineReady = false;
+ elseHeaderFollowsComments = false;
+ caseHeaderFollowsComments = false;
+ isPreviousBraceBlockRelated = false;
+ isInPotentialCalculation = false;
+ needHeaderOpeningBrace = false;
+ shouldBreakLineAtNextChar = false;
+ shouldKeepLineUnbroken = false;
+ shouldReparseCurrentChar = false;
+ passedSemicolon = false;
+ passedColon = false;
+ isImmediatelyPostNonInStmt = false;
+ isCharImmediatelyPostNonInStmt = false;
+ isInTemplate = false;
+ isImmediatelyPostComment = false;
+ isImmediatelyPostLineComment = false;
+ isImmediatelyPostEmptyBlock = false;
+ isImmediatelyPostObjCMethodPrefix = false;
+ isImmediatelyPostPreprocessor = false;
+ isImmediatelyPostReturn = false;
+ isImmediatelyPostThrow = false;
+ isImmediatelyPostNewDelete = false;
+ isImmediatelyPostOperator = false;
+ isImmediatelyPostTemplate = false;
+ isImmediatelyPostPointerOrReference = false;
+ isCharImmediatelyPostReturn = false;
+ isCharImmediatelyPostThrow = false;
+ isCharImmediatelyPostNewDelete = false;
+ isCharImmediatelyPostOperator = false;
+ isCharImmediatelyPostComment = false;
+ isPreviousCharPostComment = false;
+ isCharImmediatelyPostLineComment = false;
+ isCharImmediatelyPostOpenBlock = false;
+ isCharImmediatelyPostCloseBlock = false;
+ isCharImmediatelyPostTemplate = false;
+ isCharImmediatelyPostPointerOrReference = false;
+ isInObjCInterface = false;
+ isInObjCMethodDefinition = false;
+ isInObjCReturnType = false;
+ isInObjCSelector = false;
+ breakCurrentOneLineBlock = false;
+ shouldRemoveNextClosingBrace = false;
+ isInBraceRunIn = false;
+ currentLineBeginsWithBrace = false;
+ isPrependPostBlockEmptyLineRequested = false;
+ isAppendPostBlockEmptyLineRequested = false;
+ isIndentableProprocessor = false;
+ isIndentableProprocessorBlock = false;
+ prependEmptyLine = false;
+ appendOpeningBrace = false;
+ foundClosingHeader = false;
+ isImmediatelyPostHeader = false;
+ isInHeader = false;
+ isInCase = false;
+ isFirstPreprocConditional = false;
+ processedFirstConditional = false;
+ isJavaStaticConstructor = false;
+}
+
+/**
+ * build vectors for each programing language
+ * depending on the file extension.
+ */
+void ASFormatter::buildLanguageVectors()
+{
+ if (getFileType() == formatterFileType) // don't build unless necessary
+ return;
+
+ formatterFileType = getFileType();
+
+ headers->clear();
+ nonParenHeaders->clear();
+ preDefinitionHeaders->clear();
+ preCommandHeaders->clear();
+ operators->clear();
+ assignmentOperators->clear();
+ castOperators->clear();
+ indentableMacros->clear(); // ASEnhancer
+
+ ASResource::buildHeaders(headers, getFileType());
+ ASResource::buildNonParenHeaders(nonParenHeaders, getFileType());
+ ASResource::buildPreDefinitionHeaders(preDefinitionHeaders, getFileType());
+ ASResource::buildPreCommandHeaders(preCommandHeaders, getFileType());
+ ASResource::buildOperators(operators, getFileType());
+ ASResource::buildAssignmentOperators(assignmentOperators);
+ ASResource::buildCastOperators(castOperators);
+ ASResource::buildIndentableMacros(indentableMacros); //ASEnhancer
+}
+
+/**
+ * set the variables for each predefined style.
+ * this will override any previous settings.
+ */
+void ASFormatter::fixOptionVariableConflicts()
+{
+ if (formattingStyle == STYLE_ALLMAN)
+ {
+ setBraceFormatMode(BREAK_MODE);
+ }
+ else if (formattingStyle == STYLE_JAVA)
+ {
+ setBraceFormatMode(ATTACH_MODE);
+ }
+ else if (formattingStyle == STYLE_KR)
+ {
+ setBraceFormatMode(LINUX_MODE);
+ }
+ else if (formattingStyle == STYLE_STROUSTRUP)
+ {
+ setBraceFormatMode(LINUX_MODE);
+ setBreakClosingHeaderBracesMode(true);
+ }
+ else if (formattingStyle == STYLE_WHITESMITH)
+ {
+ setBraceFormatMode(BREAK_MODE);
+ setBraceIndent(true);
+ setClassIndent(true); // avoid hanging indent with access modifiers
+ setSwitchIndent(true); // avoid hanging indent with case statements
+ }
+ else if (formattingStyle == STYLE_VTK)
+ {
+ // the unindented class brace does NOT cause a hanging indent like Whitesmith
+ setBraceFormatMode(BREAK_MODE);
+ setBraceIndentVtk(true); // sets both braceIndent and braceIndentVtk
+ setSwitchIndent(true); // avoid hanging indent with case statements
+ }
+ else if (formattingStyle == STYLE_BANNER)
+ {
+ // attached braces can have hanging indents with the closing brace
+ setBraceFormatMode(ATTACH_MODE);
+ setBraceIndent(true);
+ setClassIndent(true); // avoid hanging indent with access modifiers
+ setSwitchIndent(true); // avoid hanging indent with case statements
+ }
+ else if (formattingStyle == STYLE_GNU)
+ {
+ setBraceFormatMode(BREAK_MODE);
+ setBlockIndent(true);
+ }
+ else if (formattingStyle == STYLE_LINUX)
+ {
+ setBraceFormatMode(LINUX_MODE);
+ // always for Linux style
+ setMinConditionalIndentOption(MINCOND_ONEHALF);
+ }
+ else if (formattingStyle == STYLE_HORSTMANN)
+ {
+ setBraceFormatMode(RUN_IN_MODE);
+ setSwitchIndent(true);
+ }
+ else if (formattingStyle == STYLE_1TBS)
+ {
+ setBraceFormatMode(LINUX_MODE);
+ setAddBracesMode(true);
+ setRemoveBracesMode(false);
+ }
+ else if (formattingStyle == STYLE_GOOGLE)
+ {
+ setBraceFormatMode(ATTACH_MODE);
+ setModifierIndent(true);
+ setClassIndent(false);
+ }
+ else if (formattingStyle == STYLE_MOZILLA)
+ {
+ setBraceFormatMode(LINUX_MODE);
+ }
+ else if (formattingStyle == STYLE_PICO)
+ {
+ setBraceFormatMode(RUN_IN_MODE);
+ setAttachClosingBraceMode(true);
+ setSwitchIndent(true);
+ setBreakOneLineBlocksMode(false);
+ setBreakOneLineStatementsMode(false);
+ // add-braces won't work for pico, but it could be fixed if necessary
+ // both options should be set to true
+ if (shouldAddBraces)
+ shouldAddOneLineBraces = true;
+ }
+ else if (formattingStyle == STYLE_LISP)
+ {
+ setBraceFormatMode(ATTACH_MODE);
+ setAttachClosingBraceMode(true);
+ setBreakOneLineStatementsMode(false);
+ // add-one-line-braces won't work for lisp
+ // only shouldAddBraces should be set to true
+ if (shouldAddOneLineBraces)
+ {
+ shouldAddBraces = true;
+ shouldAddOneLineBraces = false;
+ }
+ }
+ setMinConditionalIndentLength();
+ // if not set by indent=force-tab-x set equal to indentLength
+ if (getTabLength() == 0)
+ setDefaultTabLength();
+ // add-one-line-braces implies keep-one-line-blocks
+ if (shouldAddOneLineBraces)
+ setBreakOneLineBlocksMode(false);
+ // don't allow add-braces and remove-braces
+ if (shouldAddBraces || shouldAddOneLineBraces)
+ setRemoveBracesMode(false);
+ // don't allow indent-classes and indent-modifiers
+ if (getClassIndent())
+ setModifierIndent(false);
+}
+
+/**
+ * get the next formatted line.
+ *
+ * @return formatted line.
+ */
+string ASFormatter::nextLine()
+{
+ const string* newHeader = nullptr;
+ isInVirginLine = isVirgin;
+ isCharImmediatelyPostComment = false;
+ isPreviousCharPostComment = false;
+ isCharImmediatelyPostLineComment = false;
+ isCharImmediatelyPostOpenBlock = false;
+ isCharImmediatelyPostCloseBlock = false;
+ isCharImmediatelyPostTemplate = false;
+
+ while (!isLineReady)
+ {
+ if (shouldReparseCurrentChar)
+ shouldReparseCurrentChar = false;
+ else if (!getNextChar())
+ {
+ breakLine();
+ continue;
+ }
+ else // stuff to do when reading a new character...
+ {
+ // make sure that a virgin '{' at the beginning of the file will be treated as a block...
+ if (isInVirginLine && currentChar == '{'
+ && currentLineBeginsWithBrace
+ && previousCommandChar == ' ')
+ previousCommandChar = '{';
+ if (isInClassInitializer
+ && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
+ isInClassInitializer = false;
+ if (isInBraceRunIn)
+ isInLineBreak = false;
+ if (!isWhiteSpace(currentChar))
+ isInBraceRunIn = false;
+ isPreviousCharPostComment = isCharImmediatelyPostComment;
+ isCharImmediatelyPostComment = false;
+ isCharImmediatelyPostTemplate = false;
+ isCharImmediatelyPostReturn = false;
+ isCharImmediatelyPostThrow = false;
+ isCharImmediatelyPostNewDelete = false;
+ isCharImmediatelyPostOperator = false;
+ isCharImmediatelyPostPointerOrReference = false;
+ isCharImmediatelyPostOpenBlock = false;
+ isCharImmediatelyPostCloseBlock = false;
+ }
+
+ if ((lineIsLineCommentOnly || lineIsCommentOnly)
+ && currentLine.find("*INDENT-ON*", charNum) != string::npos
+ && isFormattingModeOff)
+ {
+ isFormattingModeOff = false;
+ breakLine();
+ formattedLine = currentLine;
+ charNum = (int) currentLine.length() - 1;
+ continue;
+ }
+ if (isFormattingModeOff)
+ {
+ breakLine();
+ formattedLine = currentLine;
+ charNum = (int) currentLine.length() - 1;
+ continue;
+ }
+ if ((lineIsLineCommentOnly || lineIsCommentOnly)
+ && currentLine.find("*INDENT-OFF*", charNum) != string::npos)
+ {
+ isFormattingModeOff = true;
+ if (isInLineBreak) // is true if not the first line
+ breakLine();
+ formattedLine = currentLine;
+ charNum = (int)currentLine.length() - 1;
+ continue;
+ }
+
+ if (shouldBreakLineAtNextChar)
+ {
+ if (isWhiteSpace(currentChar) && !lineIsEmpty)
+ continue;
+ isInLineBreak = true;
+ shouldBreakLineAtNextChar = false;
+ }
+
+ if (isInExecSQL && !passedSemicolon)
+ {
+ if (currentChar == ';')
+ passedSemicolon = true;
+ appendCurrentChar();
+ continue;
+ }
+
+ if (isInLineComment)
+ {
+ formatLineCommentBody();
+ continue;
+ }
+ else if (isInComment)
+ {
+ formatCommentBody();
+ continue;
+ }
+
+ else if (isInQuote)
+ {
+ formatQuoteBody();
+ continue;
+ }
+
+ // not in quote or comment or line comment
+
+ if (isSequenceReached("//"))
+ {
+ formatLineCommentOpener();
+ testForTimeToSplitFormattedLine();
+ continue;
+ }
+ else if (isSequenceReached("/*"))
+ {
+ formatCommentOpener();
+ testForTimeToSplitFormattedLine();
+ continue;
+ }
+ else if (currentChar == '"'
+ || (currentChar == '\'' && !isDigitSeparator(currentLine, charNum)))
+ {
+ formatQuoteOpener();
+ testForTimeToSplitFormattedLine();
+ continue;
+ }
+ // treat these preprocessor statements as a line comment
+ else if (currentChar == '#'
+ && currentLine.find_first_not_of(" \t") == (size_t) charNum)
+ {
+ string preproc = trim(currentLine.c_str() + charNum + 1);
+ if (preproc.length() > 0
+ && isCharPotentialHeader(preproc, 0)
+ && (findKeyword(preproc, 0, "region")
+ || findKeyword(preproc, 0, "endregion")
+ || findKeyword(preproc, 0, "error")
+ || findKeyword(preproc, 0, "warning")
+ || findKeyword(preproc, 0, "line")))
+ {
+ currentLine = rtrim(currentLine); // trim the end only
+ // check for run-in
+ if (formattedLine.length() > 0 && formattedLine[0] == '{')
+ {
+ isInLineBreak = true;
+ isInBraceRunIn = false;
+ }
+ if (previousCommandChar == '}')
+ currentHeader = nullptr;
+ isInLineComment = true;
+ appendCurrentChar();
+ continue;
+ }
+ }
+
+ if (isInPreprocessor)
+ {
+ appendCurrentChar();
+ continue;
+ }
+
+ if (isInTemplate && shouldCloseTemplates)
+ {
+ if (previousNonWSChar == '>' && isWhiteSpace(currentChar) && peekNextChar() == '>')
+ continue;
+ }
+
+ if (shouldRemoveNextClosingBrace && currentChar == '}')
+ {
+ currentLine[charNum] = currentChar = ' ';
+ shouldRemoveNextClosingBrace = false;
+ assert(adjustChecksumIn(-'}'));
+ if (isEmptyLine(currentLine))
+ continue;
+ }
+
+ // handle white space - needed to simplify the rest.
+ if (isWhiteSpace(currentChar))
+ {
+ appendCurrentChar();
+ continue;
+ }
+
+ /* not in MIDDLE of quote or comment or SQL or white-space of any type ... */
+
+ // check if in preprocessor
+ // ** isInPreprocessor will be automatically reset at the beginning
+ // of a new line in getnextChar()
+ if (currentChar == '#')
+ {
+ isInPreprocessor = true;
+ // check for run-in
+ if (formattedLine.length() > 0 && formattedLine[0] == '{')
+ {
+ isInLineBreak = true;
+ isInBraceRunIn = false;
+ }
+ processPreprocessor();
+ // if top level it is potentially indentable
+ if (shouldIndentPreprocBlock
+ && (isBraceType(braceTypeStack->back(), NULL_TYPE)
+ || isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
+ && !foundClassHeader
+ && !isInClassInitializer
+ && sourceIterator->tellg() > preprocBlockEnd)
+ {
+ // indent the #if preprocessor blocks
+ string preproc = ASBeautifier::extractPreprocessorStatement(currentLine);
+ if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef
+ {
+ if (isImmediatelyPostPreprocessor)
+ breakLine();
+ isIndentableProprocessorBlock = isIndentablePreprocessorBlock(currentLine, charNum);
+ isIndentableProprocessor = isIndentableProprocessorBlock;
+ }
+ }
+ if (isIndentableProprocessorBlock
+ && charNum < (int) currentLine.length() - 1
+ && isWhiteSpace(currentLine[charNum + 1]))
+ {
+ size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextText != string::npos)
+ currentLine.erase(charNum + 1, nextText - charNum - 1);
+ }
+ if (isIndentableProprocessorBlock
+ && sourceIterator->tellg() >= preprocBlockEnd)
+ isIndentableProprocessorBlock = false;
+ // need to fall thru here to reset the variables
+ }
+
+ /* not in preprocessor ... */
+
+ if (isImmediatelyPostComment)
+ {
+ caseHeaderFollowsComments = false;
+ isImmediatelyPostComment = false;
+ isCharImmediatelyPostComment = true;
+ }
+
+ if (isImmediatelyPostLineComment)
+ {
+ caseHeaderFollowsComments = false;
+ isImmediatelyPostLineComment = false;
+ isCharImmediatelyPostLineComment = true;
+ }
+
+ if (isImmediatelyPostReturn)
+ {
+ isImmediatelyPostReturn = false;
+ isCharImmediatelyPostReturn = true;
+ }
+
+ if (isImmediatelyPostThrow)
+ {
+ isImmediatelyPostThrow = false;
+ isCharImmediatelyPostThrow = true;
+ }
+
+ if (isImmediatelyPostNewDelete)
+ {
+ isImmediatelyPostNewDelete = false;
+ isCharImmediatelyPostNewDelete = true;
+ }
+
+ if (isImmediatelyPostOperator)
+ {
+ isImmediatelyPostOperator = false;
+ isCharImmediatelyPostOperator = true;
+ }
+ if (isImmediatelyPostTemplate)
+ {
+ isImmediatelyPostTemplate = false;
+ isCharImmediatelyPostTemplate = true;
+ }
+ if (isImmediatelyPostPointerOrReference)
+ {
+ isImmediatelyPostPointerOrReference = false;
+ isCharImmediatelyPostPointerOrReference = true;
+ }
+
+ // reset isImmediatelyPostHeader information
+ if (isImmediatelyPostHeader)
+ {
+ // should braces be added
+ if (currentChar != '{'
+ && shouldAddBraces
+ && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine)
+ && isOkToBreakBlock(braceTypeStack->back()))
+ {
+ bool bracesAdded = addBracesToStatement();
+ if (bracesAdded && !shouldAddOneLineBraces)
+ {
+ size_t firstText = currentLine.find_first_not_of(" \t");
+ assert(firstText != string::npos);
+ if ((int) firstText == charNum || shouldBreakOneLineHeaders)
+ breakCurrentOneLineBlock = true;
+ }
+ }
+ // should braces be removed
+ else if (currentChar == '{' && shouldRemoveBraces)
+ {
+ bool bracesRemoved = removeBracesFromStatement();
+ if (bracesRemoved)
+ {
+ shouldRemoveNextClosingBrace = true;
+ if (isBeforeAnyLineEndComment(charNum))
+ spacePadNum--;
+ else if (shouldBreakOneLineBlocks
+ || (currentLineBeginsWithBrace
+ && currentLine.find_first_not_of(" \t") != string::npos))
+ shouldBreakLineAtNextChar = true;
+ continue;
+ }
+ }
+
+ // break 'else-if' if shouldBreakElseIfs is requested
+ if (shouldBreakElseIfs
+ && currentHeader == &AS_ELSE
+ && isOkToBreakBlock(braceTypeStack->back())
+ && !isBeforeAnyComment()
+ && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine))
+ {
+ string nextText = peekNextText(currentLine.substr(charNum));
+ if (nextText.length() > 0
+ && isCharPotentialHeader(nextText, 0)
+ && ASBase::findHeader(nextText, 0, headers) == &AS_IF)
+ {
+ isInLineBreak = true;
+ }
+ }
+
+ // break a header (e.g. if, while, else) from the following statement
+ if (shouldBreakOneLineHeaders
+ && peekNextChar() != ' '
+ && (shouldBreakOneLineStatements
+ || (!isHeaderInMultiStatementLine
+ && !isMultiStatementLine()))
+ && isOkToBreakBlock(braceTypeStack->back())
+ && !isBeforeAnyComment())
+ {
+ if (currentChar == '{')
+ {
+ if (!currentLineBeginsWithBrace)
+ {
+ if (isOneLineBlockReached(currentLine, charNum) == 3)
+ isInLineBreak = false;
+ else
+ breakCurrentOneLineBlock = true;
+ }
+ }
+ else if (currentHeader == &AS_ELSE)
+ {
+ string nextText = peekNextText(currentLine.substr(charNum), true);
+ if (nextText.length() > 0
+ && ((isCharPotentialHeader(nextText, 0)
+ && ASBase::findHeader(nextText, 0, headers) != &AS_IF)
+ || nextText[0] == '{'))
+ isInLineBreak = true;
+ }
+ else
+ {
+ isInLineBreak = true;
+ }
+ }
+
+ isImmediatelyPostHeader = false;
+ }
+
+ if (passedSemicolon) // need to break the formattedLine
+ {
+ passedSemicolon = false;
+ if (parenStack->back() == 0 && !isCharImmediatelyPostComment && currentChar != ';') // allow ;;
+ {
+ // does a one-line block have ending comments?
+ if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
+ {
+ size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACE);
+ assert(blockEnd != string::npos);
+ // move ending comments to this formattedLine
+ if (isBeforeAnyLineEndComment(blockEnd))
+ {
+ size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1);
+ assert(commentStart != string::npos);
+ assert((currentLine.compare(commentStart, 2, "//") == 0)
+ || (currentLine.compare(commentStart, 2, "/*") == 0));
+ formattedLine.append(getIndentLength() - 1, ' ');
+ // append comment
+ int charNumSave = charNum;
+ charNum = commentStart;
+ while (charNum < (int) currentLine.length())
+ {
+ currentChar = currentLine[charNum];
+ if (currentChar == '\t' && shouldConvertTabs)
+ convertTabToSpaces();
+ formattedLine.append(1, currentChar);
+ ++charNum;
+ }
+ size_t commentLength = currentLine.length() - commentStart;
+ currentLine.erase(commentStart, commentLength);
+ charNum = charNumSave;
+ currentChar = currentLine[charNum];
+ testForTimeToSplitFormattedLine();
+ }
+ }
+ isInExecSQL = false;
+ shouldReparseCurrentChar = true;
+ if (formattedLine.find_first_not_of(" \t") != string::npos)
+ isInLineBreak = true;
+ if (needHeaderOpeningBrace)
+ {
+ isCharImmediatelyPostCloseBlock = true;
+ needHeaderOpeningBrace = false;
+ }
+ continue;
+ }
+ }
+
+ if (passedColon)
+ {
+ passedColon = false;
+ if (parenStack->back() == 0
+ && !isBeforeAnyComment()
+ && (formattedLine.find_first_not_of(" \t") != string::npos))
+ {
+ shouldReparseCurrentChar = true;
+ isInLineBreak = true;
+ continue;
+ }
+ }
+
+ // Check if in template declaration, e.g. foo<bar> or foo<bar,fig>
+ if (!isInTemplate && currentChar == '<')
+ {
+ checkIfTemplateOpener();
+ }
+
+ // handle parens
+ if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<'))
+ {
+ questionMarkStack->push_back(foundQuestionMark);
+ foundQuestionMark = false;
+ parenStack->back()++;
+ if (currentChar == '[')
+ {
+ ++squareBracketCount;
+ if (getAlignMethodColon() && squareBracketCount == 1 && isCStyle())
+ objCColonAlign = findObjCColonAlignment();
+ }
+ }
+ else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>'))
+ {
+ foundPreCommandHeader = false;
+ parenStack->back()--;
+ // this can happen in preprocessor directives
+ if (parenStack->back() < 0)
+ parenStack->back() = 0;
+ if (!questionMarkStack->empty())
+ {
+ foundQuestionMark = questionMarkStack->back();
+ questionMarkStack->pop_back();
+ }
+ if (isInTemplate && currentChar == '>')
+ {
+ templateDepth--;
+ if (templateDepth == 0)
+ {
+ isInTemplate = false;
+ isImmediatelyPostTemplate = true;
+ }
+ }
+
+ // check if this parenthesis closes a header, e.g. if (...), while (...)
+ if (isInHeader && parenStack->back() == 0)
+ {
+ isInHeader = false;
+ isImmediatelyPostHeader = true;
+ foundQuestionMark = false;
+ }
+ if (currentChar == ']')
+ {
+ --squareBracketCount;
+ if (squareBracketCount <= 0)
+ {
+ squareBracketCount = 0;
+ objCColonAlign = 0;
+ }
+ }
+ if (currentChar == ')')
+ {
+ foundCastOperator = false;
+ if (parenStack->back() == 0)
+ endOfAsmReached = true;
+ }
+ }
+
+ // handle braces
+ if (currentChar == '{' || currentChar == '}')
+ {
+ // if appendOpeningBrace this was already done for the original brace
+ if (currentChar == '{' && !appendOpeningBrace)
+ {
+ BraceType newBraceType = getBraceType();
+ breakCurrentOneLineBlock = false;
+ foundNamespaceHeader = false;
+ foundClassHeader = false;
+ foundStructHeader = false;
+ foundInterfaceHeader = false;
+ foundPreDefinitionHeader = false;
+ foundPreCommandHeader = false;
+ foundPreCommandMacro = false;
+ foundTrailingReturnType = false;
+ isInPotentialCalculation = false;
+ isInObjCMethodDefinition = false;
+ isInObjCInterface = false;
+ isInEnum = false;
+ isJavaStaticConstructor = false;
+ isCharImmediatelyPostNonInStmt = false;
+ needHeaderOpeningBrace = false;
+ shouldKeepLineUnbroken = false;
+ objCColonAlign = 0;
+
+ isPreviousBraceBlockRelated = !isBraceType(newBraceType, ARRAY_TYPE);
+ braceTypeStack->emplace_back(newBraceType);
+ preBraceHeaderStack->emplace_back(currentHeader);
+ currentHeader = nullptr;
+ structStack->push_back(isInIndentableStruct);
+ if (isBraceType(newBraceType, STRUCT_TYPE) && isCStyle())
+ isInIndentableStruct = isStructAccessModified(currentLine, charNum);
+ else
+ isInIndentableStruct = false;
+ }
+
+ // this must be done before the braceTypeStack is popped
+ BraceType braceType = braceTypeStack->back();
+ bool isOpeningArrayBrace = (isBraceType(braceType, ARRAY_TYPE)
+ && braceTypeStack->size() >= 2
+ && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2], ARRAY_TYPE)
+ );
+
+ if (currentChar == '}')
+ {
+ // if a request has been made to append a post block empty line,
+ // but the block exists immediately before a closing brace,
+ // then there is no need for the post block empty line.
+ isAppendPostBlockEmptyLineRequested = false;
+ if (isInAsm)
+ endOfAsmReached = true;
+ isInAsmOneLine = isInQuote = false;
+ shouldKeepLineUnbroken = false;
+ squareBracketCount = 0;
+
+ if (braceTypeStack->size() > 1)
+ {
+ previousBraceType = braceTypeStack->back();
+ braceTypeStack->pop_back();
+ isPreviousBraceBlockRelated = !isBraceType(braceType, ARRAY_TYPE);
+ }
+ else
+ {
+ previousBraceType = NULL_TYPE;
+ isPreviousBraceBlockRelated = false;
+ }
+
+ if (!preBraceHeaderStack->empty())
+ {
+ currentHeader = preBraceHeaderStack->back();
+ preBraceHeaderStack->pop_back();
+ }
+ else
+ currentHeader = nullptr;
+
+ if (!structStack->empty())
+ {
+ isInIndentableStruct = structStack->back();
+ structStack->pop_back();
+ }
+ else
+ isInIndentableStruct = false;
+
+ if (isNonInStatementArray
+ && (!isBraceType(braceTypeStack->back(), ARRAY_TYPE) // check previous brace
+ || peekNextChar() == ';')) // check for "};" added V2.01
+ isImmediatelyPostNonInStmt = true;
+
+ if (!shouldBreakOneLineStatements
+ && ASBeautifier::getNextWord(currentLine, charNum) == AS_ELSE)
+ {
+ // handle special case of "else" at the end of line
+ size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ')
+ shouldBreakLineAtNextChar = true;
+ }
+ }
+
+ // format braces
+ appendOpeningBrace = false;
+ if (isBraceType(braceType, ARRAY_TYPE))
+ {
+ formatArrayBraces(braceType, isOpeningArrayBrace);
+ }
+ else
+ {
+ if (currentChar == '{')
+ formatOpeningBrace(braceType);
+ else
+ formatClosingBrace(braceType);
+ }
+ continue;
+ }
+
+ if ((((previousCommandChar == '{' && isPreviousBraceBlockRelated)
+ || ((previousCommandChar == '}'
+ && !isImmediatelyPostEmptyBlock
+ && isPreviousBraceBlockRelated
+ && !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments
+ && peekNextChar() != ' '
+ && !isBraceType(previousBraceType, DEFINITION_TYPE))
+ && !isBraceType(braceTypeStack->back(), DEFINITION_TYPE)))
+ && isOkToBreakBlock(braceTypeStack->back()))
+ // check for array
+ || (previousCommandChar == '{' // added 9/30/2010
+ && isBraceType(braceTypeStack->back(), ARRAY_TYPE)
+ && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
+ && isNonInStatementArray)
+ // check for pico one line braces
+ || (formattingStyle == STYLE_PICO
+ && (previousCommandChar == '{' && isPreviousBraceBlockRelated)
+ && isBraceType(braceTypeStack->back(), COMMAND_TYPE)
+ && isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
+ && braceFormatMode == RUN_IN_MODE)
+ )
+ {
+ isCharImmediatelyPostOpenBlock = (previousCommandChar == '{');
+ isCharImmediatelyPostCloseBlock = (previousCommandChar == '}');
+
+ if (isCharImmediatelyPostOpenBlock
+ && !isCharImmediatelyPostComment
+ && !isCharImmediatelyPostLineComment)
+ {
+ previousCommandChar = ' ';
+
+ if (braceFormatMode == NONE_MODE)
+ {
+ if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
+ && (isBraceType(braceTypeStack->back(), BREAK_BLOCK_TYPE)
+ || shouldBreakOneLineBlocks))
+ isInLineBreak = true;
+ else if (currentLineBeginsWithBrace)
+ formatRunIn();
+ else
+ breakLine();
+ }
+ else if (braceFormatMode == RUN_IN_MODE
+ && currentChar != '#')
+ formatRunIn();
+ else
+ isInLineBreak = true;
+ }
+ else if (isCharImmediatelyPostCloseBlock
+ && shouldBreakOneLineStatements
+ && !isCharImmediatelyPostComment
+ && ((isLegalNameChar(currentChar) && currentChar != '.')
+ || currentChar == '+'
+ || currentChar == '-'
+ || currentChar == '*'
+ || currentChar == '&'
+ || currentChar == '('))
+ {
+ previousCommandChar = ' ';
+ isInLineBreak = true;
+ }
+ }
+
+ // reset block handling flags
+ isImmediatelyPostEmptyBlock = false;
+
+ // look for headers
+ bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum);
+
+ if (isPotentialHeader && !isInTemplate && squareBracketCount == 0)
+ {
+ isNonParenHeader = false;
+ foundClosingHeader = false;
+
+ newHeader = findHeader(headers);
+
+ // Qt headers may be variables in C++
+ if (isCStyle()
+ && (newHeader == &AS_FOREVER || newHeader == &AS_FOREACH))
+ {
+ if (currentLine.find_first_of("=;", charNum) != string::npos)
+ newHeader = nullptr;
+ }
+ if (isJavaStyle()
+ && (newHeader == &AS_SYNCHRONIZED))
+ {
+ // want synchronized statements not synchronized methods
+ if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE))
+ newHeader = nullptr;
+ }
+ else if (newHeader == &AS_USING
+ && ASBeautifier::peekNextChar(
+ currentLine, charNum + (*newHeader).length() - 1) != '(')
+ newHeader = nullptr;
+
+ if (newHeader != nullptr)
+ {
+ foundClosingHeader = isClosingHeader(newHeader);
+
+ if (!foundClosingHeader)
+ {
+ // these are closing headers
+ if ((newHeader == &AS_WHILE && currentHeader == &AS_DO)
+ || (newHeader == &_AS_FINALLY && currentHeader == &_AS_TRY)
+ || (newHeader == &_AS_EXCEPT && currentHeader == &_AS_TRY))
+ foundClosingHeader = true;
+ // don't append empty block for these related headers
+ else if (isSharpStyle()
+ && previousNonWSChar == '}'
+ && ((newHeader == &AS_SET && currentHeader == &AS_GET)
+ || (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
+ && isOkToBreakBlock(braceTypeStack->back()))
+ isAppendPostBlockEmptyLineRequested = false;
+ }
+
+ // TODO: this can be removed in a future release
+ // version 3.0 - break erroneous attached header from previous versions
+ if (isSharpStyle()
+ && ((newHeader == &AS_SET && currentHeader == &AS_GET)
+ || (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
+ && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
+ && currentLine[currentLine.find_first_not_of(" \t")] == '}')
+ isInLineBreak = true;
+ // END TODO
+
+ const string* previousHeader = currentHeader;
+ currentHeader = newHeader;
+ needHeaderOpeningBrace = true;
+
+ // is the previous statement on the same line?
+ if ((previousNonWSChar == ';' || previousNonWSChar == ':')
+ && !isInLineBreak
+ && isOkToBreakBlock(braceTypeStack->back()))
+ {
+ // if breaking lines, break the line at the header
+ // except for multiple 'case' statements on a line
+ if (maxCodeLength != string::npos
+ && previousHeader != &AS_CASE)
+ isInLineBreak = true;
+ else
+ isHeaderInMultiStatementLine = true;
+ }
+
+ if (foundClosingHeader && previousNonWSChar == '}')
+ {
+ if (isOkToBreakBlock(braceTypeStack->back()))
+ isLineBreakBeforeClosingHeader();
+
+ // get the adjustment for a comment following the closing header
+ if (isInLineBreak)
+ nextLineSpacePadNum = getNextLineCommentAdjustment();
+ else
+ spacePadNum = getCurrentLineCommentAdjustment();
+ }
+
+ // check if the found header is non-paren header
+ isNonParenHeader = findHeader(nonParenHeaders) != nullptr;
+
+ if (isNonParenHeader
+ && (currentHeader == &AS_CATCH
+ || currentHeader == &AS_CASE))
+ {
+ int startChar = charNum + currentHeader->length() - 1;
+ if (ASBeautifier::peekNextChar(currentLine, startChar) == '(')
+ isNonParenHeader = false;
+ }
+
+ // join 'else if' statements
+ if (currentHeader == &AS_IF
+ && previousHeader == &AS_ELSE
+ && isInLineBreak
+ && !shouldBreakElseIfs
+ && !isCharImmediatelyPostLineComment
+ && !isImmediatelyPostPreprocessor)
+ {
+ // 'else' must be last thing on the line
+ size_t start = formattedLine.length() >= 6 ? formattedLine.length() - 6 : 0;
+ if (formattedLine.find(AS_ELSE, start) != string::npos)
+ {
+ appendSpacePad();
+ isInLineBreak = false;
+ }
+ }
+
+ appendSequence(*currentHeader);
+ goForward(currentHeader->length() - 1);
+ // if a paren-header is found add a space after it, if needed
+ // this checks currentLine, appendSpacePad() checks formattedLine
+ // in 'case' and C# 'catch' can be either a paren or non-paren header
+ if (shouldPadHeader
+ && !isNonParenHeader
+ && charNum < (int) currentLine.length() - 1 && !isWhiteSpace(currentLine[charNum + 1]))
+ appendSpacePad();
+
+ // Signal that a header has been reached
+ // *** But treat a closing while() (as in do...while)
+ // as if it were NOT a header since a closing while()
+ // should never have a block after it!
+ if (currentHeader != &AS_CASE && currentHeader != &AS_DEFAULT
+ && !(foundClosingHeader && currentHeader == &AS_WHILE))
+ {
+ isInHeader = true;
+
+ // in C# 'catch' and 'delegate' can be a paren or non-paren header
+ if (isNonParenHeader && !isSharpStyleWithParen(currentHeader))
+ {
+ isImmediatelyPostHeader = true;
+ isInHeader = false;
+ }
+ }
+
+ if (shouldBreakBlocks
+ && isOkToBreakBlock(braceTypeStack->back())
+ && !isHeaderInMultiStatementLine)
+ {
+ if (previousHeader == nullptr
+ && !foundClosingHeader
+ && !isCharImmediatelyPostOpenBlock
+ && !isImmediatelyPostCommentOnly)
+ {
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+
+ if (isClosingHeader(currentHeader)
+ || foundClosingHeader)
+ {
+ isPrependPostBlockEmptyLineRequested = false;
+ }
+
+ if (shouldBreakClosingHeaderBlocks
+ && isCharImmediatelyPostCloseBlock
+ && !isImmediatelyPostCommentOnly
+ && !(currentHeader == &AS_WHILE // do-while
+ && foundClosingHeader))
+ {
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+ }
+
+ if (currentHeader == &AS_CASE
+ || currentHeader == &AS_DEFAULT)
+ isInCase = true;
+
+ continue;
+ }
+ else if ((newHeader = findHeader(preDefinitionHeaders)) != nullptr
+ && parenStack->back() == 0
+ && !isInEnum) // not C++11 enum class
+ {
+ if (newHeader == &AS_NAMESPACE || newHeader == &AS_MODULE)
+ foundNamespaceHeader = true;
+ if (newHeader == &AS_CLASS)
+ foundClassHeader = true;
+ if (newHeader == &AS_STRUCT)
+ foundStructHeader = true;
+ if (newHeader == &AS_INTERFACE)
+ foundInterfaceHeader = true;
+ foundPreDefinitionHeader = true;
+ appendSequence(*newHeader);
+ goForward(newHeader->length() - 1);
+
+ continue;
+ }
+ else if ((newHeader = findHeader(preCommandHeaders)) != nullptr)
+ {
+ // a 'const' variable is not a preCommandHeader
+ if (previousNonWSChar == ')')
+ foundPreCommandHeader = true;
+ }
+ else if ((newHeader = findHeader(castOperators)) != nullptr)
+ {
+ foundCastOperator = true;
+ appendSequence(*newHeader);
+ goForward(newHeader->length() - 1);
+ continue;
+ }
+ } // (isPotentialHeader && !isInTemplate)
+
+ if (isInLineBreak) // OK to break line here
+ {
+ breakLine();
+ if (isInVirginLine) // adjust for the first line
+ {
+ lineCommentNoBeautify = lineCommentNoIndent;
+ lineCommentNoIndent = false;
+ if (isImmediatelyPostPreprocessor)
+ {
+ isInIndentablePreproc = isIndentableProprocessor;
+ isIndentableProprocessor = false;
+ }
+ }
+ }
+
+ if (previousNonWSChar == '}' || currentChar == ';')
+ {
+ if (currentChar == ';')
+ {
+ squareBracketCount = 0;
+
+ if (((shouldBreakOneLineStatements
+ || isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
+ && isOkToBreakBlock(braceTypeStack->back()))
+ && !(attachClosingBraceMode && peekNextChar() == '}'))
+ {
+ passedSemicolon = true;
+ }
+ else if (!shouldBreakOneLineStatements
+ && ASBeautifier::getNextWord(currentLine, charNum) == AS_ELSE)
+ {
+ // handle special case of "else" at the end of line
+ size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ')
+ passedSemicolon = true;
+ }
+
+ if (shouldBreakBlocks
+ && currentHeader != nullptr
+ && currentHeader != &AS_CASE
+ && currentHeader != &AS_DEFAULT
+ && !isHeaderInMultiStatementLine
+ && parenStack->back() == 0)
+ {
+ isAppendPostBlockEmptyLineRequested = true;
+ }
+ }
+ if (currentChar != ';'
+ || (needHeaderOpeningBrace && parenStack->back() == 0))
+ currentHeader = nullptr;
+ resetEndOfStatement();
+ }
+
+ if (currentChar == ':'
+ && previousChar != ':' // not part of '::'
+ && peekNextChar() != ':') // not part of '::'
+ {
+ if (isInCase)
+ {
+ isInCase = false;
+ if (shouldBreakOneLineStatements)
+ passedColon = true;
+ }
+ else if (isCStyle() // for C/C++ only
+ && isOkToBreakBlock(braceTypeStack->back())
+ && shouldBreakOneLineStatements
+ && !foundQuestionMark // not in a ?: sequence
+ && !foundPreDefinitionHeader // not in a definition block
+ && previousCommandChar != ')' // not after closing paren of a method header
+ && !foundPreCommandHeader // not after a 'noexcept'
+ && squareBracketCount == 0 // not in objC method call
+ && !isInObjCMethodDefinition // not objC '-' or '+' method
+ && !isInObjCInterface // not objC @interface
+ && !isInObjCSelector // not objC @selector
+ && !isDigit(peekNextChar()) // not a bit field
+ && !isInEnum // not an enum with a base type
+ && !isInAsm // not in extended assembler
+ && !isInAsmOneLine // not in extended assembler
+ && !isInAsmBlock) // not in extended assembler
+ {
+ passedColon = true;
+ }
+
+ if (isCStyle()
+ && shouldPadMethodColon
+ && (squareBracketCount > 0 || isInObjCMethodDefinition || isInObjCSelector)
+ && !foundQuestionMark) // not in a ?: sequence
+ padObjCMethodColon();
+
+ if (isInObjCInterface)
+ {
+ appendSpacePad();
+ if ((int) currentLine.length() > charNum + 1
+ && !isWhiteSpace(currentLine[charNum + 1]))
+ currentLine.insert(charNum + 1, " ");
+ }
+
+ if (isClassInitializer())
+ isInClassInitializer = true;
+ }
+
+ if (currentChar == '?')
+ foundQuestionMark = true;
+
+ if (isPotentialHeader && !isInTemplate)
+ {
+ if (findKeyword(currentLine, charNum, AS_NEW)
+ || findKeyword(currentLine, charNum, AS_DELETE))
+ {
+ isInPotentialCalculation = false;
+ isImmediatelyPostNewDelete = true;
+ }
+
+ if (findKeyword(currentLine, charNum, AS_RETURN))
+ {
+ isInPotentialCalculation = true; // return is the same as an = sign
+ isImmediatelyPostReturn = true;
+ }
+
+ if (findKeyword(currentLine, charNum, AS_OPERATOR))
+ isImmediatelyPostOperator = true;
+
+ if (findKeyword(currentLine, charNum, AS_ENUM))
+ {
+ size_t firstNum = currentLine.find_first_of("(){},/");
+ if (firstNum == string::npos
+ || currentLine[firstNum] == '{'
+ || currentLine[firstNum] == '/')
+ isInEnum = true;
+ }
+
+ if (isCStyle()
+ && findKeyword(currentLine, charNum, AS_THROW)
+ && previousCommandChar != ')'
+ && !foundPreCommandHeader) // 'const' throw()
+ isImmediatelyPostThrow = true;
+
+ if (isCStyle() && findKeyword(currentLine, charNum, AS_EXTERN) && isExternC())
+ isInExternC = true;
+
+ if (isCStyle() && findKeyword(currentLine, charNum, AS_AUTO)
+ && (isBraceType(braceTypeStack->back(), NULL_TYPE)
+ || isBraceType(braceTypeStack->back(), NAMESPACE_TYPE)
+ || isBraceType(braceTypeStack->back(), CLASS_TYPE)))
+ foundTrailingReturnType = true;
+
+ // Objective-C NSException macros are preCommandHeaders
+ if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_DURING))
+ foundPreCommandMacro = true;
+ if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_HANDLER))
+ foundPreCommandMacro = true;
+
+ if (isCStyle() && isExecSQL(currentLine, charNum))
+ isInExecSQL = true;
+
+ if (isCStyle())
+ {
+ if (findKeyword(currentLine, charNum, AS_ASM)
+ || findKeyword(currentLine, charNum, AS__ASM__))
+ {
+ isInAsm = true;
+ }
+ else if (findKeyword(currentLine, charNum, AS_MS_ASM) // microsoft specific
+ || findKeyword(currentLine, charNum, AS_MS__ASM))
+ {
+ int index = 4;
+ if (peekNextChar() == '_') // check for __asm
+ index = 5;
+
+ char peekedChar = ASBase::peekNextChar(currentLine, charNum + index);
+ if (peekedChar == '{' || peekedChar == ' ')
+ isInAsmBlock = true;
+ else
+ isInAsmOneLine = true;
+ }
+ }
+
+ if (isJavaStyle()
+ && (findKeyword(currentLine, charNum, AS_STATIC)
+ && isNextCharOpeningBrace(charNum + 6)))
+ isJavaStaticConstructor = true;
+
+ if (isSharpStyle()
+ && (findKeyword(currentLine, charNum, AS_DELEGATE)
+ || findKeyword(currentLine, charNum, AS_UNCHECKED)))
+ isSharpDelegate = true;
+
+ // append the entire name
+ string name = getCurrentWord(currentLine, charNum);
+ // must pad the 'and' and 'or' operators if required
+ if (name == "and" || name == "or")
+ {
+ if (shouldPadOperators && previousNonWSChar != ':')
+ {
+ appendSpacePad();
+ appendOperator(name);
+ goForward(name.length() - 1);
+ if (!isBeforeAnyComment()
+ && !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0)
+ && !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0))
+ appendSpaceAfter();
+ }
+ else
+ {
+ appendOperator(name);
+ goForward(name.length() - 1);
+ }
+ }
+ else
+ {
+ appendSequence(name);
+ goForward(name.length() - 1);
+ }
+
+ continue;
+
+ } // (isPotentialHeader && !isInTemplate)
+
+ // determine if this is an Objective-C statement
+
+ if (currentChar == '@'
+ && isCharPotentialHeader(currentLine, charNum + 1)
+ && findKeyword(currentLine, charNum + 1, AS_INTERFACE)
+ && isBraceType(braceTypeStack->back(), NULL_TYPE))
+ {
+ isInObjCInterface = true;
+ string name = '@' + AS_INTERFACE;
+ appendSequence(name);
+ goForward(name.length() - 1);
+ continue;
+ }
+ else if (currentChar == '@'
+ && isCharPotentialHeader(currentLine, charNum + 1)
+ && findKeyword(currentLine, charNum + 1, AS_SELECTOR))
+ {
+ isInObjCSelector = true;
+ string name = '@' + AS_SELECTOR;
+ appendSequence(name);
+ goForward(name.length() - 1);
+ continue;
+ }
+ else if ((currentChar == '-' || currentChar == '+')
+ && (int) currentLine.find_first_not_of(" \t") == charNum
+ && peekNextChar() == '('
+ && isBraceType(braceTypeStack->back(), NULL_TYPE)
+ && !isInPotentialCalculation)
+ {
+ isInObjCMethodDefinition = true;
+ isImmediatelyPostObjCMethodPrefix = true;
+ isInObjCInterface = false;
+ if (getAlignMethodColon())
+ objCColonAlign = findObjCColonAlignment();
+ appendCurrentChar();
+ continue;
+ }
+
+ // determine if this is a potential calculation
+
+ bool isPotentialOperator = isCharPotentialOperator(currentChar);
+ newHeader = nullptr;
+
+ if (isPotentialOperator)
+ {
+ newHeader = findOperator(operators);
+
+ // check for Java ? wildcard
+ if (newHeader != nullptr
+ && newHeader == &AS_GCC_MIN_ASSIGN
+ && isJavaStyle()
+ && isInTemplate)
+ newHeader = nullptr;
+
+ if (newHeader != nullptr)
+ {
+ if (newHeader == &AS_LAMBDA)
+ foundPreCommandHeader = true;
+
+ // correct mistake of two >> closing a template
+ if (isInTemplate && (newHeader == &AS_GR_GR || newHeader == &AS_GR_GR_GR))
+ newHeader = &AS_GR;
+
+ if (!isInPotentialCalculation)
+ {
+ // must determine if newHeader is an assignment operator
+ // do NOT use findOperator - the length must be exact!!!
+ if (find(begin(*assignmentOperators), end(*assignmentOperators), newHeader)
+ != end(*assignmentOperators))
+ {
+ foundPreCommandHeader = false;
+ char peekedChar = peekNextChar();
+ isInPotentialCalculation = !(newHeader == &AS_EQUAL && peekedChar == '*')
+ && !(newHeader == &AS_EQUAL && peekedChar == '&')
+ && !isCharImmediatelyPostOperator;
+ }
+ }
+ }
+ }
+
+ // process pointers and references
+ // check newHeader to eliminate things like '&&' sequence
+ if (newHeader != nullptr && !isJavaStyle()
+ && (newHeader == &AS_MULT
+ || newHeader == &AS_BIT_AND
+ || newHeader == &AS_BIT_XOR
+ || newHeader == &AS_AND)
+ && isPointerOrReference())
+ {
+ if (!isDereferenceOrAddressOf() && !isOperatorPaddingDisabled())
+ formatPointerOrReference();
+ else
+ {
+ appendOperator(*newHeader);
+ goForward(newHeader->length() - 1);
+ }
+ isImmediatelyPostPointerOrReference = true;
+ continue;
+ }
+
+ if (shouldPadOperators && newHeader != nullptr && !isOperatorPaddingDisabled())
+ {
+ padOperators(newHeader);
+ continue;
+ }
+
+ // remove spaces before commas
+ if (currentChar == ',')
+ {
+ const size_t len = formattedLine.length();
+ size_t lastText = formattedLine.find_last_not_of(' ');
+ if (lastText != string::npos && lastText < len - 1)
+ {
+ formattedLine.resize(lastText + 1);
+ int size_diff = len - (lastText + 1);
+ spacePadNum -= size_diff;
+ }
+ }
+
+ // pad commas and semi-colons
+ if (currentChar == ';'
+ || (currentChar == ',' && (shouldPadOperators || shouldPadCommas)))
+ {
+ char nextChar = ' ';
+ if (charNum + 1 < (int) currentLine.length())
+ nextChar = currentLine[charNum + 1];
+ if (!isWhiteSpace(nextChar)
+ && nextChar != '}'
+ && nextChar != ')'
+ && nextChar != ']'
+ && nextChar != '>'
+ && nextChar != ';'
+ && !isBeforeAnyComment()
+ /* && !(isBraceType(braceTypeStack->back(), ARRAY_TYPE)) */
+ )
+ {
+ appendCurrentChar();
+ appendSpaceAfter();
+ continue;
+ }
+ }
+
+ // pad parens
+ if (currentChar == '(' || currentChar == ')')
+ {
+ if (currentChar == '(')
+ {
+ if (shouldPadHeader
+ && (isCharImmediatelyPostReturn
+ || isCharImmediatelyPostThrow
+ || isCharImmediatelyPostNewDelete))
+ appendSpacePad();
+ }
+
+ if (shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen)
+ padParens();
+ else
+ appendCurrentChar();
+
+ if (isInObjCMethodDefinition)
+ {
+ if (currentChar == '(' && isImmediatelyPostObjCMethodPrefix)
+ {
+ if (shouldPadMethodPrefix || shouldUnPadMethodPrefix)
+ padObjCMethodPrefix();
+ isImmediatelyPostObjCMethodPrefix = false;
+ isInObjCReturnType = true;
+ }
+ else if (currentChar == ')' && isInObjCReturnType)
+ {
+ if (shouldPadReturnType || shouldUnPadReturnType)
+ padObjCReturnType();
+ isInObjCReturnType = false;
+ }
+ else if (shouldPadParamType || shouldUnPadParamType)
+ padObjCParamType();
+ }
+ continue;
+ }
+
+ // bypass the entire operator
+ if (newHeader != nullptr)
+ {
+ appendOperator(*newHeader);
+ goForward(newHeader->length() - 1);
+ continue;
+ }
+
+ appendCurrentChar();
+
+ } // end of while loop * end of while loop * end of while loop * end of while loop
+
+ // return a beautified (i.e. correctly indented) line.
+
+ string beautifiedLine;
+ size_t readyFormattedLineLength = trim(readyFormattedLine).length();
+ bool isInNamespace = isBraceType(braceTypeStack->back(), NAMESPACE_TYPE);
+
+ if (prependEmptyLine // prepend a blank line before this formatted line
+ && readyFormattedLineLength > 0
+ && previousReadyFormattedLineLength > 0)
+ {
+ isLineReady = true; // signal a waiting readyFormattedLine
+ beautifiedLine = beautify("");
+ previousReadyFormattedLineLength = 0;
+ // call the enhancer for new empty lines
+ enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL);
+ }
+ else // format the current formatted line
+ {
+ isLineReady = false;
+ runInIndentContinuation = runInIndentChars;
+ beautifiedLine = beautify(readyFormattedLine);
+ previousReadyFormattedLineLength = readyFormattedLineLength;
+ // the enhancer is not called for no-indent line comments
+ if (!lineCommentNoBeautify && !isFormattingModeOff)
+ enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL);
+ runInIndentChars = 0;
+ lineCommentNoBeautify = lineCommentNoIndent;
+ lineCommentNoIndent = false;
+ isInIndentablePreproc = isIndentableProprocessor;
+ isIndentableProprocessor = false;
+ isElseHeaderIndent = elseHeaderFollowsComments;
+ isCaseHeaderCommentIndent = caseHeaderFollowsComments;
+ objCColonAlignSubsequent = objCColonAlign;
+ if (isCharImmediatelyPostNonInStmt)
+ {
+ isNonInStatementArray = false;
+ isCharImmediatelyPostNonInStmt = false;
+ }
+ isInPreprocessorBeautify = isInPreprocessor; // used by ASEnhancer
+ isInBeautifySQL = isInExecSQL; // used by ASEnhancer
+ }
+
+ prependEmptyLine = false;
+ assert(computeChecksumOut(beautifiedLine));
+ return beautifiedLine;
+}
+
+/**
+ * check if there are any indented lines ready to be read by nextLine()
+ *
+ * @return are there any indented lines ready?
+ */
+bool ASFormatter::hasMoreLines() const
+{
+ return !endOfCodeReached;
+}
+
+/**
+ * comparison function for BraceType enum
+ */
+bool ASFormatter::isBraceType(BraceType a, BraceType b) const
+{
+ if (a == NULL_TYPE || b == NULL_TYPE)
+ return (a == b);
+ return ((a & b) == b);
+}
+
+/**
+ * set the formatting style.
+ *
+ * @param style the formatting style.
+ */
+void ASFormatter::setFormattingStyle(FormatStyle style)
+{
+ formattingStyle = style;
+}
+
+/**
+ * set the add braces mode.
+ * options:
+ * true braces added to headers for single line statements.
+ * false braces NOT added to headers for single line statements.
+ *
+ * @param state the add braces state.
+ */
+void ASFormatter::setAddBracesMode(bool state)
+{
+ shouldAddBraces = state;
+}
+
+/**
+ * set the add one line braces mode.
+ * options:
+ * true one line braces added to headers for single line statements.
+ * false one line braces NOT added to headers for single line statements.
+ *
+ * @param state the add one line braces state.
+ */
+void ASFormatter::setAddOneLineBracesMode(bool state)
+{
+ shouldAddBraces = state;
+ shouldAddOneLineBraces = state;
+}
+
+/**
+ * set the remove braces mode.
+ * options:
+ * true braces removed from headers for single line statements.
+ * false braces NOT removed from headers for single line statements.
+ *
+ * @param state the remove braces state.
+ */
+void ASFormatter::setRemoveBracesMode(bool state)
+{
+ shouldRemoveBraces = state;
+}
+
+// retained for compatability with release 2.06
+// "Brackets" have been changed to "Braces" in 3.0
+// it is referenced only by the old "bracket" options
+void ASFormatter::setAddBracketsMode(bool state)
+{
+ setAddBracesMode(state);
+}
+
+// retained for compatability with release 2.06
+// "Brackets" have been changed to "Braces" in 3.0
+// it is referenced only by the old "bracket" options
+void ASFormatter::setAddOneLineBracketsMode(bool state)
+{
+ setAddOneLineBracesMode(state);
+}
+
+// retained for compatability with release 2.06
+// "Brackets" have been changed to "Braces" in 3.0
+// it is referenced only by the old "bracket" options
+void ASFormatter::setRemoveBracketsMode(bool state)
+{
+ setRemoveBracesMode(state);
+}
+
+// retained for compatability with release 2.06
+// "Brackets" have been changed to "Braces" in 3.0
+// it is referenced only by the old "bracket" options
+void ASFormatter::setBreakClosingHeaderBracketsMode(bool state)
+{
+ setBreakClosingHeaderBracesMode(state);
+}
+
+
+/**
+ * set the brace formatting mode.
+ * options:
+ *
+ * @param mode the brace formatting mode.
+ */
+void ASFormatter::setBraceFormatMode(BraceMode mode)
+{
+ braceFormatMode = mode;
+}
+
+/**
+ * set 'break after' mode for maximum code length
+ *
+ * @param state the 'break after' mode.
+ */
+void ASFormatter::setBreakAfterMode(bool state)
+{
+ shouldBreakLineAfterLogical = state;
+}
+
+/**
+ * set closing header brace breaking mode
+ * options:
+ * true braces just before closing headers (e.g. 'else', 'catch')
+ * will be broken, even if standard braces are attached.
+ * false closing header braces will be treated as standard braces.
+ *
+ * @param state the closing header brace breaking mode.
+ */
+void ASFormatter::setBreakClosingHeaderBracesMode(bool state)
+{
+ shouldBreakClosingHeaderBraces = state;
+}
+
+/**
+ * set 'else if()' breaking mode
+ * options:
+ * true 'else' headers will be broken from their succeeding 'if' headers.
+ * false 'else' headers will be attached to their succeeding 'if' headers.
+ *
+ * @param state the 'else if()' breaking mode.
+ */
+void ASFormatter::setBreakElseIfsMode(bool state)
+{
+ shouldBreakElseIfs = state;
+}
+
+/**
+* set comma padding mode.
+* options:
+* true statement commas and semicolons will be padded with spaces around them.
+* false statement commas and semicolons will not be padded.
+*
+* @param state the padding mode.
+*/
+void ASFormatter::setCommaPaddingMode(bool state)
+{
+ shouldPadCommas = state;
+}
+
+/**
+ * set maximum code length
+ *
+ * @param max the maximum code length.
+ */
+void ASFormatter::setMaxCodeLength(int max)
+{
+ maxCodeLength = max;
+}
+
+/**
+ * set operator padding mode.
+ * options:
+ * true statement operators will be padded with spaces around them.
+ * false statement operators will not be padded.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setOperatorPaddingMode(bool state)
+{
+ shouldPadOperators = state;
+}
+
+/**
+ * set parenthesis outside padding mode.
+ * options:
+ * true statement parentheses will be padded with spaces around them.
+ * false statement parentheses will not be padded.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setParensOutsidePaddingMode(bool state)
+{
+ shouldPadParensOutside = state;
+}
+
+/**
+ * set parenthesis inside padding mode.
+ * options:
+ * true statement parenthesis will be padded with spaces around them.
+ * false statement parenthesis will not be padded.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setParensInsidePaddingMode(bool state)
+{
+ shouldPadParensInside = state;
+}
+
+/**
+ * set padding mode before one or more open parentheses.
+ * options:
+ * true first open parenthesis will be padded with a space before.
+ * false first open parenthesis will not be padded.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setParensFirstPaddingMode(bool state)
+{
+ shouldPadFirstParen = state;
+}
+
+/**
+ * set header padding mode.
+ * options:
+ * true headers will be padded with spaces around them.
+ * false headers will not be padded.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setParensHeaderPaddingMode(bool state)
+{
+ shouldPadHeader = state;
+}
+
+/**
+ * set parenthesis unpadding mode.
+ * options:
+ * true statement parenthesis will be unpadded with spaces removed around them.
+ * false statement parenthesis will not be unpadded.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setParensUnPaddingMode(bool state)
+{
+ shouldUnPadParens = state;
+}
+
+/**
+* set the state of the preprocessor indentation option.
+* If true, #ifdef blocks at level 0 will be indented.
+*
+* @param state state of option.
+*/
+void ASFormatter::setPreprocBlockIndent(bool state)
+{
+ shouldIndentPreprocBlock = state;
+}
+
+/**
+ * Set strip comment prefix mode.
+ * options:
+ * true strip leading '*' in a comment.
+ * false leading '*' in a comment will be left unchanged.
+ *
+ * @param state the strip comment prefix mode.
+ */
+void ASFormatter::setStripCommentPrefix(bool state)
+{
+ shouldStripCommentPrefix = state;
+}
+
+/**
+ * set objective-c '-' or '+' class prefix padding mode.
+ * options:
+ * true class prefix will be padded a spaces after them.
+ * false class prefix will be left unchanged.
+ *
+ * @param state the padding mode.
+ */
+void ASFormatter::setMethodPrefixPaddingMode(bool state)
+{
+ shouldPadMethodPrefix = state;
+}
+
+/**
+ * set objective-c '-' or '+' class prefix unpadding mode.
+ * options:
+ * true class prefix will be unpadded with spaces after them removed.
+ * false class prefix will left unchanged.
+ *
+ * @param state the unpadding mode.
+ */
+void ASFormatter::setMethodPrefixUnPaddingMode(bool state)
+{
+ shouldUnPadMethodPrefix = state;
+}
+
+// set objective-c '-' or '+' return type padding mode.
+void ASFormatter::setReturnTypePaddingMode(bool state)
+{
+ shouldPadReturnType = state;
+}
+
+// set objective-c '-' or '+' return type unpadding mode.
+void ASFormatter::setReturnTypeUnPaddingMode(bool state)
+{
+ shouldUnPadReturnType = state;
+}
+
+// set objective-c method parameter type padding mode.
+void ASFormatter::setParamTypePaddingMode(bool state)
+{
+ shouldPadParamType = state;
+}
+
+// set objective-c method parameter type unpadding mode.
+void ASFormatter::setParamTypeUnPaddingMode(bool state)
+{
+ shouldUnPadParamType = state;
+}
+
+/**
+ * set objective-c method colon padding mode.
+ *
+ * @param mode objective-c colon padding mode.
+ */
+void ASFormatter::setObjCColonPaddingMode(ObjCColonPad mode)
+{
+ shouldPadMethodColon = true;
+ objCColonPadMode = mode;
+}
+
+/**
+ * set option to attach closing braces
+ *
+ * @param state true = attach, false = don't attach.
+ */
+void ASFormatter::setAttachClosingBraceMode(bool state)
+{
+ attachClosingBraceMode = state;
+}
+
+/**
+ * set option to attach class braces
+ *
+ * @param state true = attach, false = use style default.
+ */
+void ASFormatter::setAttachClass(bool state)
+{
+ shouldAttachClass = state;
+}
+
+/**
+ * set option to attach extern "C" braces
+ *
+ * @param state true = attach, false = use style default.
+ */
+void ASFormatter::setAttachExternC(bool state)
+{
+ shouldAttachExternC = state;
+}
+
+/**
+ * set option to attach namespace braces
+ *
+ * @param state true = attach, false = use style default.
+ */
+void ASFormatter::setAttachNamespace(bool state)
+{
+ shouldAttachNamespace = state;
+}
+
+/**
+ * set option to attach inline braces
+ *
+ * @param state true = attach, false = use style default.
+ */
+void ASFormatter::setAttachInline(bool state)
+{
+ shouldAttachInline = state;
+}
+
+void ASFormatter::setAttachClosingWhile(bool state)
+{
+ shouldAttachClosingWhile = state;
+}
+
+/**
+ * set option to break/not break one-line blocks
+ *
+ * @param state true = break, false = don't break.
+ */
+void ASFormatter::setBreakOneLineBlocksMode(bool state)
+{
+ shouldBreakOneLineBlocks = state;
+}
+
+/**
+* set one line headers breaking mode
+*/
+void ASFormatter::setBreakOneLineHeadersMode(bool state)
+{
+ shouldBreakOneLineHeaders = state;
+}
+
+/**
+* set option to break/not break lines consisting of multiple statements.
+*
+* @param state true = break, false = don't break.
+*/
+void ASFormatter::setBreakOneLineStatementsMode(bool state)
+{
+ shouldBreakOneLineStatements = state;
+}
+
+void ASFormatter::setCloseTemplatesMode(bool state)
+{
+ shouldCloseTemplates = state;
+}
+
+/**
+ * set option to convert tabs to spaces.
+ *
+ * @param state true = convert, false = don't convert.
+ */
+void ASFormatter::setTabSpaceConversionMode(bool state)
+{
+ shouldConvertTabs = state;
+}
+
+/**
+ * set option to indent comments in column 1.
+ *
+ * @param state true = indent, false = don't indent.
+ */
+void ASFormatter::setIndentCol1CommentsMode(bool state)
+{
+ shouldIndentCol1Comments = state;
+}
+
+/**
+ * set option to force all line ends to a particular style.
+ *
+ * @param fmt format enum value
+ */
+void ASFormatter::setLineEndFormat(LineEndFormat fmt)
+{
+ lineEnd = fmt;
+}
+
+/**
+ * set option to break unrelated blocks of code with empty lines.
+ *
+ * @param state true = convert, false = don't convert.
+ */
+void ASFormatter::setBreakBlocksMode(bool state)
+{
+ shouldBreakBlocks = state;
+}
+
+/**
+ * set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines.
+ *
+ * @param state true = convert, false = don't convert.
+ */
+void ASFormatter::setBreakClosingHeaderBlocksMode(bool state)
+{
+ shouldBreakClosingHeaderBlocks = state;
+}
+
+/**
+ * set option to delete empty lines.
+ *
+ * @param state true = delete, false = don't delete.
+ */
+void ASFormatter::setDeleteEmptyLinesMode(bool state)
+{
+ shouldDeleteEmptyLines = state;
+}
+
+/**
+ * set the pointer alignment.
+ *
+ * @param alignment the pointer alignment.
+ */
+void ASFormatter::setPointerAlignment(PointerAlign alignment)
+{
+ pointerAlignment = alignment;
+}
+
+void ASFormatter::setReferenceAlignment(ReferenceAlign alignment)
+{
+ referenceAlignment = alignment;
+}
+
+/**
+ * jump over several characters.
+ *
+ * @param i the number of characters to jump over.
+ */
+void ASFormatter::goForward(int i)
+{
+ while (--i >= 0)
+ getNextChar();
+}
+
+/**
+ * peek at the next unread character.
+ *
+ * @return the next unread character.
+ */
+char ASFormatter::peekNextChar() const
+{
+ char ch = ' ';
+ size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
+
+ if (peekNum == string::npos)
+ return ch;
+
+ ch = currentLine[peekNum];
+
+ return ch;
+}
+
+/**
+ * check if current placement is before a comment
+ *
+ * @return is before a comment.
+ */
+bool ASFormatter::isBeforeComment() const
+{
+ bool foundComment = false;
+ size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
+
+ if (peekNum == string::npos)
+ return foundComment;
+
+ foundComment = (currentLine.compare(peekNum, 2, "/*") == 0);
+
+ return foundComment;
+}
+
+/**
+ * check if current placement is before a comment or line-comment
+ *
+ * @return is before a comment or line-comment.
+ */
+bool ASFormatter::isBeforeAnyComment() const
+{
+ bool foundComment = false;
+ size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
+
+ if (peekNum == string::npos)
+ return foundComment;
+
+ foundComment = (currentLine.compare(peekNum, 2, "/*") == 0
+ || currentLine.compare(peekNum, 2, "//") == 0);
+
+ return foundComment;
+}
+
+/**
+ * check if current placement is before a comment or line-comment
+ * if a block comment it must be at the end of the line
+ *
+ * @return is before a comment or line-comment.
+ */
+bool ASFormatter::isBeforeAnyLineEndComment(int startPos) const
+{
+ bool foundLineEndComment = false;
+ size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
+
+ if (peekNum != string::npos)
+ {
+ if (currentLine.compare(peekNum, 2, "//") == 0)
+ foundLineEndComment = true;
+ else if (currentLine.compare(peekNum, 2, "/*") == 0)
+ {
+ // comment must be closed on this line with nothing after it
+ size_t endNum = currentLine.find("*/", peekNum + 2);
+ if (endNum != string::npos)
+ {
+ size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
+ if (nextChar == string::npos)
+ foundLineEndComment = true;
+ }
+ }
+ }
+ return foundLineEndComment;
+}
+
+/**
+ * check if current placement is before a comment followed by a line-comment
+ *
+ * @return is before a multiple line-end comment.
+ */
+bool ASFormatter::isBeforeMultipleLineEndComments(int startPos) const
+{
+ bool foundMultipleLineEndComment = false;
+ size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
+
+ if (peekNum != string::npos)
+ {
+ if (currentLine.compare(peekNum, 2, "/*") == 0)
+ {
+ // comment must be closed on this line with nothing after it
+ size_t endNum = currentLine.find("*/", peekNum + 2);
+ if (endNum != string::npos)
+ {
+ size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
+ if (nextChar != string::npos
+ && currentLine.compare(nextChar, 2, "//") == 0)
+ foundMultipleLineEndComment = true;
+ }
+ }
+ }
+ return foundMultipleLineEndComment;
+}
+
+/**
+ * get the next character, increasing the current placement in the process.
+ * the new character is inserted into the variable currentChar.
+ *
+ * @return whether succeeded to receive the new character.
+ */
+bool ASFormatter::getNextChar()
+{
+ isInLineBreak = false;
+ previousChar = currentChar;
+
+ if (!isWhiteSpace(currentChar))
+ {
+ previousNonWSChar = currentChar;
+ if (!isInComment && !isInLineComment && !isInQuote
+ && !isImmediatelyPostComment
+ && !isImmediatelyPostLineComment
+ && !isInPreprocessor
+ && !isSequenceReached("/*")
+ && !isSequenceReached("//"))
+ previousCommandChar = currentChar;
+ }
+
+ if (charNum + 1 < (int) currentLine.length()
+ && (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment))
+ {
+ currentChar = currentLine[++charNum];
+
+ if (currentChar == '\t' && shouldConvertTabs)
+ convertTabToSpaces();
+
+ return true;
+ }
+
+ // end of line has been reached
+ return getNextLine();
+}
+
+/**
+ * get the next line of input, increasing the current placement in the process.
+ *
+ * @param emptyLineWasDeleted an empty line was deleted.
+ * @return whether succeeded in reading the next line.
+ */
+bool ASFormatter::getNextLine(bool emptyLineWasDeleted /*false*/)
+{
+ if (!sourceIterator->hasMoreLines())
+ {
+ endOfCodeReached = true;
+ return false;
+ }
+ if (appendOpeningBrace)
+ currentLine = "{"; // append brace that was removed from the previous line
+ else
+ {
+ currentLine = sourceIterator->nextLine(emptyLineWasDeleted);
+ assert(computeChecksumIn(currentLine));
+ }
+ // reset variables for new line
+ inLineNumber++;
+ if (endOfAsmReached)
+ endOfAsmReached = isInAsmBlock = isInAsm = false;
+ shouldKeepLineUnbroken = false;
+ isInCommentStartLine = false;
+ isInCase = false;
+ isInAsmOneLine = false;
+ isHeaderInMultiStatementLine = false;
+ isInQuoteContinuation = isInVerbatimQuote || haveLineContinuationChar;
+ haveLineContinuationChar = false;
+ isImmediatelyPostEmptyLine = lineIsEmpty;
+ previousChar = ' ';
+
+ if (currentLine.length() == 0)
+ currentLine = string(" "); // a null is inserted if this is not done
+
+ // unless reading in the first line of the file, break a new line.
+ if (!isVirgin)
+ isInLineBreak = true;
+ else
+ isVirgin = false;
+
+ if (isImmediatelyPostNonInStmt)
+ {
+ isCharImmediatelyPostNonInStmt = true;
+ isImmediatelyPostNonInStmt = false;
+ }
+
+ // check if is in preprocessor before line trimming
+ // a blank line after a \ will remove the flag
+ isImmediatelyPostPreprocessor = isInPreprocessor;
+ if (!isInComment
+ && (previousNonWSChar != '\\'
+ || isEmptyLine(currentLine)))
+ isInPreprocessor = false;
+
+ if (passedSemicolon)
+ isInExecSQL = false;
+ initNewLine();
+
+ currentChar = currentLine[charNum];
+ if (isInBraceRunIn && previousNonWSChar == '{' && !isInComment)
+ isInLineBreak = false;
+ isInBraceRunIn = false;
+
+ if (currentChar == '\t' && shouldConvertTabs)
+ convertTabToSpaces();
+
+ // check for an empty line inside a command brace.
+ // if yes then read the next line (calls getNextLine recursively).
+ // must be after initNewLine.
+ if (shouldDeleteEmptyLines
+ && lineIsEmpty
+ && isBraceType((*braceTypeStack)[braceTypeStack->size() - 1], COMMAND_TYPE))
+ {
+ if (!shouldBreakBlocks || previousNonWSChar == '{' || !commentAndHeaderFollows())
+ {
+ isInPreprocessor = isImmediatelyPostPreprocessor; // restore
+ lineIsEmpty = false;
+ return getNextLine(true);
+ }
+ }
+ return true;
+}
+
+/**
+ * jump over the leading white space in the current line,
+ * IF the line does not begin a comment or is in a preprocessor definition.
+ */
+void ASFormatter::initNewLine()
+{
+ size_t len = currentLine.length();
+ size_t tabSize = getTabLength();
+ charNum = 0;
+
+ // don't trim these
+ if (isInQuoteContinuation
+ || (isInPreprocessor && !getPreprocDefineIndent()))
+ return;
+
+ // SQL continuation lines must be adjusted so the leading spaces
+ // is equivalent to the opening EXEC SQL
+ if (isInExecSQL)
+ {
+ // replace leading tabs with spaces
+ // so that continuation indent will be spaces
+ size_t tabCount_ = 0;
+ size_t i;
+ for (i = 0; i < currentLine.length(); i++)
+ {
+ if (!isWhiteSpace(currentLine[i])) // stop at first text
+ break;
+ if (currentLine[i] == '\t')
+ {
+ size_t numSpaces = tabSize - ((tabCount_ + i) % tabSize);
+ currentLine.replace(i, 1, numSpaces, ' ');
+ tabCount_++;
+ i += tabSize - 1;
+ }
+ }
+ // this will correct the format if EXEC SQL is not a hanging indent
+ trimContinuationLine();
+ return;
+ }
+
+ // comment continuation lines must be adjusted so the leading spaces
+ // is equivalent to the opening comment
+ if (isInComment)
+ {
+ if (noTrimCommentContinuation)
+ leadingSpaces = tabIncrementIn = 0;
+ trimContinuationLine();
+ return;
+ }
+
+ // compute leading spaces
+ isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly;
+ lineIsCommentOnly = false;
+ lineIsLineCommentOnly = false;
+ lineEndsInCommentOnly = false;
+ doesLineStartComment = false;
+ currentLineBeginsWithBrace = false;
+ lineIsEmpty = false;
+ currentLineFirstBraceNum = string::npos;
+ tabIncrementIn = 0;
+
+ // bypass whitespace at the start of a line
+ // preprocessor tabs are replaced later in the program
+ for (charNum = 0; isWhiteSpace(currentLine[charNum]) && charNum + 1 < (int) len; charNum++)
+ {
+ if (currentLine[charNum] == '\t' && !isInPreprocessor)
+ tabIncrementIn += tabSize - 1 - ((tabIncrementIn + charNum) % tabSize);
+ }
+ leadingSpaces = charNum + tabIncrementIn;
+
+ if (isSequenceReached("/*"))
+ {
+ doesLineStartComment = true;
+ if ((int) currentLine.length() > charNum + 2
+ && currentLine.find("*/", charNum + 2) != string::npos)
+ lineIsCommentOnly = true;
+ }
+ else if (isSequenceReached("//"))
+ {
+ lineIsLineCommentOnly = true;
+ }
+ else if (isSequenceReached("{"))
+ {
+ currentLineBeginsWithBrace = true;
+ currentLineFirstBraceNum = charNum;
+ size_t firstText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (firstText != string::npos)
+ {
+ if (currentLine.compare(firstText, 2, "//") == 0)
+ lineIsLineCommentOnly = true;
+ else if (currentLine.compare(firstText, 2, "/*") == 0
+ || isExecSQL(currentLine, firstText))
+ {
+ // get the extra adjustment
+ size_t j;
+ for (j = charNum + 1; j < firstText && isWhiteSpace(currentLine[j]); j++)
+ {
+ if (currentLine[j] == '\t')
+ tabIncrementIn += tabSize - 1 - ((tabIncrementIn + j) % tabSize);
+ }
+ leadingSpaces = j + tabIncrementIn;
+ if (currentLine.compare(firstText, 2, "/*") == 0)
+ doesLineStartComment = true;
+ }
+ }
+ }
+ else if (isWhiteSpace(currentLine[charNum]) && !(charNum + 1 < (int) currentLine.length()))
+ {
+ lineIsEmpty = true;
+ }
+
+ // do not trim indented preprocessor define (except for comment continuation lines)
+ if (isInPreprocessor)
+ {
+ if (!doesLineStartComment)
+ leadingSpaces = 0;
+ charNum = 0;
+ }
+}
+
+/**
+ * Append a character to the current formatted line.
+ * The formattedLine split points are updated.
+ *
+ * @param ch the character to append.
+ * @param canBreakLine if true, a registered line-break
+ */
+void ASFormatter::appendChar(char ch, bool canBreakLine)
+{
+ if (canBreakLine && isInLineBreak)
+ breakLine();
+
+ formattedLine.append(1, ch);
+ isImmediatelyPostCommentOnly = false;
+ if (maxCodeLength != string::npos)
+ {
+ // These compares reduce the frequency of function calls.
+ if (isOkToSplitFormattedLine())
+ updateFormattedLineSplitPoints(ch);
+ if (formattedLine.length() > maxCodeLength)
+ testForTimeToSplitFormattedLine();
+ }
+}
+
+/**
+ * Append a string sequence to the current formatted line.
+ * The formattedLine split points are NOT updated.
+ * But the formattedLine is checked for time to split.
+ *
+ * @param sequence the sequence to append.
+ * @param canBreakLine if true, a registered line-break
+ */
+void ASFormatter::appendSequence(const string& sequence, bool canBreakLine)
+{
+ if (canBreakLine && isInLineBreak)
+ breakLine();
+ formattedLine.append(sequence);
+ if (formattedLine.length() > maxCodeLength)
+ testForTimeToSplitFormattedLine();
+}
+
+/**
+ * Append an operator sequence to the current formatted line.
+ * The formattedLine split points are updated.
+ *
+ * @param sequence the sequence to append.
+ * @param canBreakLine if true, a registered line-break
+ */
+void ASFormatter::appendOperator(const string& sequence, bool canBreakLine)
+{
+ if (canBreakLine && isInLineBreak)
+ breakLine();
+ formattedLine.append(sequence);
+ if (maxCodeLength != string::npos)
+ {
+ // These compares reduce the frequency of function calls.
+ if (isOkToSplitFormattedLine())
+ updateFormattedLineSplitPointsOperator(sequence);
+ if (formattedLine.length() > maxCodeLength)
+ testForTimeToSplitFormattedLine();
+ }
+}
+
+/**
+ * append a space to the current formattedline, UNLESS the
+ * last character is already a white-space character.
+ */
+void ASFormatter::appendSpacePad()
+{
+ int len = formattedLine.length();
+ if (len > 0 && !isWhiteSpace(formattedLine[len - 1]))
+ {
+ formattedLine.append(1, ' ');
+ spacePadNum++;
+ if (maxCodeLength != string::npos)
+ {
+ // These compares reduce the frequency of function calls.
+ if (isOkToSplitFormattedLine())
+ updateFormattedLineSplitPoints(' ');
+ if (formattedLine.length() > maxCodeLength)
+ testForTimeToSplitFormattedLine();
+ }
+ }
+}
+
+/**
+ * append a space to the current formattedline, UNLESS the
+ * next character is already a white-space character.
+ */
+void ASFormatter::appendSpaceAfter()
+{
+ int len = currentLine.length();
+ if (charNum + 1 < len && !isWhiteSpace(currentLine[charNum + 1]))
+ {
+ formattedLine.append(1, ' ');
+ spacePadNum++;
+ if (maxCodeLength != string::npos)
+ {
+ // These compares reduce the frequency of function calls.
+ if (isOkToSplitFormattedLine())
+ updateFormattedLineSplitPoints(' ');
+ if (formattedLine.length() > maxCodeLength)
+ testForTimeToSplitFormattedLine();
+ }
+ }
+}
+
+/**
+ * register a line break for the formatted line.
+ */
+void ASFormatter::breakLine(bool isSplitLine /*false*/)
+{
+ isLineReady = true;
+ isInLineBreak = false;
+ spacePadNum = nextLineSpacePadNum;
+ nextLineSpacePadNum = 0;
+ readyFormattedLine = formattedLine;
+ formattedLine.erase();
+ // queue an empty line prepend request if one exists
+ prependEmptyLine = isPrependPostBlockEmptyLineRequested;
+
+ if (!isSplitLine)
+ {
+ formattedLineCommentNum = string::npos;
+ clearFormattedLineSplitPoints();
+
+ if (isAppendPostBlockEmptyLineRequested)
+ {
+ isAppendPostBlockEmptyLineRequested = false;
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+ else
+ isPrependPostBlockEmptyLineRequested = false;
+ }
+}
+
+/**
+ * check if the currently reached open-brace (i.e. '{')
+ * opens a:
+ * - a definition type block (such as a class or namespace),
+ * - a command block (such as a method block)
+ * - a static array
+ * this method takes for granted that the current character
+ * is an opening brace.
+ *
+ * @return the type of the opened block.
+ */
+BraceType ASFormatter::getBraceType()
+{
+ assert(currentChar == '{');
+
+ BraceType returnVal = NULL_TYPE;
+
+ if ((previousNonWSChar == '='
+ || isBraceType(braceTypeStack->back(), ARRAY_TYPE))
+ && previousCommandChar != ')'
+ && !isNonParenHeader)
+ returnVal = ARRAY_TYPE;
+ else if (foundPreDefinitionHeader && previousCommandChar != ')')
+ {
+ returnVal = DEFINITION_TYPE;
+ if (foundNamespaceHeader)
+ returnVal = (BraceType)(returnVal | NAMESPACE_TYPE);
+ else if (foundClassHeader)
+ returnVal = (BraceType)(returnVal | CLASS_TYPE);
+ else if (foundStructHeader)
+ returnVal = (BraceType)(returnVal | STRUCT_TYPE);
+ else if (foundInterfaceHeader)
+ returnVal = (BraceType)(returnVal | INTERFACE_TYPE);
+ }
+ else if (isInEnum)
+ {
+ returnVal = (BraceType)(ARRAY_TYPE | ENUM_TYPE);
+ }
+ else
+ {
+ bool isCommandType = (foundPreCommandHeader
+ || foundPreCommandMacro
+ || (currentHeader != nullptr && isNonParenHeader)
+ || (previousCommandChar == ')')
+ || (previousCommandChar == ':' && !foundQuestionMark)
+ || (previousCommandChar == ';')
+ || ((previousCommandChar == '{' || previousCommandChar == '}')
+ && isPreviousBraceBlockRelated)
+ || (isInClassInitializer
+ && (!isLegalNameChar(previousNonWSChar) || foundPreCommandHeader))
+ || foundTrailingReturnType
+ || isInObjCMethodDefinition
+ || isInObjCInterface
+ || isJavaStaticConstructor
+ || isSharpDelegate);
+
+ // C# methods containing 'get', 'set', 'add', and 'remove' do NOT end with parens
+ if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1))
+ {
+ isCommandType = true;
+ isSharpAccessor = true;
+ }
+
+ if (isInExternC)
+ returnVal = (isCommandType ? COMMAND_TYPE : EXTERN_TYPE);
+ else
+ returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE);
+ }
+
+ int foundOneLineBlock = isOneLineBlockReached(currentLine, charNum);
+
+ if (foundOneLineBlock == 2 && returnVal == COMMAND_TYPE)
+ returnVal = ARRAY_TYPE;
+
+ if (foundOneLineBlock > 0)
+ {
+ returnVal = (BraceType) (returnVal | SINGLE_LINE_TYPE);
+ if (breakCurrentOneLineBlock)
+ returnVal = (BraceType) (returnVal | BREAK_BLOCK_TYPE);
+ if (foundOneLineBlock == 3)
+ returnVal = (BraceType)(returnVal | EMPTY_BLOCK_TYPE);
+ }
+
+ if (isBraceType(returnVal, ARRAY_TYPE))
+ {
+ if (isNonInStatementArrayBrace())
+ {
+ returnVal = (BraceType)(returnVal | ARRAY_NIS_TYPE);
+ isNonInStatementArray = true;
+ isImmediatelyPostNonInStmt = false; // in case of "},{"
+ nonInStatementBrace = formattedLine.length() - 1;
+ }
+ if (isUniformInitializerBrace())
+ returnVal = (BraceType)(returnVal | INIT_TYPE);
+ }
+
+ return returnVal;
+}
+
+/**
+* check if a colon is a class initializer separator
+*
+* @return whether it is a class initializer separator
+*/
+bool ASFormatter::isClassInitializer() const
+{
+ assert(currentChar == ':');
+ assert(previousChar != ':' && peekNextChar() != ':'); // not part of '::'
+
+ // this should be similar to ASBeautifier::parseCurrentLine()
+ bool foundClassInitializer = false;
+
+ if (foundQuestionMark)
+ {
+ // do nothing special
+ }
+ else if (parenStack->back() > 0)
+ {
+ // found a 'for' loop or an objective-C statement
+ // so do nothing special
+ }
+ else if (isInEnum)
+ {
+ // found an enum with a base-type
+ }
+ else if (isCStyle()
+ && !isInCase
+ && (previousCommandChar == ')' || foundPreCommandHeader))
+ {
+ // found a 'class' c'tor initializer
+ foundClassInitializer = true;
+ }
+ return foundClassInitializer;
+}
+
+/**
+ * check if a line is empty
+ *
+ * @return whether line is empty
+ */
+bool ASFormatter::isEmptyLine(const string& line) const
+{
+ return line.find_first_not_of(" \t") == string::npos;
+}
+
+/**
+ * Check if the following text is "C" as in extern "C".
+ *
+ * @return whether the statement is extern "C"
+ */
+bool ASFormatter::isExternC() const
+{
+ // charNum should be at 'extern'
+ assert(!isWhiteSpace(currentLine[charNum]));
+ size_t startQuote = currentLine.find_first_of(" \t\"", charNum);
+ if (startQuote == string::npos)
+ return false;
+ startQuote = currentLine.find_first_not_of(" \t", startQuote);
+ if (startQuote == string::npos)
+ return false;
+ if (currentLine.compare(startQuote, 3, "\"C\"") != 0)
+ return false;
+ return true;
+}
+
+/**
+ * Check if the currently reached '*', '&' or '^' character is
+ * a pointer-or-reference symbol, or another operator.
+ * A pointer dereference (*) or an "address of" character (&)
+ * counts as a pointer or reference because it is not an
+ * arithmetic operator.
+ *
+ * @return whether current character is a reference-or-pointer
+ */
+bool ASFormatter::isPointerOrReference() const
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+
+ if (isJavaStyle())
+ return false;
+
+ if (isCharImmediatelyPostOperator)
+ return false;
+
+ // get the last legal word (may be a number)
+ string lastWord = getPreviousWord(currentLine, charNum);
+ if (lastWord.empty())
+ lastWord = " ";
+
+ // check for preceding or following numeric values
+ string nextText = peekNextText(currentLine.substr(charNum + 1));
+ if (nextText.length() == 0)
+ nextText = " ";
+ char nextChar = nextText[0];
+ if (isDigit(lastWord[0])
+ || isDigit(nextChar)
+ || nextChar == '!'
+ || nextChar == '~')
+ return false;
+
+ // check for multiply then a dereference (a * *b)
+ if (currentChar == '*'
+ && charNum < (int) currentLine.length() - 1
+ && isWhiteSpace(currentLine[charNum + 1])
+ && nextChar == '*')
+ return false;
+
+ if ((foundCastOperator && nextChar == '>')
+ || isPointerOrReferenceVariable(lastWord))
+ return true;
+
+ if (isInClassInitializer
+ && previousNonWSChar != '('
+ && previousNonWSChar != '{'
+ && previousCommandChar != ','
+ && nextChar != ')'
+ && nextChar != '}')
+ return false;
+
+ //check for rvalue reference
+ if (currentChar == '&' && nextChar == '&')
+ {
+ if (previousNonWSChar == '>')
+ return true;
+ string followingText;
+ if ((int) currentLine.length() > charNum + 2)
+ followingText = peekNextText(currentLine.substr(charNum + 2));
+ if (followingText.length() > 0 && followingText[0] == ')')
+ return true;
+ if (currentHeader != nullptr || isInPotentialCalculation)
+ return false;
+ if (parenStack->back() > 0 && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
+ return false;
+ return true;
+ }
+ if (nextChar == '*'
+ || previousNonWSChar == '='
+ || previousNonWSChar == '('
+ || previousNonWSChar == '['
+ || isCharImmediatelyPostReturn
+ || isInTemplate
+ || isCharImmediatelyPostTemplate
+ || currentHeader == &AS_CATCH
+ || currentHeader == &AS_FOREACH
+ || currentHeader == &AS_QFOREACH)
+ return true;
+
+ if (isBraceType(braceTypeStack->back(), ARRAY_TYPE)
+ && isLegalNameChar(lastWord[0])
+ && isLegalNameChar(nextChar)
+ && previousNonWSChar != ')')
+ {
+ if (isArrayOperator())
+ return false;
+ }
+
+ // checks on operators in parens
+ if (parenStack->back() > 0
+ && isLegalNameChar(lastWord[0])
+ && isLegalNameChar(nextChar))
+ {
+ // if followed by an assignment it is a pointer or reference
+ // if followed by semicolon it is a pointer or reference in range-based for
+ const string* followingOperator = getFollowingOperator();
+ if (followingOperator != nullptr
+ && followingOperator != &AS_MULT
+ && followingOperator != &AS_BIT_AND)
+ {
+ if (followingOperator == &AS_ASSIGN || followingOperator == &AS_COLON)
+ return true;
+ return false;
+ }
+
+ if (isBraceType(braceTypeStack->back(), COMMAND_TYPE)
+ || squareBracketCount > 0)
+ return false;
+ return true;
+ }
+
+ // checks on operators in parens with following '('
+ if (parenStack->back() > 0
+ && nextChar == '('
+ && previousNonWSChar != ','
+ && previousNonWSChar != '('
+ && previousNonWSChar != '!'
+ && previousNonWSChar != '&'
+ && previousNonWSChar != '*'
+ && previousNonWSChar != '|')
+ return false;
+
+ if (nextChar == '-'
+ || nextChar == '+')
+ {
+ size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextNum != string::npos)
+ {
+ if (currentLine.compare(nextNum, 2, "++") != 0
+ && currentLine.compare(nextNum, 2, "--") != 0)
+ return false;
+ }
+ }
+
+ bool isPR = (!isInPotentialCalculation
+ || (!isLegalNameChar(previousNonWSChar)
+ && !(previousNonWSChar == ')' && nextChar == '(')
+ && !(previousNonWSChar == ')' && currentChar == '*' && !isImmediatelyPostCast())
+ && previousNonWSChar != ']')
+ || (!isWhiteSpace(nextChar)
+ && nextChar != '-'
+ && nextChar != '('
+ && nextChar != '['
+ && !isLegalNameChar(nextChar))
+ );
+
+ return isPR;
+}
+
+/**
+ * Check if the currently reached '*' or '&' character is
+ * a dereferenced pointer or "address of" symbol.
+ * NOTE: this MUST be a pointer or reference as determined by
+ * the function isPointerOrReference().
+ *
+ * @return whether current character is a dereference or address of
+ */
+bool ASFormatter::isDereferenceOrAddressOf() const
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+
+ if (isCharImmediatelyPostTemplate)
+ return false;
+
+ if (previousNonWSChar == '='
+ || previousNonWSChar == ','
+ || previousNonWSChar == '.'
+ || previousNonWSChar == '{'
+ || previousNonWSChar == '>'
+ || previousNonWSChar == '<'
+ || previousNonWSChar == '?'
+ || isCharImmediatelyPostLineComment
+ || isCharImmediatelyPostComment
+ || isCharImmediatelyPostReturn)
+ return true;
+
+ char nextChar = peekNextChar();
+ if (currentChar == '*' && nextChar == '*')
+ {
+ if (previousNonWSChar == '(')
+ return true;
+ if ((int) currentLine.length() < charNum + 2)
+ return true;
+ return false;
+ }
+ if (currentChar == '&' && nextChar == '&')
+ {
+ if (previousNonWSChar == '(' || isInTemplate)
+ return true;
+ if ((int) currentLine.length() < charNum + 2)
+ return true;
+ return false;
+ }
+
+ // check first char on the line
+ if (charNum == (int) currentLine.find_first_not_of(" \t")
+ && (isBraceType(braceTypeStack->back(), COMMAND_TYPE)
+ || parenStack->back() != 0))
+ return true;
+
+ string nextText = peekNextText(currentLine.substr(charNum + 1));
+ if (nextText.length() > 0)
+ {
+ if (nextText[0] == ')' || nextText[0] == '>'
+ || nextText[0] == ',' || nextText[0] == '=')
+ return false;
+ if (nextText[0] == ';')
+ return true;
+ }
+
+ // check for reference to a pointer *& (cannot have &*)
+ if ((currentChar == '*' && nextChar == '&')
+ || (previousNonWSChar == '*' && currentChar == '&'))
+ return false;
+
+ if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE)
+ && parenStack->back() == 0)
+ return false;
+
+ string lastWord = getPreviousWord(currentLine, charNum);
+ if (lastWord == "else" || lastWord == "delete")
+ return true;
+
+ if (isPointerOrReferenceVariable(lastWord))
+ return false;
+
+ bool isDA = (!(isLegalNameChar(previousNonWSChar) || previousNonWSChar == '>')
+ || (nextText.length() > 0 && !isLegalNameChar(nextText[0]) && nextText[0] != '/')
+ || (ispunct((unsigned char)previousNonWSChar) && previousNonWSChar != '.')
+ || isCharImmediatelyPostReturn);
+
+ return isDA;
+}
+
+/**
+ * Check if the currently reached '*' or '&' character is
+ * centered with one space on each side.
+ * Only spaces are checked, not tabs.
+ * If true then a space will be deleted on the output.
+ *
+ * @return whether current character is centered.
+ */
+bool ASFormatter::isPointerOrReferenceCentered() const
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+
+ int prNum = charNum;
+ int lineLength = (int) currentLine.length();
+
+ // check for end of line
+ if (peekNextChar() == ' ')
+ return false;
+
+ // check space before
+ if (prNum < 1
+ || currentLine[prNum - 1] != ' ')
+ return false;
+
+ // check no space before that
+ if (prNum < 2
+ || currentLine[prNum - 2] == ' ')
+ return false;
+
+ // check for ** or &&
+ if (prNum + 1 < lineLength
+ && (currentLine[prNum + 1] == '*' || currentLine[prNum + 1] == '&'))
+ prNum++;
+
+ // check space after
+ if (prNum + 1 <= lineLength
+ && currentLine[prNum + 1] != ' ')
+ return false;
+
+ // check no space after that
+ if (prNum + 2 < lineLength
+ && currentLine[prNum + 2] == ' ')
+ return false;
+
+ return true;
+}
+
+/**
+ * Check if a word is a pointer or reference variable type.
+ *
+ * @return whether word is a pointer or reference variable.
+ */
+bool ASFormatter::isPointerOrReferenceVariable(const string& word) const
+{
+ return (word == "char"
+ || word == "int"
+ || word == "void"
+ || (word.length() >= 6 // check end of word for _t
+ && word.compare(word.length() - 2, 2, "_t") == 0)
+ || word == "INT"
+ || word == "VOID");
+}
+
+/**
+ * check if the currently reached '+' or '-' character is a unary operator
+ * this method takes for granted that the current character
+ * is a '+' or '-'.
+ *
+ * @return whether the current '+' or '-' is a unary operator.
+ */
+bool ASFormatter::isUnaryOperator() const
+{
+ assert(currentChar == '+' || currentChar == '-');
+
+ return ((isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar))
+ && previousCommandChar != '.'
+ && previousCommandChar != '\"'
+ && previousCommandChar != '\''
+ && previousCommandChar != ')'
+ && previousCommandChar != ']');
+}
+
+/**
+ * check if the currently reached comment is in a 'switch' statement
+ *
+ * @return whether the current '+' or '-' is in an exponent.
+ */
+bool ASFormatter::isInSwitchStatement() const
+{
+ assert(isInLineComment || isInComment);
+ if (!preBraceHeaderStack->empty())
+ for (size_t i = 1; i < preBraceHeaderStack->size(); i++)
+ if (preBraceHeaderStack->at(i) == &AS_SWITCH)
+ return true;
+ return false;
+}
+
+/**
+ * check if the currently reached '+' or '-' character is
+ * part of an exponent, i.e. 0.2E-5.
+ *
+ * @return whether the current '+' or '-' is in an exponent.
+ */
+bool ASFormatter::isInExponent() const
+{
+ assert(currentChar == '+' || currentChar == '-');
+
+ if (charNum >= 2)
+ {
+ char prevPrevFormattedChar = currentLine[charNum - 2];
+ char prevFormattedChar = currentLine[charNum - 1];
+ return ((prevFormattedChar == 'e' || prevFormattedChar == 'E')
+ && (prevPrevFormattedChar == '.' || isDigit(prevPrevFormattedChar)));
+ }
+ return false;
+}
+
+/**
+ * check if an array brace should NOT have an in-statement indent
+ *
+ * @return the array is non in-statement
+ */
+bool ASFormatter::isNonInStatementArrayBrace() const
+{
+ bool returnVal = false;
+ char nextChar = peekNextChar();
+ // if this opening brace begins the line there will be no inStatement indent
+ if (currentLineBeginsWithBrace
+ && charNum == (int) currentLineFirstBraceNum
+ && nextChar != '}')
+ returnVal = true;
+ // if an opening brace ends the line there will be no inStatement indent
+ if (isWhiteSpace(nextChar)
+ || isBeforeAnyLineEndComment(charNum)
+ || nextChar == '{')
+ returnVal = true;
+
+ // Java "new Type [] {...}" IS an inStatement indent
+ if (isJavaStyle() && previousNonWSChar == ']')
+ returnVal = false;
+
+ return returnVal;
+}
+
+/**
+ * check if a one-line block has been reached,
+ * i.e. if the currently reached '{' character is closed
+ * with a complimentary '}' elsewhere on the current line,
+ *.
+ * @return 0 = one-line block has not been reached.
+ * 1 = one-line block has been reached.
+ * 2 = one-line block has been reached and is followed by a comma.
+ * 3 = one-line block has been reached and is an empty block.
+ */
+int ASFormatter::isOneLineBlockReached(const string& line, int startChar) const
+{
+ assert(line[startChar] == '{');
+
+ bool isInComment_ = false;
+ bool isInQuote_ = false;
+ bool hasText = false;
+ int braceCount = 0;
+ int lineLength = line.length();
+ char quoteChar_ = ' ';
+ char ch = ' ';
+ char prevCh = ' ';
+
+ for (int i = startChar; i < lineLength; ++i)
+ {
+ ch = line[i];
+
+ if (isInComment_)
+ {
+ if (line.compare(i, 2, "*/") == 0)
+ {
+ isInComment_ = false;
+ ++i;
+ }
+ continue;
+ }
+
+ if (ch == '\\')
+ {
+ ++i;
+ continue;
+ }
+
+ if (isInQuote_)
+ {
+ if (ch == quoteChar_)
+ isInQuote_ = false;
+ continue;
+ }
+
+ if (ch == '"'
+ || (ch == '\'' && !isDigitSeparator(line, i)))
+ {
+ isInQuote_ = true;
+ quoteChar_ = ch;
+ continue;
+ }
+
+ if (line.compare(i, 2, "//") == 0)
+ break;
+
+ if (line.compare(i, 2, "/*") == 0)
+ {
+ isInComment_ = true;
+ ++i;
+ continue;
+ }
+
+ if (ch == '{')
+ {
+ ++braceCount;
+ continue;
+ }
+ if (ch == '}')
+ {
+ --braceCount;
+ if (braceCount == 0)
+ {
+ // is this an array?
+ if (parenStack->back() == 0 && prevCh != '}')
+ {
+ size_t peekNum = line.find_first_not_of(" \t", i + 1);
+ if (peekNum != string::npos && line[peekNum] == ',')
+ return 2;
+ }
+ if (!hasText)
+ return 3; // is an empty block
+ return 1;
+ }
+ }
+ if (ch == ';')
+ continue;
+ if (!isWhiteSpace(ch))
+ {
+ hasText = true;
+ prevCh = ch;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * peek at the next word to determine if it is a C# non-paren header.
+ * will look ahead in the input file if necessary.
+ *
+ * @param startChar position on currentLine to start the search
+ * @return true if the next word is get or set.
+ */
+bool ASFormatter::isNextWordSharpNonParenHeader(int startChar) const
+{
+ // look ahead to find the next non-comment text
+ string nextText = peekNextText(currentLine.substr(startChar));
+ if (nextText.length() == 0)
+ return false;
+ if (nextText[0] == '[')
+ return true;
+ if (!isCharPotentialHeader(nextText, 0))
+ return false;
+ if (findKeyword(nextText, 0, AS_GET) || findKeyword(nextText, 0, AS_SET)
+ || findKeyword(nextText, 0, AS_ADD) || findKeyword(nextText, 0, AS_REMOVE))
+ return true;
+ return false;
+}
+
+/**
+ * peek at the next char to determine if it is an opening brace.
+ * will look ahead in the input file if necessary.
+ * this determines a java static constructor.
+ *
+ * @param startChar position on currentLine to start the search
+ * @return true if the next word is an opening brace.
+ */
+bool ASFormatter::isNextCharOpeningBrace(int startChar) const
+{
+ bool retVal = false;
+ string nextText = peekNextText(currentLine.substr(startChar));
+ if (nextText.length() > 0
+ && nextText.compare(0, 1, "{") == 0)
+ retVal = true;
+ return retVal;
+}
+
+/**
+* Check if operator and, pointer, and reference padding is disabled.
+* Disabling is done thru a NOPAD tag in an ending comment.
+*
+* @return true if the formatting on this line is disabled.
+*/
+bool ASFormatter::isOperatorPaddingDisabled() const
+{
+ size_t commentStart = currentLine.find("//", charNum);
+ if (commentStart == string::npos)
+ {
+ commentStart = currentLine.find("/*", charNum);
+ // comment must end on this line
+ if (commentStart != string::npos)
+ {
+ size_t commentEnd = currentLine.find("*/", commentStart + 2);
+ if (commentEnd == string::npos)
+ commentStart = string::npos;
+ }
+ }
+ if (commentStart == string::npos)
+ return false;
+ size_t noPadStart = currentLine.find("*NOPAD*", commentStart);
+ if (noPadStart == string::npos)
+ return false;
+ return true;
+}
+
+/**
+* Determine if an opening array-type brace should have a leading space pad.
+* This is to identify C++11 uniform initializers.
+*/
+bool ASFormatter::isUniformInitializerBrace() const
+{
+ if (isCStyle() && !isInEnum && !isImmediatelyPostPreprocessor)
+ {
+ if (isInClassInitializer
+ || isLegalNameChar(previousNonWSChar))
+ return true;
+ }
+ return false;
+}
+
+/**
+* Determine if there is a following statement on the current line.
+*/
+bool ASFormatter::isMultiStatementLine() const
+{
+ assert((isImmediatelyPostHeader || foundClosingHeader));
+ bool isInComment_ = false;
+ bool isInQuote_ = false;
+ int semiCount_ = 0;
+ int parenCount_ = 0;
+ int braceCount_ = 0;
+
+ for (size_t i = 0; i < currentLine.length(); i++)
+ {
+ if (isInComment_)
+ {
+ if (currentLine.compare(i, 2, "*/") == 0)
+ {
+ isInComment_ = false;
+ continue;
+ }
+ }
+ if (currentLine.compare(i, 2, "/*") == 0)
+ {
+ isInComment_ = true;
+ continue;
+ }
+ if (currentLine.compare(i, 2, "//") == 0)
+ return false;
+ if (isInQuote_)
+ {
+ if (currentLine[i] == '"' || currentLine[i] == '\'')
+ isInQuote_ = false;
+ continue;
+ }
+ if (currentLine[i] == '"' || currentLine[i] == '\'')
+ {
+ isInQuote_ = true;
+ continue;
+ }
+ if (currentLine[i] == '(')
+ {
+ ++parenCount_;
+ continue;
+ }
+ if (currentLine[i] == ')')
+ {
+ --parenCount_;
+ continue;
+ }
+ if (parenCount_ > 0)
+ continue;
+ if (currentLine[i] == '{')
+ {
+ ++braceCount_;
+ }
+ if (currentLine[i] == '}')
+ {
+ --braceCount_;
+ }
+ if (braceCount_ > 0)
+ continue;
+ if (currentLine[i] == ';')
+ {
+ ++semiCount_;
+ if (semiCount_ > 1)
+ return true;
+ continue;
+ }
+ }
+ return false;
+}
+
+/**
+ * get the next non-whitespace substring on following lines, bypassing all comments.
+ *
+ * @param firstLine the first line to check
+ * @return the next non-whitespace substring.
+ */
+string ASFormatter::peekNextText(const string& firstLine,
+ bool endOnEmptyLine /*false*/,
+ shared_ptr<ASPeekStream> streamArg /*nullptr*/) const
+{
+ bool isFirstLine = true;
+ string nextLine_ = firstLine;
+ size_t firstChar = string::npos;
+ shared_ptr<ASPeekStream> stream = streamArg;
+ if (stream == nullptr) // Borland may need == 0
+ stream = make_shared<ASPeekStream>(sourceIterator);
+
+ // find the first non-blank text, bypassing all comments.
+ bool isInComment_ = false;
+ while (stream->hasMoreLines() || isFirstLine)
+ {
+ if (isFirstLine)
+ isFirstLine = false;
+ else
+ nextLine_ = stream->peekNextLine();
+
+ firstChar = nextLine_.find_first_not_of(" \t");
+ if (firstChar == string::npos)
+ {
+ if (endOnEmptyLine && !isInComment_)
+ break;
+ continue;
+ }
+
+ if (nextLine_.compare(firstChar, 2, "/*") == 0)
+ {
+ firstChar += 2;
+ isInComment_ = true;
+ }
+
+ if (isInComment_)
+ {
+ firstChar = nextLine_.find("*/", firstChar);
+ if (firstChar == string::npos)
+ continue;
+ firstChar += 2;
+ isInComment_ = false;
+ firstChar = nextLine_.find_first_not_of(" \t", firstChar);
+ if (firstChar == string::npos)
+ continue;
+ }
+
+ if (nextLine_.compare(firstChar, 2, "//") == 0)
+ continue;
+
+ // found the next text
+ break;
+ }
+
+ if (firstChar == string::npos)
+ nextLine_ = "";
+ else
+ nextLine_ = nextLine_.substr(firstChar);
+ return nextLine_;
+}
+
+/**
+ * adjust comment position because of adding or deleting spaces
+ * the spaces are added or deleted to formattedLine
+ * spacePadNum contains the adjustment
+ */
+void ASFormatter::adjustComments()
+{
+ assert(spacePadNum != 0);
+ assert(isSequenceReached("//") || isSequenceReached("/*"));
+
+ // block comment must be closed on this line with nothing after it
+ if (isSequenceReached("/*"))
+ {
+ size_t endNum = currentLine.find("*/", charNum + 2);
+ if (endNum == string::npos)
+ return;
+ if (currentLine.find_first_not_of(" \t", endNum + 2) != string::npos)
+ return;
+ }
+
+ size_t len = formattedLine.length();
+ // don't adjust a tab
+ if (formattedLine[len - 1] == '\t')
+ return;
+ // if spaces were removed, need to add spaces before the comment
+ if (spacePadNum < 0)
+ {
+ int adjust = -spacePadNum; // make the number positive
+ formattedLine.append(adjust, ' ');
+ }
+ // if spaces were added, need to delete extra spaces before the comment
+ // if cannot be done put the comment one space after the last text
+ else if (spacePadNum > 0)
+ {
+ int adjust = spacePadNum;
+ size_t lastText = formattedLine.find_last_not_of(' ');
+ if (lastText != string::npos
+ && lastText < len - adjust - 1)
+ formattedLine.resize(len - adjust);
+ else if (len > lastText + 2)
+ formattedLine.resize(lastText + 2);
+ else if (len < lastText + 2)
+ formattedLine.append(len - lastText, ' ');
+ }
+}
+
+/**
+ * append the current brace inside the end of line comments
+ * currentChar contains the brace, it will be appended to formattedLine
+ * formattedLineCommentNum is the comment location on formattedLine
+ */
+void ASFormatter::appendCharInsideComments()
+{
+ if (formattedLineCommentNum == string::npos // does the comment start on the previous line?
+ || formattedLineCommentNum == 0)
+ {
+ appendCurrentChar(); // don't attach
+ return;
+ }
+ assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0
+ || formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0);
+
+ // find the previous non space char
+ size_t end = formattedLineCommentNum;
+ size_t beg = formattedLine.find_last_not_of(" \t", end - 1);
+ if (beg == string::npos)
+ {
+ appendCurrentChar(); // don't attach
+ return;
+ }
+ beg++;
+
+ // insert the brace
+ if (end - beg < 3) // is there room to insert?
+ formattedLine.insert(beg, 3 - end + beg, ' ');
+ if (formattedLine[beg] == '\t') // don't pad with a tab
+ formattedLine.insert(beg, 1, ' ');
+ formattedLine[beg + 1] = currentChar;
+ testForTimeToSplitFormattedLine();
+
+ if (isBeforeComment())
+ breakLine();
+ else if (isCharImmediatelyPostLineComment)
+ shouldBreakLineAtNextChar = true;
+}
+
+/**
+ * add or remove space padding to operators
+ * the operators and necessary padding will be appended to formattedLine
+ * the calling function should have a continue statement after calling this method
+ *
+ * @param newOperator the operator to be padded
+ */
+void ASFormatter::padOperators(const string* newOperator)
+{
+ assert(shouldPadOperators);
+ assert(newOperator != nullptr);
+
+ char nextNonWSChar = ASBase::peekNextChar(currentLine, charNum);
+ bool shouldPad = (newOperator != &AS_SCOPE_RESOLUTION
+ && newOperator != &AS_PLUS_PLUS
+ && newOperator != &AS_MINUS_MINUS
+ && newOperator != &AS_NOT
+ && newOperator != &AS_BIT_NOT
+ && newOperator != &AS_ARROW
+ && !(newOperator == &AS_COLON && !foundQuestionMark // objC methods
+ && (isInObjCMethodDefinition || isInObjCInterface
+ || isInObjCSelector || squareBracketCount != 0))
+ && !(newOperator == &AS_MINUS && isInExponent())
+ && !(newOperator == &AS_PLUS && isInExponent())
+ && !((newOperator == &AS_PLUS || newOperator == &AS_MINUS) // check for unary plus or minus
+ && (previousNonWSChar == '('
+ || previousNonWSChar == '['
+ || previousNonWSChar == '='
+ || previousNonWSChar == ','
+ || previousNonWSChar == ':'
+ || previousNonWSChar == '{'))
+//? // commented out in release 2.05.1 - doesn't seem to do anything???
+//x && !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND || newOperator == &AS_AND)
+//x && isPointerOrReference())
+ && !(newOperator == &AS_MULT
+ && (previousNonWSChar == '.'
+ || previousNonWSChar == '>')) // check for ->
+ && !(newOperator == &AS_MULT && peekNextChar() == '>')
+ && !((isInTemplate || isImmediatelyPostTemplate)
+ && (newOperator == &AS_LS || newOperator == &AS_GR))
+ && !(newOperator == &AS_GCC_MIN_ASSIGN
+ && ASBase::peekNextChar(currentLine, charNum + 1) == '>')
+ && !(newOperator == &AS_GR && previousNonWSChar == '?')
+ && !(newOperator == &AS_QUESTION // check for Java wildcard
+ && isJavaStyle()
+ && (previousNonWSChar == '<'
+ || nextNonWSChar == '>'
+ || nextNonWSChar == '.'))
+ && !(newOperator == &AS_QUESTION // check for C# null conditional operator
+ && isSharpStyle()
+ && (nextNonWSChar == '.'
+ || nextNonWSChar == '['))
+ && !isCharImmediatelyPostOperator
+ && !isInCase
+ && !isInAsm
+ && !isInAsmOneLine
+ && !isInAsmBlock
+ );
+
+ // pad before operator
+ if (shouldPad
+ && !(newOperator == &AS_COLON
+ && (!foundQuestionMark && !isInEnum) && currentHeader != &AS_FOR)
+ && !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
+ && currentLine.find(':', charNum + 1) == string::npos)
+ )
+ appendSpacePad();
+ appendOperator(*newOperator);
+ goForward(newOperator->length() - 1);
+
+ currentChar = (*newOperator)[newOperator->length() - 1];
+ // pad after operator
+ // but do not pad after a '-' that is a unary-minus.
+ if (shouldPad
+ && !isBeforeAnyComment()
+ && !(newOperator == &AS_PLUS && isUnaryOperator())
+ && !(newOperator == &AS_MINUS && isUnaryOperator())
+ && !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0)
+ && !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0)
+ && !(peekNextChar() == ',')
+ && !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
+ && peekNextChar() == '[')
+ )
+ appendSpaceAfter();
+}
+
+/**
+ * format pointer or reference
+ * currentChar contains the pointer or reference
+ * the symbol and necessary padding will be appended to formattedLine
+ * the calling function should have a continue statement after calling this method
+ *
+ * NOTE: Do NOT use appendCurrentChar() in this method. The line should not be
+ * broken once the calculation starts.
+ */
+void ASFormatter::formatPointerOrReference()
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+ assert(!isJavaStyle());
+
+ int pa = pointerAlignment;
+ int ra = referenceAlignment;
+ int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
+
+ // check for ** and &&
+ int ptrLength = 1;
+ char peekedChar = peekNextChar();
+ if ((currentChar == '*' && peekedChar == '*')
+ || (currentChar == '&' && peekedChar == '&'))
+ {
+ ptrLength = 2;
+ size_t nextChar = currentLine.find_first_not_of(" \t", charNum + 2);
+ if (nextChar == string::npos)
+ peekedChar = ' ';
+ else
+ peekedChar = currentLine[nextChar];
+ }
+ // check for cast
+ if (peekedChar == ')' || peekedChar == '>' || peekedChar == ',')
+ {
+ formatPointerOrReferenceCast();
+ return;
+ }
+
+ // check for a padded space and remove it
+ if (charNum > 0
+ && !isWhiteSpace(currentLine[charNum - 1])
+ && formattedLine.length() > 0
+ && isWhiteSpace(formattedLine[formattedLine.length() - 1]))
+ {
+ formattedLine.erase(formattedLine.length() - 1);
+ spacePadNum--;
+ }
+
+ if (itemAlignment == PTR_ALIGN_TYPE)
+ {
+ formatPointerOrReferenceToType();
+ }
+ else if (itemAlignment == PTR_ALIGN_MIDDLE)
+ {
+ formatPointerOrReferenceToMiddle();
+ }
+ else if (itemAlignment == PTR_ALIGN_NAME)
+ {
+ formatPointerOrReferenceToName();
+ }
+ else // pointerAlignment == PTR_ALIGN_NONE
+ {
+ formattedLine.append(ptrLength, currentChar);
+ if (ptrLength > 1)
+ goForward(ptrLength - 1);
+ }
+}
+
+/**
+ * format pointer or reference with align to type
+ */
+void ASFormatter::formatPointerOrReferenceToType()
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+ assert(!isJavaStyle());
+
+ // do this before bumping charNum
+ bool isOldPRCentered = isPointerOrReferenceCentered();
+
+ size_t prevCh = formattedLine.find_last_not_of(" \t");
+ if (prevCh == string::npos)
+ prevCh = 0;
+ if (formattedLine.length() == 0 || prevCh == formattedLine.length() - 1)
+ formattedLine.append(1, currentChar);
+ else
+ {
+ // exchange * or & with character following the type
+ // this may not work every time with a tab character
+ string charSave = formattedLine.substr(prevCh + 1, 1);
+ formattedLine[prevCh + 1] = currentChar;
+ formattedLine.append(charSave);
+ }
+ if (isSequenceReached("**") || isSequenceReached("&&"))
+ {
+ if (formattedLine.length() == 1)
+ formattedLine.append(1, currentChar);
+ else
+ formattedLine.insert(prevCh + 2, 1, currentChar);
+ goForward(1);
+ }
+ // if no space after then add one
+ if (charNum < (int) currentLine.length() - 1
+ && !isWhiteSpace(currentLine[charNum + 1])
+ && currentLine[charNum + 1] != ')')
+ appendSpacePad();
+ // if old pointer or reference is centered, remove a space
+ if (isOldPRCentered
+ && isWhiteSpace(formattedLine[formattedLine.length() - 1]))
+ {
+ formattedLine.erase(formattedLine.length() - 1, 1);
+ spacePadNum--;
+ }
+ // update the formattedLine split point
+ if (maxCodeLength != string::npos)
+ {
+ size_t index = formattedLine.length() - 1;
+ if (isWhiteSpace(formattedLine[index]))
+ {
+ updateFormattedLineSplitPointsPointerOrReference(index);
+ testForTimeToSplitFormattedLine();
+ }
+ }
+}
+
+/**
+ * format pointer or reference with align in the middle
+ */
+void ASFormatter::formatPointerOrReferenceToMiddle()
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+ assert(!isJavaStyle());
+
+ // compute current whitespace before
+ size_t wsBefore = currentLine.find_last_not_of(" \t", charNum - 1);
+ if (wsBefore == string::npos)
+ wsBefore = 0;
+ else
+ wsBefore = charNum - wsBefore - 1;
+ string sequenceToInsert(1, currentChar);
+ if (isSequenceReached("**"))
+ {
+ sequenceToInsert = "**";
+ goForward(1);
+ }
+ else if (isSequenceReached("&&"))
+ {
+ sequenceToInsert = "&&";
+ goForward(1);
+ }
+ // if reference to a pointer check for conflicting alignment
+ else if (currentChar == '*' && peekNextChar() == '&'
+ && (referenceAlignment == REF_ALIGN_TYPE
+ || referenceAlignment == REF_ALIGN_MIDDLE
+ || referenceAlignment == REF_SAME_AS_PTR))
+ {
+ sequenceToInsert = "*&";
+ goForward(1);
+ for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
+ goForward(1);
+ }
+ // if a comment follows don't align, just space pad
+ if (isBeforeAnyComment())
+ {
+ appendSpacePad();
+ formattedLine.append(sequenceToInsert);
+ appendSpaceAfter();
+ return;
+ }
+ // do this before goForward()
+ bool isAfterScopeResolution = previousNonWSChar == ':';
+ size_t charNumSave = charNum;
+ // if this is the last thing on the line
+ if (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos)
+ {
+ if (wsBefore == 0 && !isAfterScopeResolution)
+ formattedLine.append(1, ' ');
+ formattedLine.append(sequenceToInsert);
+ return;
+ }
+ // goForward() to convert tabs to spaces, if necessary,
+ // and move following characters to preceding characters
+ // this may not work every time with tab characters
+ for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
+ {
+ goForward(1);
+ if (formattedLine.length() > 0)
+ formattedLine.append(1, currentLine[i]);
+ else
+ spacePadNum--;
+ }
+ // find space padding after
+ size_t wsAfter = currentLine.find_first_not_of(" \t", charNumSave + 1);
+ if (wsAfter == string::npos || isBeforeAnyComment())
+ wsAfter = 0;
+ else
+ wsAfter = wsAfter - charNumSave - 1;
+ // don't pad before scope resolution operator, but pad after
+ if (isAfterScopeResolution)
+ {
+ size_t lastText = formattedLine.find_last_not_of(" \t");
+ formattedLine.insert(lastText + 1, sequenceToInsert);
+ appendSpacePad();
+ }
+ else if (formattedLine.length() > 0)
+ {
+ // whitespace should be at least 2 chars to center
+ if (wsBefore + wsAfter < 2)
+ {
+ size_t charsToAppend = (2 - (wsBefore + wsAfter));
+ formattedLine.append(charsToAppend, ' ');
+ spacePadNum += charsToAppend;
+ if (wsBefore == 0)
+ wsBefore++;
+ if (wsAfter == 0)
+ wsAfter++;
+ }
+ // insert the pointer or reference char
+ size_t padAfter = (wsBefore + wsAfter) / 2;
+ size_t index = formattedLine.length() - padAfter;
+ formattedLine.insert(index, sequenceToInsert);
+ }
+ else // formattedLine.length() == 0
+ {
+ formattedLine.append(sequenceToInsert);
+ if (wsAfter == 0)
+ wsAfter++;
+ formattedLine.append(wsAfter, ' ');
+ spacePadNum += wsAfter;
+ }
+ // update the formattedLine split point after the pointer
+ if (maxCodeLength != string::npos && formattedLine.length() > 0)
+ {
+ size_t index = formattedLine.find_last_not_of(" \t");
+ if (index != string::npos && (index < formattedLine.length() - 1))
+ {
+ index++;
+ updateFormattedLineSplitPointsPointerOrReference(index);
+ testForTimeToSplitFormattedLine();
+ }
+ }
+}
+
+/**
+ * format pointer or reference with align to name
+ */
+void ASFormatter::formatPointerOrReferenceToName()
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+ assert(!isJavaStyle());
+
+ // do this before bumping charNum
+ bool isOldPRCentered = isPointerOrReferenceCentered();
+
+ size_t startNum = formattedLine.find_last_not_of(" \t");
+ if (startNum == string::npos)
+ startNum = 0;
+ string sequenceToInsert(1, currentChar);
+ if (isSequenceReached("**"))
+ {
+ sequenceToInsert = "**";
+ goForward(1);
+ }
+ else if (isSequenceReached("&&"))
+ {
+ sequenceToInsert = "&&";
+ goForward(1);
+ }
+ // if reference to a pointer align both to name
+ else if (currentChar == '*' && peekNextChar() == '&')
+ {
+ sequenceToInsert = "*&";
+ goForward(1);
+ for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
+ goForward(1);
+ }
+ char peekedChar = peekNextChar();
+ bool isAfterScopeResolution = previousNonWSChar == ':'; // check for ::
+ // if this is not the last thing on the line
+ if (!isBeforeAnyComment()
+ && (int) currentLine.find_first_not_of(" \t", charNum + 1) > charNum)
+ {
+ // goForward() to convert tabs to spaces, if necessary,
+ // and move following characters to preceding characters
+ // this may not work every time with tab characters
+ for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
+ {
+ // if a padded paren follows don't move
+ if (shouldPadParensOutside && peekedChar == '(' && !isOldPRCentered)
+ {
+ // empty parens don't count
+ size_t start = currentLine.find_first_not_of("( \t", charNum + 1);
+ if (start != string::npos && currentLine[start] != ')')
+ break;
+ }
+ goForward(1);
+ if (formattedLine.length() > 0)
+ formattedLine.append(1, currentLine[i]);
+ else
+ spacePadNum--;
+ }
+ }
+ // don't pad before scope resolution operator
+ if (isAfterScopeResolution)
+ {
+ size_t lastText = formattedLine.find_last_not_of(" \t");
+ if (lastText != string::npos && lastText + 1 < formattedLine.length())
+ formattedLine.erase(lastText + 1);
+ }
+ // if no space before * then add one
+ else if (formattedLine.length() > 0
+ && (formattedLine.length() <= startNum + 1
+ || !isWhiteSpace(formattedLine[startNum + 1])))
+ {
+ formattedLine.insert(startNum + 1, 1, ' ');
+ spacePadNum++;
+ }
+ appendSequence(sequenceToInsert, false);
+ // if old pointer or reference is centered, remove a space
+ if (isOldPRCentered
+ && formattedLine.length() > startNum + 1
+ && isWhiteSpace(formattedLine[startNum + 1])
+ && !isBeforeAnyComment())
+ {
+ formattedLine.erase(startNum + 1, 1);
+ spacePadNum--;
+ }
+ // don't convert to *= or &=
+ if (peekedChar == '=')
+ {
+ appendSpaceAfter();
+ // if more than one space before, delete one
+ if (formattedLine.length() > startNum
+ && isWhiteSpace(formattedLine[startNum + 1])
+ && isWhiteSpace(formattedLine[startNum + 2]))
+ {
+ formattedLine.erase(startNum + 1, 1);
+ spacePadNum--;
+ }
+ }
+ // update the formattedLine split point
+ if (maxCodeLength != string::npos)
+ {
+ size_t index = formattedLine.find_last_of(" \t");
+ if (index != string::npos
+ && index < formattedLine.length() - 1
+ && (formattedLine[index + 1] == '*'
+ || formattedLine[index + 1] == '&'
+ || formattedLine[index + 1] == '^'))
+ {
+ updateFormattedLineSplitPointsPointerOrReference(index);
+ testForTimeToSplitFormattedLine();
+ }
+ }
+}
+
+/**
+ * format pointer or reference cast
+ * currentChar contains the pointer or reference
+ * NOTE: the pointers and references in function definitions
+ * are processed as a cast (e.g. void foo(void*, void*))
+ * is processed here.
+ */
+void ASFormatter::formatPointerOrReferenceCast()
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+ assert(!isJavaStyle());
+
+ int pa = pointerAlignment;
+ int ra = referenceAlignment;
+ int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
+
+ string sequenceToInsert(1, currentChar);
+ if (isSequenceReached("**") || isSequenceReached("&&"))
+ {
+ goForward(1);
+ sequenceToInsert.append(1, currentLine[charNum]);
+ }
+ if (itemAlignment == PTR_ALIGN_NONE)
+ {
+ appendSequence(sequenceToInsert, false);
+ return;
+ }
+ // remove preceding whitespace
+ char prevCh = ' ';
+ size_t prevNum = formattedLine.find_last_not_of(" \t");
+ if (prevNum != string::npos)
+ {
+ prevCh = formattedLine[prevNum];
+ if (prevNum + 1 < formattedLine.length()
+ && isWhiteSpace(formattedLine[prevNum + 1])
+ && prevCh != '(')
+ {
+ spacePadNum -= (formattedLine.length() - 1 - prevNum);
+ formattedLine.erase(prevNum + 1);
+ }
+ }
+ bool isAfterScopeResolution = previousNonWSChar == ':';
+ if ((itemAlignment == PTR_ALIGN_MIDDLE || itemAlignment == PTR_ALIGN_NAME)
+ && !isAfterScopeResolution && prevCh != '(')
+ {
+ appendSpacePad();
+ // in this case appendSpacePad may or may not update the split point
+ if (maxCodeLength != string::npos && formattedLine.length() > 0)
+ updateFormattedLineSplitPointsPointerOrReference(formattedLine.length() - 1);
+ appendSequence(sequenceToInsert, false);
+ }
+ else
+ appendSequence(sequenceToInsert, false);
+}
+
+/**
+ * add or remove space padding to parens
+ * currentChar contains the paren
+ * the parens and necessary padding will be appended to formattedLine
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::padParens()
+{
+ assert(currentChar == '(' || currentChar == ')');
+ assert(shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen);
+
+ int spacesOutsideToDelete = 0;
+ int spacesInsideToDelete = 0;
+
+ if (currentChar == '(')
+ {
+ spacesOutsideToDelete = formattedLine.length() - 1;
+ spacesInsideToDelete = 0;
+
+ // compute spaces outside the opening paren to delete
+ if (shouldUnPadParens)
+ {
+ char lastChar = ' ';
+ bool prevIsParenHeader = false;
+ size_t i = formattedLine.find_last_not_of(" \t");
+ if (i != string::npos)
+ {
+ // if last char is a brace the previous whitespace is an indent
+ if (formattedLine[i] == '{')
+ spacesOutsideToDelete = 0;
+ else if (isCharImmediatelyPostPointerOrReference)
+ spacesOutsideToDelete = 0;
+ else
+ {
+ spacesOutsideToDelete -= i;
+ lastChar = formattedLine[i];
+ // if previous word is a header, it will be a paren header
+ string prevWord = getPreviousWord(formattedLine, formattedLine.length());
+ const string* prevWordH = nullptr;
+ if (shouldPadHeader
+ && prevWord.length() > 0
+ && isCharPotentialHeader(prevWord, 0))
+ prevWordH = ASBase::findHeader(prevWord, 0, headers);
+ if (prevWordH != nullptr)
+ prevIsParenHeader = true;
+ else if (prevWord == AS_RETURN) // don't unpad
+ prevIsParenHeader = true;
+ else if ((prevWord == AS_NEW || prevWord == AS_DELETE)
+ && shouldPadHeader) // don't unpad
+ prevIsParenHeader = true;
+ else if (isCStyle() && prevWord == AS_THROW && shouldPadHeader) // don't unpad
+ prevIsParenHeader = true;
+ else if (prevWord == "and" || prevWord == "or" || prevWord == "in") // don't unpad
+ prevIsParenHeader = true;
+ // don't unpad variables
+ else if (prevWord == "bool"
+ || prevWord == "int"
+ || prevWord == "void"
+ || prevWord == "void*"
+ || prevWord == "char"
+ || prevWord == "char*"
+ || prevWord == "long"
+ || prevWord == "double"
+ || prevWord == "float"
+ || (prevWord.length() >= 4 // check end of word for _t
+ && prevWord.compare(prevWord.length() - 2, 2, "_t") == 0)
+ || prevWord == "Int32"
+ || prevWord == "UInt32"
+ || prevWord == "Int64"
+ || prevWord == "UInt64"
+ || prevWord == "BOOL"
+ || prevWord == "DWORD"
+ || prevWord == "HWND"
+ || prevWord == "INT"
+ || prevWord == "LPSTR"
+ || prevWord == "VOID"
+ || prevWord == "LPVOID"
+ )
+ {
+ prevIsParenHeader = true;
+ }
+ }
+ }
+ // do not unpad operators, but leave them if already padded
+ if (shouldPadParensOutside || prevIsParenHeader)
+ spacesOutsideToDelete--;
+ else if (lastChar == '|' // check for ||
+ || lastChar == '&' // check for &&
+ || lastChar == ','
+ || (lastChar == '(' && shouldPadParensInside)
+ || (lastChar == '>' && !foundCastOperator)
+ || lastChar == '<'
+ || lastChar == '?'
+ || lastChar == ':'
+ || lastChar == ';'
+ || lastChar == '='
+ || lastChar == '+'
+ || lastChar == '-'
+ || lastChar == '*'
+ || lastChar == '/'
+ || lastChar == '%'
+ || lastChar == '^'
+ )
+ spacesOutsideToDelete--;
+
+ if (spacesOutsideToDelete > 0)
+ {
+ formattedLine.erase(i + 1, spacesOutsideToDelete);
+ spacePadNum -= spacesOutsideToDelete;
+ }
+ }
+
+ // pad open paren outside
+ char peekedCharOutside = peekNextChar();
+ if (shouldPadFirstParen && previousChar != '(' && peekedCharOutside != ')')
+ appendSpacePad();
+ else if (shouldPadParensOutside)
+ {
+ if (!(currentChar == '(' && peekedCharOutside == ')'))
+ appendSpacePad();
+ }
+
+ appendCurrentChar();
+
+ // unpad open paren inside
+ if (shouldUnPadParens)
+ {
+ size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (j != string::npos)
+ spacesInsideToDelete = j - charNum - 1;
+ if (shouldPadParensInside)
+ spacesInsideToDelete--;
+ if (spacesInsideToDelete > 0)
+ {
+ currentLine.erase(charNum + 1, spacesInsideToDelete);
+ spacePadNum -= spacesInsideToDelete;
+ }
+ // convert tab to space if requested
+ if (shouldConvertTabs
+ && (int) currentLine.length() > charNum + 1
+ && currentLine[charNum + 1] == '\t')
+ currentLine[charNum + 1] = ' ';
+ }
+
+ // pad open paren inside
+ char peekedCharInside = peekNextChar();
+ if (shouldPadParensInside)
+ if (!(currentChar == '(' && peekedCharInside == ')'))
+ appendSpaceAfter();
+ }
+ else if (currentChar == ')')
+ {
+ // unpad close paren inside
+ if (shouldUnPadParens)
+ {
+ spacesInsideToDelete = formattedLine.length();
+ size_t i = formattedLine.find_last_not_of(" \t");
+ if (i != string::npos)
+ spacesInsideToDelete = formattedLine.length() - 1 - i;
+ if (shouldPadParensInside)
+ spacesInsideToDelete--;
+ if (spacesInsideToDelete > 0)
+ {
+ formattedLine.erase(i + 1, spacesInsideToDelete);
+ spacePadNum -= spacesInsideToDelete;
+ }
+ }
+
+ // pad close paren inside
+ if (shouldPadParensInside)
+ if (!(previousChar == '(' && currentChar == ')'))
+ appendSpacePad();
+
+ appendCurrentChar();
+
+ // unpad close paren outside
+ // close parens outside are left unchanged
+ if (shouldUnPadParens)
+ {
+ //spacesOutsideToDelete = 0;
+ //size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
+ //if (j != string::npos)
+ // spacesOutsideToDelete = j - charNum - 1;
+ //if (shouldPadParensOutside)
+ // spacesOutsideToDelete--;
+
+ //if (spacesOutsideToDelete > 0)
+ //{
+ // currentLine.erase(charNum + 1, spacesOutsideToDelete);
+ // spacePadNum -= spacesOutsideToDelete;
+ //}
+ }
+
+ // pad close paren outside
+ char peekedCharOutside = peekNextChar();
+ if (shouldPadParensOutside)
+ if (peekedCharOutside != ';'
+ && peekedCharOutside != ','
+ && peekedCharOutside != '.'
+ && peekedCharOutside != '+' // check for ++
+ && peekedCharOutside != '-' // check for --
+ && peekedCharOutside != ']')
+ appendSpaceAfter();
+ }
+}
+
+/**
+* add or remove space padding to objective-c parens
+* these options have precedence over the padParens methods
+* the padParens method has already been called, this method adjusts
+*/
+void ASFormatter::padObjCMethodPrefix()
+{
+ assert(currentChar == '(' && isImmediatelyPostObjCMethodPrefix);
+ assert(shouldPadMethodPrefix || shouldUnPadMethodPrefix);
+
+ size_t prefix = formattedLine.find_first_of("+-");
+ if (prefix == string::npos)
+ return;
+ size_t paren = formattedLine.find_first_of('(');
+ if (paren == string::npos)
+ return;
+ int spaces = paren - prefix - 1;
+
+ if (shouldPadMethodPrefix)
+ {
+ if (spaces == 0)
+ {
+ formattedLine.insert(prefix + 1, 1, ' ');
+ spacePadNum += 1;
+ }
+ else if (spaces > 1)
+ {
+ formattedLine.erase(prefix + 1, spaces - 1);
+ spacePadNum -= spaces - 1;
+ }
+ }
+ // this option will be ignored if used with pad-method-prefix
+ else if (shouldUnPadMethodPrefix)
+ {
+ if (spaces > 0)
+ {
+ formattedLine.erase(prefix + 1, spaces);
+ spacePadNum -= spaces;
+ }
+ }
+}
+
+/**
+* add or remove space padding to objective-c parens
+* these options have precedence over the padParens methods
+* the padParens method has already been called, this method adjusts
+*/
+void ASFormatter::padObjCReturnType()
+{
+ assert(currentChar == ')' && isInObjCReturnType);
+ assert(shouldPadReturnType || shouldUnPadReturnType);
+
+ size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextText == string::npos)
+ return;
+ int spaces = nextText - charNum - 1;
+
+ if (shouldPadReturnType)
+ {
+ if (spaces == 0)
+ {
+ // this will already be padded if pad-paren is used
+ if (formattedLine[formattedLine.length() - 1] != ' ')
+ {
+ formattedLine.append(" ");
+ spacePadNum += 1;
+ }
+ }
+ else if (spaces > 1)
+ {
+ // do not use goForward here
+ currentLine.erase(charNum + 1, spaces - 1);
+ spacePadNum -= spaces - 1;
+ }
+ }
+ // this option will be ignored if used with pad-return-type
+ else if (shouldUnPadReturnType)
+ {
+ // this will already be padded if pad-paren is used
+ if (formattedLine[formattedLine.length() - 1] == ' ')
+ {
+ spacePadNum -= formattedLine.length() - 1 - nextText;
+ int lastText = formattedLine.find_last_not_of(" \t");
+ formattedLine.resize(lastText + 1);
+ }
+ if (spaces > 0)
+ {
+ // do not use goForward here
+ currentLine.erase(charNum + 1, spaces);
+ spacePadNum -= spaces;
+ }
+ }
+}
+
+/**
+* add or remove space padding to objective-c parens
+* these options have precedence over the padParens methods
+* the padParens method has already been called, this method adjusts
+*/
+void ASFormatter::padObjCParamType()
+{
+ assert((currentChar == '(' || currentChar == ')') && isInObjCMethodDefinition);
+ assert(!isImmediatelyPostObjCMethodPrefix && !isInObjCReturnType);
+ assert(shouldPadParamType || shouldUnPadParamType);
+
+ if (currentChar == '(')
+ {
+ // open paren has already been attached to formattedLine by padParen
+ size_t paramOpen = formattedLine.rfind('(');
+ assert(paramOpen != string::npos);
+ size_t prevText = formattedLine.find_last_not_of(" \t", paramOpen - 1);
+ if (prevText == string::npos)
+ return;
+ int spaces = paramOpen - prevText - 1;
+
+ if (shouldPadParamType
+ || objCColonPadMode == COLON_PAD_ALL
+ || objCColonPadMode == COLON_PAD_AFTER)
+ {
+ if (spaces == 0)
+ {
+ formattedLine.insert(paramOpen, 1, ' ');
+ spacePadNum += 1;
+ }
+ if (spaces > 1)
+ {
+ formattedLine.erase(prevText + 1, spaces - 1);
+ spacePadNum -= spaces - 1;
+ }
+ }
+ // this option will be ignored if used with pad-param-type
+ else if (shouldUnPadParamType
+ || objCColonPadMode == COLON_PAD_NONE
+ || objCColonPadMode == COLON_PAD_BEFORE)
+ {
+ if (spaces > 0)
+ {
+ formattedLine.erase(prevText + 1, spaces);
+ spacePadNum -= spaces;
+ }
+ }
+ }
+ else if (currentChar == ')')
+ {
+ size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextText == string::npos)
+ return;
+ int spaces = nextText - charNum - 1;
+
+ if (shouldPadParamType)
+ {
+ if (spaces == 0)
+ {
+ // this will already be padded if pad-paren is used
+ if (formattedLine[formattedLine.length() - 1] != ' ')
+ {
+ formattedLine.append(" ");
+ spacePadNum += 1;
+ }
+ }
+ else if (spaces > 1)
+ {
+ // do not use goForward here
+ currentLine.erase(charNum + 1, spaces - 1);
+ spacePadNum -= spaces - 1;
+ }
+ }
+ // this option will be ignored if used with pad-param-type
+ else if (shouldUnPadParamType)
+ {
+ // this will already be padded if pad-paren is used
+ if (formattedLine[formattedLine.length() - 1] == ' ')
+ {
+ spacePadNum -= 1;
+ int lastText = formattedLine.find_last_not_of(" \t");
+ formattedLine.resize(lastText + 1);
+ }
+ if (spaces > 0)
+ {
+ // do not use goForward here
+ currentLine.erase(charNum + 1, spaces);
+ spacePadNum -= spaces;
+ }
+ }
+ }
+}
+
+/**
+ * format opening brace as attached or broken
+ * currentChar contains the brace
+ * the braces will be appended to the current formattedLine or a new formattedLine as necessary
+ * the calling function should have a continue statement after calling this method
+ *
+ * @param braceType the type of brace to be formatted.
+ */
+void ASFormatter::formatOpeningBrace(BraceType braceType)
+{
+ assert(!isBraceType(braceType, ARRAY_TYPE));
+ assert(currentChar == '{');
+
+ parenStack->emplace_back(0);
+
+ bool breakBrace = isCurrentBraceBroken();
+
+ if (breakBrace)
+ {
+ if (isBeforeAnyComment() && isOkToBreakBlock(braceType))
+ {
+ // if comment is at line end leave the comment on this line
+ if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
+ {
+ currentChar = ' '; // remove brace from current line
+ if (parenStack->size() > 1)
+ parenStack->pop_back();
+ currentLine[charNum] = currentChar;
+ appendOpeningBrace = true; // append brace to following line
+ }
+ // else put comment after the brace
+ else if (!isBeforeMultipleLineEndComments(charNum))
+ breakLine();
+ }
+ else if (!isBraceType(braceType, SINGLE_LINE_TYPE))
+ {
+ formattedLine = rtrim(formattedLine);
+ breakLine();
+ }
+ else if ((shouldBreakOneLineBlocks || isBraceType(braceType, BREAK_BLOCK_TYPE))
+ && !isBraceType(braceType, EMPTY_BLOCK_TYPE))
+ breakLine();
+ else if (!isInLineBreak)
+ appendSpacePad();
+
+ appendCurrentChar();
+
+ // should a following comment break from the brace?
+ // must break the line AFTER the brace
+ if (isBeforeComment()
+ && formattedLine.length() > 0
+ && formattedLine[0] == '{'
+ && isOkToBreakBlock(braceType)
+ && (braceFormatMode == BREAK_MODE
+ || braceFormatMode == LINUX_MODE))
+ {
+ shouldBreakLineAtNextChar = true;
+ }
+ }
+ else // attach brace
+ {
+ // are there comments before the brace?
+ if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment)
+ {
+ if (isOkToBreakBlock(braceType)
+ && !(isCharImmediatelyPostComment && isCharImmediatelyPostLineComment) // don't attach if two comments on the line
+ && !isImmediatelyPostPreprocessor
+// && peekNextChar() != '}' // don't attach { } // removed release 2.03
+ && previousCommandChar != '{' // don't attach { {
+ && previousCommandChar != '}' // don't attach } {
+ && previousCommandChar != ';') // don't attach ; {
+ {
+ appendCharInsideComments();
+ }
+ else
+ {
+ appendCurrentChar(); // don't attach
+ }
+ }
+ else if (previousCommandChar == '{'
+ || (previousCommandChar == '}' && !isInClassInitializer)
+ || previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';'
+ {
+ appendCurrentChar(); // don't attach
+ }
+ else
+ {
+ // if a blank line precedes this don't attach
+ if (isEmptyLine(formattedLine))
+ appendCurrentChar(); // don't attach
+ else if (isOkToBreakBlock(braceType)
+ && !(isImmediatelyPostPreprocessor
+ && currentLineBeginsWithBrace))
+ {
+ if (!isBraceType(braceType, EMPTY_BLOCK_TYPE))
+ {
+ appendSpacePad();
+ appendCurrentChar(false); // OK to attach
+ testForTimeToSplitFormattedLine(); // line length will have changed
+ // should a following comment attach with the brace?
+ // insert spaces to reposition the comment
+ if (isBeforeComment()
+ && !isBeforeMultipleLineEndComments(charNum)
+ && (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace))
+ {
+ shouldBreakLineAtNextChar = true;
+ currentLine.insert(charNum + 1, charNum + 1, ' ');
+ }
+ else if (!isBeforeAnyComment()) // added in release 2.03
+ {
+ shouldBreakLineAtNextChar = true;
+ }
+ }
+ else
+ {
+ if (currentLineBeginsWithBrace && charNum == (int) currentLineFirstBraceNum)
+ {
+ appendSpacePad();
+ appendCurrentChar(false); // attach
+ shouldBreakLineAtNextChar = true;
+ }
+ else
+ {
+ appendSpacePad();
+ appendCurrentChar(); // don't attach
+ }
+ }
+ }
+ else
+ {
+ if (!isInLineBreak)
+ appendSpacePad();
+ appendCurrentChar(); // don't attach
+ }
+ }
+ }
+}
+
+/**
+ * format closing brace
+ * currentChar contains the brace
+ * the calling function should have a continue statement after calling this method
+ *
+ * @param braceType the type of the opening brace for this closing brace.
+ */
+void ASFormatter::formatClosingBrace(BraceType braceType)
+{
+ assert(!isBraceType(braceType, ARRAY_TYPE));
+ assert(currentChar == '}');
+
+ // parenStack must contain one entry
+ if (parenStack->size() > 1)
+ parenStack->pop_back();
+
+ // mark state of immediately after empty block
+ // this state will be used for locating braces that appear immediately AFTER an empty block (e.g. '{} \n}').
+ if (previousCommandChar == '{')
+ isImmediatelyPostEmptyBlock = true;
+
+ if (attachClosingBraceMode)
+ {
+ // for now, namespaces and classes will be attached. Uncomment the lines below to break.
+ if ((isEmptyLine(formattedLine) // if a blank line precedes this
+ || isCharImmediatelyPostLineComment
+ || isCharImmediatelyPostComment
+ || (isImmediatelyPostPreprocessor && (int) currentLine.find_first_not_of(" \t") == charNum)
+// || (isBraceType(braceType, CLASS_TYPE) && isOkToBreakBlock(braceType) && previousNonWSChar != '{')
+// || (isBraceType(braceType, NAMESPACE_TYPE) && isOkToBreakBlock(braceType) && previousNonWSChar != '{')
+ )
+ && (!isBraceType(braceType, SINGLE_LINE_TYPE) || isOkToBreakBlock(braceType)))
+ {
+ breakLine();
+ appendCurrentChar(); // don't attach
+ }
+ else
+ {
+ if (previousNonWSChar != '{'
+ && (!isBraceType(braceType, SINGLE_LINE_TYPE)
+ || isOkToBreakBlock(braceType)))
+ appendSpacePad();
+ appendCurrentChar(false); // attach
+ }
+ }
+ else if (!isBraceType(braceType, EMPTY_BLOCK_TYPE)
+ && (isBraceType(braceType, BREAK_BLOCK_TYPE)
+ || isOkToBreakBlock(braceType)))
+ {
+ breakLine();
+ appendCurrentChar();
+ }
+ else
+ {
+ appendCurrentChar();
+ }
+
+ // if a declaration follows a definition, space pad
+ if (isLegalNameChar(peekNextChar()))
+ appendSpaceAfter();
+
+ if (shouldBreakBlocks
+ && currentHeader != nullptr
+ && !isHeaderInMultiStatementLine
+ && parenStack->back() == 0)
+ {
+ if (currentHeader == &AS_CASE || currentHeader == &AS_DEFAULT)
+ {
+ // do not yet insert a line if "break" statement is outside the braces
+ string nextText = peekNextText(currentLine.substr(charNum + 1));
+ if (nextText.length() > 0
+ && nextText.substr(0, 5) != "break")
+ isAppendPostBlockEmptyLineRequested = true;
+ }
+ else
+ isAppendPostBlockEmptyLineRequested = true;
+ }
+}
+
+/**
+ * format array braces as attached or broken
+ * determine if the braces can have an inStatement indent
+ * currentChar contains the brace
+ * the braces will be appended to the current formattedLine or a new formattedLine as necessary
+ * the calling function should have a continue statement after calling this method
+ *
+ * @param braceType the type of brace to be formatted, must be an ARRAY_TYPE.
+ * @param isOpeningArrayBrace indicates if this is the opening brace for the array block.
+ */
+void ASFormatter::formatArrayBraces(BraceType braceType, bool isOpeningArrayBrace)
+{
+ assert(isBraceType(braceType, ARRAY_TYPE));
+ assert(currentChar == '{' || currentChar == '}');
+
+ if (currentChar == '{')
+ {
+ // is this the first opening brace in the array?
+ if (isOpeningArrayBrace)
+ {
+ if (braceFormatMode == ATTACH_MODE
+ || braceFormatMode == LINUX_MODE)
+ {
+ // break an enum if mozilla
+ if (isBraceType(braceType, ENUM_TYPE)
+ && formattingStyle == STYLE_MOZILLA)
+ {
+ isInLineBreak = true;
+ appendCurrentChar(); // don't attach
+ }
+ // don't attach to a preprocessor directive or '\' line
+ else if ((isImmediatelyPostPreprocessor
+ || (formattedLine.length() > 0
+ && formattedLine[formattedLine.length() - 1] == '\\'))
+ && currentLineBeginsWithBrace)
+ {
+ isInLineBreak = true;
+ appendCurrentChar(); // don't attach
+ }
+ else if (isCharImmediatelyPostComment)
+ {
+ // TODO: attach brace to line-end comment
+ appendCurrentChar(); // don't attach
+ }
+ else if (isCharImmediatelyPostLineComment && !isBraceType(braceType, SINGLE_LINE_TYPE))
+ {
+ appendCharInsideComments();
+ }
+ else
+ {
+ // if a blank line precedes this don't attach
+ if (isEmptyLine(formattedLine))
+ appendCurrentChar(); // don't attach
+ else
+ {
+ // if brace is broken or not an assignment
+ if (currentLineBeginsWithBrace
+ && !isBraceType(braceType, SINGLE_LINE_TYPE))
+ {
+ appendSpacePad();
+ appendCurrentChar(false); // OK to attach
+ // TODO: debug the following line
+ testForTimeToSplitFormattedLine(); // line length will have changed
+
+ if (currentLineBeginsWithBrace
+ && (int) currentLineFirstBraceNum == charNum)
+ shouldBreakLineAtNextChar = true;
+ }
+ else
+ {
+ if (previousNonWSChar != '(')
+ {
+ // don't space pad C++11 uniform initialization
+ if (!isBraceType(braceType, INIT_TYPE))
+ appendSpacePad();
+ }
+ appendCurrentChar();
+ }
+ }
+ }
+ }
+ else if (braceFormatMode == BREAK_MODE)
+ {
+ if (isWhiteSpace(peekNextChar()) && !isInVirginLine)
+ breakLine();
+ else if (isBeforeAnyComment())
+ {
+ // do not break unless comment is at line end
+ if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
+ {
+ currentChar = ' '; // remove brace from current line
+ appendOpeningBrace = true; // append brace to following line
+ }
+ }
+ if (!isInLineBreak && previousNonWSChar != '(')
+ {
+ // don't space pad C++11 uniform initialization
+ if (!isBraceType(braceType, INIT_TYPE))
+ appendSpacePad();
+ }
+ appendCurrentChar();
+
+ if (currentLineBeginsWithBrace
+ && (int) currentLineFirstBraceNum == charNum
+ && !isBraceType(braceType, SINGLE_LINE_TYPE))
+ shouldBreakLineAtNextChar = true;
+ }
+ else if (braceFormatMode == RUN_IN_MODE)
+ {
+ if (isWhiteSpace(peekNextChar()) && !isInVirginLine)
+ breakLine();
+ else if (isBeforeAnyComment())
+ {
+ // do not break unless comment is at line end
+ if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
+ {
+ currentChar = ' '; // remove brace from current line
+ appendOpeningBrace = true; // append brace to following line
+ }
+ }
+ if (!isInLineBreak && previousNonWSChar != '(')
+ {
+ // don't space pad C++11 uniform initialization
+ if (!isBraceType(braceType, INIT_TYPE))
+ appendSpacePad();
+ }
+ appendCurrentChar();
+ }
+ else if (braceFormatMode == NONE_MODE)
+ {
+ if (currentLineBeginsWithBrace
+ && charNum == (int) currentLineFirstBraceNum)
+ {
+ appendCurrentChar(); // don't attach
+ }
+ else
+ {
+ if (previousNonWSChar != '(')
+ {
+ // don't space pad C++11 uniform initialization
+ if (!isBraceType(braceType, INIT_TYPE))
+ appendSpacePad();
+ }
+ appendCurrentChar(false); // OK to attach
+ }
+ }
+ }
+ else // not the first opening brace
+ {
+ if (braceFormatMode == RUN_IN_MODE)
+ {
+ if (previousNonWSChar == '{'
+ && braceTypeStack->size() > 2
+ && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2],
+ SINGLE_LINE_TYPE))
+ formatArrayRunIn();
+ }
+ else if (!isInLineBreak
+ && !isWhiteSpace(peekNextChar())
+ && previousNonWSChar == '{'
+ && braceTypeStack->size() > 2
+ && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2],
+ SINGLE_LINE_TYPE))
+ formatArrayRunIn();
+
+ appendCurrentChar();
+ }
+ }
+ else if (currentChar == '}')
+ {
+ if (attachClosingBraceMode)
+ {
+ if (isEmptyLine(formattedLine) // if a blank line precedes this
+ || isImmediatelyPostPreprocessor
+ || isCharImmediatelyPostLineComment
+ || isCharImmediatelyPostComment)
+ appendCurrentChar(); // don't attach
+ else
+ {
+ appendSpacePad();
+ appendCurrentChar(false); // attach
+ }
+ }
+ else
+ {
+ // does this close the first opening brace in the array?
+ // must check if the block is still a single line because of anonymous statements
+ if (!isBraceType(braceType, INIT_TYPE)
+ && (!isBraceType(braceType, SINGLE_LINE_TYPE)
+ || formattedLine.find('{') == string::npos))
+ breakLine();
+ appendCurrentChar();
+ }
+
+ // if a declaration follows an enum definition, space pad
+ char peekedChar = peekNextChar();
+ if (isLegalNameChar(peekedChar)
+ || peekedChar == '[')
+ appendSpaceAfter();
+ }
+}
+
+/**
+ * determine if a run-in can be attached.
+ * if it can insert the indents in formattedLine and reset the current line break.
+ */
+void ASFormatter::formatRunIn()
+{
+ assert(braceFormatMode == RUN_IN_MODE || braceFormatMode == NONE_MODE);
+
+ // keep one line blocks returns true without indenting the run-in
+ if (formattingStyle != STYLE_PICO
+ && !isOkToBreakBlock(braceTypeStack->back()))
+ return; // true;
+
+ // make sure the line begins with a brace
+ size_t lastText = formattedLine.find_last_not_of(" \t");
+ if (lastText == string::npos || formattedLine[lastText] != '{')
+ return; // false;
+
+ // make sure the brace is broken
+ if (formattedLine.find_first_not_of(" \t{") != string::npos)
+ return; // false;
+
+ if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
+ return; // false;
+
+ bool extraIndent = false;
+ bool extraHalfIndent = false;
+ isInLineBreak = true;
+
+ // cannot attach a class modifier without indent-classes
+ if (isCStyle()
+ && isCharPotentialHeader(currentLine, charNum)
+ && (isBraceType(braceTypeStack->back(), CLASS_TYPE)
+ || (isBraceType(braceTypeStack->back(), STRUCT_TYPE)
+ && isInIndentableStruct)))
+ {
+ if (findKeyword(currentLine, charNum, AS_PUBLIC)
+ || findKeyword(currentLine, charNum, AS_PRIVATE)
+ || findKeyword(currentLine, charNum, AS_PROTECTED))
+ {
+ if (getModifierIndent())
+ extraHalfIndent = true;
+ else if (!getClassIndent())
+ return; // false;
+ }
+ else if (getClassIndent())
+ extraIndent = true;
+ }
+
+ // cannot attach a 'case' statement without indent-switches
+ if (!getSwitchIndent()
+ && isCharPotentialHeader(currentLine, charNum)
+ && (findKeyword(currentLine, charNum, AS_CASE)
+ || findKeyword(currentLine, charNum, AS_DEFAULT)))
+ return; // false;
+
+ // extra indent for switch statements
+ if (getSwitchIndent()
+ && !preBraceHeaderStack->empty()
+ && preBraceHeaderStack->back() == &AS_SWITCH
+ && ((isLegalNameChar(currentChar)
+ && !findKeyword(currentLine, charNum, AS_CASE))))
+ extraIndent = true;
+
+ isInLineBreak = false;
+ // remove for extra whitespace
+ if (formattedLine.length() > lastText + 1
+ && formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos)
+ formattedLine.erase(lastText + 1);
+
+ if (extraHalfIndent)
+ {
+ int indentLength_ = getIndentLength();
+ runInIndentChars = indentLength_ / 2;
+ formattedLine.append(runInIndentChars - 1, ' ');
+ }
+ else if (getForceTabIndentation() && getIndentLength() != getTabLength())
+ {
+ // insert the space indents
+ string indent;
+ int indentLength_ = getIndentLength();
+ int tabLength_ = getTabLength();
+ indent.append(indentLength_, ' ');
+ if (extraIndent)
+ indent.append(indentLength_, ' ');
+ // replace spaces indents with tab indents
+ size_t tabCount = indent.length() / tabLength_; // truncate extra spaces
+ indent.replace(0U, tabCount * tabLength_, tabCount, '\t');
+ runInIndentChars = indentLength_;
+ if (indent[0] == ' ') // allow for brace
+ indent.erase(0, 1);
+ formattedLine.append(indent);
+ }
+ else if (getIndentString() == "\t")
+ {
+ appendChar('\t', false);
+ runInIndentChars = 2; // one for { and one for tab
+ if (extraIndent)
+ {
+ appendChar('\t', false);
+ runInIndentChars++;
+ }
+ }
+ else // spaces
+ {
+ int indentLength_ = getIndentLength();
+ formattedLine.append(indentLength_ - 1, ' ');
+ runInIndentChars = indentLength_;
+ if (extraIndent)
+ {
+ formattedLine.append(indentLength_, ' ');
+ runInIndentChars += indentLength_;
+ }
+ }
+ isInBraceRunIn = true;
+}
+
+/**
+ * remove whitespace and add indentation for an array run-in.
+ */
+void ASFormatter::formatArrayRunIn()
+{
+ assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE));
+
+ // make sure the brace is broken
+ if (formattedLine.find_first_not_of(" \t{") != string::npos)
+ return;
+
+ size_t lastText = formattedLine.find_last_not_of(" \t");
+ if (lastText == string::npos || formattedLine[lastText] != '{')
+ return;
+
+ // check for extra whitespace
+ if (formattedLine.length() > lastText + 1
+ && formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos)
+ formattedLine.erase(lastText + 1);
+
+ if (getIndentString() == "\t")
+ {
+ appendChar('\t', false);
+ runInIndentChars = 2; // one for { and one for tab
+ }
+ else
+ {
+ int indent = getIndentLength();
+ formattedLine.append(indent - 1, ' ');
+ runInIndentChars = indent;
+ }
+ isInBraceRunIn = true;
+ isInLineBreak = false;
+}
+
+/**
+ * delete a braceTypeStack vector object
+ * BraceTypeStack did not work with the DeleteContainer template
+ */
+void ASFormatter::deleteContainer(vector<BraceType>*& container)
+{
+ if (container != nullptr)
+ {
+ container->clear();
+ delete (container);
+ container = nullptr;
+ }
+}
+
+/**
+ * delete a vector object
+ * T is the type of vector
+ * used for all vectors except braceTypeStack
+ */
+template<typename T>
+void ASFormatter::deleteContainer(T& container)
+{
+ if (container != nullptr)
+ {
+ container->clear();
+ delete (container);
+ container = nullptr;
+ }
+}
+
+/**
+ * initialize a braceType vector object
+ * braceType did not work with the DeleteContainer template
+ */
+void ASFormatter::initContainer(vector<BraceType>*& container, vector<BraceType>* value)
+{
+ if (container != nullptr)
+ deleteContainer(container);
+ container = value;
+}
+
+/**
+ * initialize a vector object
+ * T is the type of vector
+ * used for all vectors except braceTypeStack
+ */
+template<typename T>
+void ASFormatter::initContainer(T& container, T value)
+{
+ // since the ASFormatter object is never deleted,
+ // the existing vectors must be deleted before creating new ones
+ if (container != nullptr)
+ deleteContainer(container);
+ container = value;
+}
+
+/**
+ * convert a tab to spaces.
+ * charNum points to the current character to convert to spaces.
+ * tabIncrementIn is the increment that must be added for tab indent characters
+ * to get the correct column for the current tab.
+ * replaces the tab in currentLine with the required number of spaces.
+ * replaces the value of currentChar.
+ */
+void ASFormatter::convertTabToSpaces()
+{
+ assert(currentChar == '\t');
+
+ // do NOT replace if in quotes
+ if (isInQuote || isInQuoteContinuation)
+ return;
+
+ size_t tabSize = getTabLength();
+ size_t numSpaces = tabSize - ((tabIncrementIn + charNum) % tabSize);
+ currentLine.replace(charNum, 1, numSpaces, ' ');
+ currentChar = currentLine[charNum];
+}
+
+/**
+* is it ok to break this block?
+*/
+bool ASFormatter::isOkToBreakBlock(BraceType braceType) const
+{
+ // Actually, there should not be an ARRAY_TYPE brace here.
+ // But this will avoid breaking a one line block when there is.
+ // Otherwise they will be formatted differently on consecutive runs.
+ if (isBraceType(braceType, ARRAY_TYPE)
+ && isBraceType(braceType, SINGLE_LINE_TYPE))
+ return false;
+ if (isBraceType(braceType, COMMAND_TYPE)
+ && isBraceType(braceType, EMPTY_BLOCK_TYPE))
+ return false;
+ if (!isBraceType(braceType, SINGLE_LINE_TYPE)
+ || isBraceType(braceType, BREAK_BLOCK_TYPE)
+ || shouldBreakOneLineBlocks)
+ return true;
+ return false;
+}
+
+/**
+* check if a sharp header is a paren or non-paren header
+*/
+bool ASFormatter::isSharpStyleWithParen(const string* header) const
+{
+ return (isSharpStyle() && peekNextChar() == '('
+ && (header == &AS_CATCH
+ || header == &AS_DELEGATE));
+}
+
+/**
+ * Check for a following header when a comment is reached.
+ * firstLine must contain the start of the comment.
+ * return value is a pointer to the header or nullptr.
+ */
+const string* ASFormatter::checkForHeaderFollowingComment(const string& firstLine) const
+{
+ assert(isInComment || isInLineComment);
+ assert(shouldBreakElseIfs || shouldBreakBlocks || isInSwitchStatement());
+ // look ahead to find the next non-comment text
+ bool endOnEmptyLine = (currentHeader == nullptr);
+ if (isInSwitchStatement())
+ endOnEmptyLine = false;
+ string nextText = peekNextText(firstLine, endOnEmptyLine);
+
+ if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
+ return nullptr;
+
+ return ASBase::findHeader(nextText, 0, headers);
+}
+
+/**
+ * process preprocessor statements.
+ * charNum should be the index of the #.
+ *
+ * delete braceTypeStack entries added by #if if a #else is found.
+ * prevents double entries in the braceTypeStack.
+ */
+void ASFormatter::processPreprocessor()
+{
+ assert(currentChar == '#');
+
+ const size_t preproc = currentLine.find_first_not_of(" \t", charNum + 1);
+
+ if (preproc == string::npos)
+ return;
+
+ if (currentLine.compare(preproc, 2, "if") == 0)
+ {
+ preprocBraceTypeStackSize = braceTypeStack->size();
+ }
+ else if (currentLine.compare(preproc, 4, "else") == 0)
+ {
+ // delete stack entries added in #if
+ // should be replaced by #else
+ if (preprocBraceTypeStackSize > 0)
+ {
+ int addedPreproc = braceTypeStack->size() - preprocBraceTypeStackSize;
+ for (int i = 0; i < addedPreproc; i++)
+ braceTypeStack->pop_back();
+ }
+ }
+}
+
+/**
+ * determine if the next line starts a comment
+ * and a header follows the comment or comments.
+ */
+bool ASFormatter::commentAndHeaderFollows()
+{
+ // called ONLY IF shouldDeleteEmptyLines and shouldBreakBlocks are TRUE.
+ assert(shouldDeleteEmptyLines && shouldBreakBlocks);
+
+ // is the next line a comment
+ auto stream = make_shared<ASPeekStream>(sourceIterator);
+ if (!stream->hasMoreLines())
+ return false;
+ string nextLine_ = stream->peekNextLine();
+ size_t firstChar = nextLine_.find_first_not_of(" \t");
+ if (firstChar == string::npos
+ || !(nextLine_.compare(firstChar, 2, "//") == 0
+ || nextLine_.compare(firstChar, 2, "/*") == 0))
+ return false;
+
+ // find the next non-comment text, and reset
+ string nextText = peekNextText(nextLine_, false, stream);
+ if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
+ return false;
+
+ const string* newHeader = ASBase::findHeader(nextText, 0, headers);
+
+ if (newHeader == nullptr)
+ return false;
+
+ // if a closing header, reset break unless break is requested
+ if (isClosingHeader(newHeader) && !shouldBreakClosingHeaderBlocks)
+ {
+ isAppendPostBlockEmptyLineRequested = false;
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * determine if a brace should be attached or broken
+ * uses braces in the braceTypeStack
+ * the last brace in the braceTypeStack is the one being formatted
+ * returns true if the brace should be broken
+ */
+bool ASFormatter::isCurrentBraceBroken() const
+{
+ assert(braceTypeStack->size() > 1);
+
+ bool breakBrace = false;
+ size_t stackEnd = braceTypeStack->size() - 1;
+
+ // check brace modifiers
+ if (shouldAttachExternC
+ && isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE))
+ {
+ return false;
+ }
+ if (shouldAttachNamespace
+ && isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE))
+ {
+ return false;
+ }
+ if (shouldAttachClass
+ && (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE)
+ || isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE)))
+ {
+ return false;
+ }
+ if (shouldAttachInline
+ && isCStyle() // for C++ only
+ && braceFormatMode != RUN_IN_MODE
+ && !(currentLineBeginsWithBrace && peekNextChar() == '/')
+ && isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE))
+ {
+ size_t i;
+ for (i = 1; i < braceTypeStack->size(); i++)
+ if (isBraceType((*braceTypeStack)[i], CLASS_TYPE)
+ || isBraceType((*braceTypeStack)[i], STRUCT_TYPE))
+ return false;
+ }
+
+ // check braces
+ if (isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE))
+ {
+ if (currentLineBeginsWithBrace
+ || braceFormatMode == RUN_IN_MODE)
+ breakBrace = true;
+ }
+ else if (braceFormatMode == NONE_MODE)
+ {
+ if (currentLineBeginsWithBrace
+ && (int) currentLineFirstBraceNum == charNum)
+ breakBrace = true;
+ }
+ else if (braceFormatMode == BREAK_MODE || braceFormatMode == RUN_IN_MODE)
+ {
+ breakBrace = true;
+ }
+ else if (braceFormatMode == LINUX_MODE)
+ {
+ // break a namespace if NOT stroustrup or mozilla
+ if (isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE))
+ {
+ if (formattingStyle != STYLE_STROUSTRUP
+ && formattingStyle != STYLE_MOZILLA)
+ breakBrace = true;
+ }
+ // break a class or interface if NOT stroustrup
+ else if (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE)
+ || isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE))
+ {
+ if (formattingStyle != STYLE_STROUSTRUP)
+ breakBrace = true;
+ }
+ // break a struct if mozilla - an enum is processed as an array brace
+ else if (isBraceType((*braceTypeStack)[stackEnd], STRUCT_TYPE))
+ {
+ if (formattingStyle == STYLE_MOZILLA)
+ breakBrace = true;
+ }
+ // break the first brace if a function
+ else if (isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE))
+ {
+ if (stackEnd == 1)
+ {
+ breakBrace = true;
+ }
+ else if (stackEnd > 1)
+ {
+ // break the first brace after these if a function
+ if (isBraceType((*braceTypeStack)[stackEnd - 1], NAMESPACE_TYPE)
+ || isBraceType((*braceTypeStack)[stackEnd - 1], CLASS_TYPE)
+ || isBraceType((*braceTypeStack)[stackEnd - 1], ARRAY_TYPE)
+ || isBraceType((*braceTypeStack)[stackEnd - 1], STRUCT_TYPE)
+ || isBraceType((*braceTypeStack)[stackEnd - 1], EXTERN_TYPE))
+ {
+ breakBrace = true;
+ }
+ }
+ }
+ }
+ return breakBrace;
+}
+
+/**
+ * format comment body
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::formatCommentBody()
+{
+ assert(isInComment);
+
+ // append the comment
+ while (charNum < (int) currentLine.length())
+ {
+ currentChar = currentLine[charNum];
+ if (isSequenceReached("*/"))
+ {
+ formatCommentCloser();
+ break;
+ }
+ if (currentChar == '\t' && shouldConvertTabs)
+ convertTabToSpaces();
+ appendCurrentChar();
+ ++charNum;
+ }
+ if (shouldStripCommentPrefix)
+ stripCommentPrefix();
+}
+
+/**
+ * format a comment opener
+ * the comment opener will be appended to the current formattedLine or a new formattedLine as necessary
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::formatCommentOpener()
+{
+ assert(isSequenceReached("/*"));
+
+ isInComment = isInCommentStartLine = true;
+ isImmediatelyPostLineComment = false;
+ if (previousNonWSChar == '}')
+ resetEndOfStatement();
+
+ // Check for a following header.
+ // For speed do not check multiple comment lines more than once.
+ // For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'.
+ const string* followingHeader = nullptr;
+ if ((doesLineStartComment
+ && !isImmediatelyPostCommentOnly
+ && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
+ && (shouldBreakElseIfs
+ || isInSwitchStatement()
+ || (shouldBreakBlocks
+ && !isImmediatelyPostEmptyLine
+ && previousCommandChar != '{')))
+ followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum));
+
+ if (spacePadNum != 0 && !isInLineBreak)
+ adjustComments();
+ formattedLineCommentNum = formattedLine.length();
+
+ // must be done BEFORE appendSequence
+ if (previousCommandChar == '{'
+ && !isImmediatelyPostComment
+ && !isImmediatelyPostLineComment)
+ {
+ if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
+ {
+ // namespace run-in is always broken.
+ isInLineBreak = true;
+ }
+ else if (braceFormatMode == NONE_MODE)
+ {
+ // should a run-in statement be attached?
+ if (currentLineBeginsWithBrace)
+ formatRunIn();
+ }
+ else if (braceFormatMode == ATTACH_MODE)
+ {
+ // if the brace was not attached?
+ if (formattedLine.length() > 0 && formattedLine[0] == '{'
+ && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
+ isInLineBreak = true;
+ }
+ else if (braceFormatMode == RUN_IN_MODE)
+ {
+ // should a run-in statement be attached?
+ if (formattedLine.length() > 0 && formattedLine[0] == '{')
+ formatRunIn();
+ }
+ }
+ else if (!doesLineStartComment)
+ noTrimCommentContinuation = true;
+
+ // ASBeautifier needs to know the following statements
+ if (shouldBreakElseIfs && followingHeader == &AS_ELSE)
+ elseHeaderFollowsComments = true;
+ if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT)
+ caseHeaderFollowsComments = true;
+
+ // appendSequence will write the previous line
+ appendSequence(AS_OPEN_COMMENT);
+ goForward(1);
+
+ // must be done AFTER appendSequence
+
+ // Break before the comment if a header follows the line comment.
+ // But not break if previous line is empty, a comment, or a '{'.
+ if (shouldBreakBlocks
+ && followingHeader != nullptr
+ && !isImmediatelyPostEmptyLine
+ && previousCommandChar != '{')
+ {
+ if (isClosingHeader(followingHeader))
+ {
+ if (!shouldBreakClosingHeaderBlocks)
+ isPrependPostBlockEmptyLineRequested = false;
+ }
+ // if an opening header, break before the comment
+ else
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+
+ if (previousCommandChar == '}')
+ currentHeader = nullptr;
+}
+
+/**
+ * format a comment closer
+ * the comment closer will be appended to the current formattedLine
+ */
+void ASFormatter::formatCommentCloser()
+{
+ assert(isSequenceReached("*/"));
+ isInComment = false;
+ noTrimCommentContinuation = false;
+ isImmediatelyPostComment = true;
+ appendSequence(AS_CLOSE_COMMENT);
+ goForward(1);
+ if (doesLineStartComment
+ && (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos))
+ lineEndsInCommentOnly = true;
+ if (peekNextChar() == '}'
+ && previousCommandChar != ';'
+ && !isBraceType(braceTypeStack->back(), ARRAY_TYPE)
+ && !isInPreprocessor
+ && isOkToBreakBlock(braceTypeStack->back()))
+ {
+ isInLineBreak = true;
+ shouldBreakLineAtNextChar = true;
+ }
+}
+
+/**
+ * format a line comment body
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::formatLineCommentBody()
+{
+ assert(isInLineComment);
+
+ // append the comment
+ while (charNum < (int) currentLine.length())
+// && !isLineReady // commented out in release 2.04, unnecessary
+ {
+ currentChar = currentLine[charNum];
+ if (currentChar == '\t' && shouldConvertTabs)
+ convertTabToSpaces();
+ appendCurrentChar();
+ ++charNum;
+ }
+
+ // explicitly break a line when a line comment's end is found.
+ if (charNum == (int) currentLine.length())
+ {
+ isInLineBreak = true;
+ isInLineComment = false;
+ isImmediatelyPostLineComment = true;
+ currentChar = 0; //make sure it is a neutral char.
+ }
+}
+
+/**
+ * format a line comment opener
+ * the line comment opener will be appended to the current formattedLine or a new formattedLine as necessary
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::formatLineCommentOpener()
+{
+ assert(isSequenceReached("//"));
+
+ if ((int) currentLine.length() > charNum + 2
+ && currentLine[charNum + 2] == '\xf2') // check for windows line marker
+ isAppendPostBlockEmptyLineRequested = false;
+
+ isInLineComment = true;
+ isCharImmediatelyPostComment = false;
+ if (previousNonWSChar == '}')
+ resetEndOfStatement();
+
+ // Check for a following header.
+ // For speed do not check multiple comment lines more than once.
+ // For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'.
+ const string* followingHeader = nullptr;
+ if ((lineIsLineCommentOnly
+ && !isImmediatelyPostCommentOnly
+ && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
+ && (shouldBreakElseIfs
+ || isInSwitchStatement()
+ || (shouldBreakBlocks
+ && !isImmediatelyPostEmptyLine
+ && previousCommandChar != '{')))
+ followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum));
+
+ // do not indent if in column 1 or 2
+ // or in a namespace before the opening brace
+ if ((!shouldIndentCol1Comments && !lineCommentNoIndent)
+ || foundNamespaceHeader)
+ {
+ if (charNum == 0)
+ lineCommentNoIndent = true;
+ else if (charNum == 1 && currentLine[0] == ' ')
+ lineCommentNoIndent = true;
+ }
+ // move comment if spaces were added or deleted
+ if (!lineCommentNoIndent && spacePadNum != 0 && !isInLineBreak)
+ adjustComments();
+ formattedLineCommentNum = formattedLine.length();
+
+ // must be done BEFORE appendSequence
+ // check for run-in statement
+ if (previousCommandChar == '{'
+ && !isImmediatelyPostComment
+ && !isImmediatelyPostLineComment)
+ {
+ if (braceFormatMode == NONE_MODE)
+ {
+ if (currentLineBeginsWithBrace)
+ formatRunIn();
+ }
+ else if (braceFormatMode == RUN_IN_MODE)
+ {
+ if (!lineCommentNoIndent)
+ formatRunIn();
+ else
+ isInLineBreak = true;
+ }
+ else if (braceFormatMode == BREAK_MODE)
+ {
+ if (formattedLine.length() > 0 && formattedLine[0] == '{')
+ isInLineBreak = true;
+ }
+ else
+ {
+ if (currentLineBeginsWithBrace)
+ isInLineBreak = true;
+ }
+ }
+
+ // ASBeautifier needs to know the following statements
+ if (shouldBreakElseIfs && followingHeader == &AS_ELSE)
+ elseHeaderFollowsComments = true;
+ if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT)
+ caseHeaderFollowsComments = true;
+
+ // appendSequence will write the previous line
+ appendSequence(AS_OPEN_LINE_COMMENT);
+ goForward(1);
+
+ // must be done AFTER appendSequence
+
+ // Break before the comment if a header follows the line comment.
+ // But do not break if previous line is empty, a comment, or a '{'.
+ if (shouldBreakBlocks
+ && followingHeader != nullptr
+ && !isImmediatelyPostEmptyLine
+ && previousCommandChar != '{')
+ {
+ if (isClosingHeader(followingHeader))
+ {
+ if (!shouldBreakClosingHeaderBlocks)
+ isPrependPostBlockEmptyLineRequested = false;
+ }
+ // if an opening header, break before the comment
+ else
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+
+ if (previousCommandChar == '}')
+ currentHeader = nullptr;
+
+ // if tabbed input don't convert the immediately following tabs to spaces
+ if (getIndentString() == "\t" && lineCommentNoIndent)
+ {
+ while (charNum + 1 < (int) currentLine.length()
+ && currentLine[charNum + 1] == '\t')
+ {
+ currentChar = currentLine[++charNum];
+ appendCurrentChar();
+ }
+ }
+
+ // explicitly break a line when a line comment's end is found.
+ if (charNum + 1 == (int) currentLine.length())
+ {
+ isInLineBreak = true;
+ isInLineComment = false;
+ isImmediatelyPostLineComment = true;
+ currentChar = 0; //make sure it is a neutral char.
+ }
+}
+
+/**
+ * format quote body
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::formatQuoteBody()
+{
+ assert(isInQuote);
+
+ if (isSpecialChar)
+ {
+ isSpecialChar = false;
+ }
+ else if (currentChar == '\\' && !isInVerbatimQuote)
+ {
+ if (peekNextChar() == ' ') // is this '\' at end of line
+ haveLineContinuationChar = true;
+ else
+ isSpecialChar = true;
+ }
+ else if (isInVerbatimQuote && currentChar == '"')
+ {
+ if (isCStyle())
+ {
+ string delim = ')' + verbatimDelimiter;
+ int delimStart = charNum - delim.length();
+ if (delimStart > 0 && currentLine.substr(delimStart, delim.length()) == delim)
+ {
+ isInQuote = false;
+ isInVerbatimQuote = false;
+ }
+ }
+ else if (isSharpStyle())
+ {
+ if ((int) currentLine.length() > charNum + 1
+ && currentLine[charNum + 1] == '"') // check consecutive quotes
+ {
+ appendSequence("\"\"");
+ goForward(1);
+ return;
+ }
+ isInQuote = false;
+ isInVerbatimQuote = false;
+ }
+ }
+ else if (quoteChar == currentChar)
+ {
+ isInQuote = false;
+ }
+
+ appendCurrentChar();
+
+ // append the text to the ending quoteChar or an escape sequence
+ // tabs in quotes are NOT changed by convert-tabs
+ if (isInQuote && currentChar != '\\')
+ {
+ while (charNum + 1 < (int) currentLine.length()
+ && currentLine[charNum + 1] != quoteChar
+ && currentLine[charNum + 1] != '\\')
+ {
+ currentChar = currentLine[++charNum];
+ appendCurrentChar();
+ }
+ }
+ if (charNum + 1 >= (int) currentLine.length()
+ && currentChar != '\\'
+ && !isInVerbatimQuote)
+ isInQuote = false; // missing closing quote
+}
+
+/**
+ * format a quote opener
+ * the quote opener will be appended to the current formattedLine or a new formattedLine as necessary
+ * the calling function should have a continue statement after calling this method
+ */
+void ASFormatter::formatQuoteOpener()
+{
+ assert(currentChar == '"'
+ || (currentChar == '\'' && !isDigitSeparator(currentLine, charNum)));
+
+ isInQuote = true;
+ quoteChar = currentChar;
+ if (isCStyle() && previousChar == 'R')
+ {
+ int parenPos = currentLine.find('(', charNum);
+ if (parenPos != -1)
+ {
+ isInVerbatimQuote = true;
+ verbatimDelimiter = currentLine.substr(charNum + 1, parenPos - charNum - 1);
+ }
+ }
+ else if (isSharpStyle() && previousChar == '@')
+ isInVerbatimQuote = true;
+
+ // a quote following a brace is an array
+ if (previousCommandChar == '{'
+ && !isImmediatelyPostComment
+ && !isImmediatelyPostLineComment
+ && isNonInStatementArray
+ && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
+ && !isWhiteSpace(peekNextChar()))
+ {
+ if (braceFormatMode == NONE_MODE)
+ {
+ if (currentLineBeginsWithBrace)
+ formatRunIn();
+ }
+ else if (braceFormatMode == RUN_IN_MODE)
+ {
+ formatRunIn();
+ }
+ else if (braceFormatMode == BREAK_MODE)
+ {
+ if (formattedLine.length() > 0 && formattedLine[0] == '{')
+ isInLineBreak = true;
+ }
+ else
+ {
+ if (currentLineBeginsWithBrace)
+ isInLineBreak = true;
+ }
+ }
+ previousCommandChar = ' ';
+ appendCurrentChar();
+}
+
+/**
+ * get the next line comment adjustment that results from breaking a closing brace.
+ * the brace must be on the same line as the closing header.
+ * i.e "} else" changed to "} <NL> else".
+ */
+int ASFormatter::getNextLineCommentAdjustment()
+{
+ assert(foundClosingHeader && previousNonWSChar == '}');
+ if (charNum < 1) // "else" is in column 1
+ return 0;
+ size_t lastBrace = currentLine.rfind('}', charNum - 1);
+ if (lastBrace != string::npos)
+ return (lastBrace - charNum); // return a negative number
+ return 0;
+}
+
+// for console build only
+LineEndFormat ASFormatter::getLineEndFormat() const
+{
+ return lineEnd;
+}
+
+/**
+ * get the current line comment adjustment that results from attaching
+ * a closing header to a closing brace.
+ * the brace must be on the line previous to the closing header.
+ * the adjustment is 2 chars, one for the brace and one for the space.
+ * i.e "} <NL> else" changed to "} else".
+ */
+int ASFormatter::getCurrentLineCommentAdjustment()
+{
+ assert(foundClosingHeader && previousNonWSChar == '}');
+ if (charNum < 1)
+ return 2;
+ size_t lastBrace = currentLine.rfind('}', charNum - 1);
+ if (lastBrace == string::npos)
+ return 2;
+ return 0;
+}
+
+/**
+ * get the previous word on a line
+ * the argument 'currPos' must point to the current position.
+ *
+ * @return is the previous word or an empty string if none found.
+ */
+string ASFormatter::getPreviousWord(const string& line, int currPos) const
+{
+ // get the last legal word (may be a number)
+ if (currPos == 0)
+ return string();
+
+ size_t end = line.find_last_not_of(" \t", currPos - 1);
+ if (end == string::npos || !isLegalNameChar(line[end]))
+ return string();
+
+ int start; // start of the previous word
+ for (start = end; start > -1; start--)
+ {
+ if (!isLegalNameChar(line[start]) || line[start] == '.')
+ break;
+ }
+ start++;
+
+ return (line.substr(start, end - start + 1));
+}
+
+/**
+ * check if a line break is needed when a closing brace
+ * is followed by a closing header.
+ * the break depends on the braceFormatMode and other factors.
+ */
+void ASFormatter::isLineBreakBeforeClosingHeader()
+{
+ assert(foundClosingHeader && previousNonWSChar == '}');
+
+ if (currentHeader == &AS_WHILE && shouldAttachClosingWhile)
+ {
+ appendClosingHeader();
+ return;
+ }
+
+ if (braceFormatMode == BREAK_MODE
+ || braceFormatMode == RUN_IN_MODE
+ || attachClosingBraceMode)
+ {
+ isInLineBreak = true;
+ }
+ else if (braceFormatMode == NONE_MODE)
+ {
+ if (shouldBreakClosingHeaderBraces
+ || getBraceIndent() || getBlockIndent())
+ {
+ isInLineBreak = true;
+ }
+ else
+ {
+ appendSpacePad();
+ // is closing brace broken?
+ size_t i = currentLine.find_first_not_of(" \t");
+ if (i != string::npos && currentLine[i] == '}')
+ isInLineBreak = false;
+
+ if (shouldBreakBlocks)
+ isAppendPostBlockEmptyLineRequested = false;
+ }
+ }
+ // braceFormatMode == ATTACH_MODE, LINUX_MODE
+ else
+ {
+ if (shouldBreakClosingHeaderBraces
+ || getBraceIndent() || getBlockIndent())
+ {
+ isInLineBreak = true;
+ }
+ else
+ {
+ appendClosingHeader();
+ if (shouldBreakBlocks)
+ isAppendPostBlockEmptyLineRequested = false;
+ }
+ }
+}
+
+/**
+ * Append a closing header to the previous closing brace, if possible
+ */
+void ASFormatter::appendClosingHeader()
+{
+ // if a blank line does not precede this
+ // or last line is not a one line block, attach header
+ bool previousLineIsEmpty = isEmptyLine(formattedLine);
+ int previousLineIsOneLineBlock = 0;
+ size_t firstBrace = findNextChar(formattedLine, '{');
+ if (firstBrace != string::npos)
+ previousLineIsOneLineBlock = isOneLineBlockReached(formattedLine, firstBrace);
+ if (!previousLineIsEmpty
+ && previousLineIsOneLineBlock == 0)
+ {
+ isInLineBreak = false;
+ appendSpacePad();
+ spacePadNum = 0; // don't count as comment padding
+ }
+}
+
+/**
+ * Add braces to a single line statement following a header.
+ * braces are not added if the proper conditions are not met.
+ * braces are added to the currentLine.
+ */
+bool ASFormatter::addBracesToStatement()
+{
+ assert(isImmediatelyPostHeader);
+
+ if (currentHeader != &AS_IF
+ && currentHeader != &AS_ELSE
+ && currentHeader != &AS_FOR
+ && currentHeader != &AS_WHILE
+ && currentHeader != &AS_DO
+ && currentHeader != &AS_FOREACH
+ && currentHeader != &AS_QFOREACH
+ && currentHeader != &AS_QFOREVER
+ && currentHeader != &AS_FOREVER)
+ return false;
+
+ if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while
+ return false;
+
+ // do not brace an empty statement
+ if (currentChar == ';')
+ return false;
+
+ // do not add if a header follows
+ if (isCharPotentialHeader(currentLine, charNum))
+ if (findHeader(headers) != nullptr)
+ return false;
+
+ // find the next semi-colon
+ size_t nextSemiColon = charNum;
+ if (currentChar != ';')
+ nextSemiColon = findNextChar(currentLine, ';', charNum + 1);
+ if (nextSemiColon == string::npos)
+ return false;
+
+ // add closing brace before changing the line length
+ if (nextSemiColon == currentLine.length() - 1)
+ currentLine.append(" }");
+ else
+ currentLine.insert(nextSemiColon + 1, " }");
+ // add opening brace
+ currentLine.insert(charNum, "{ ");
+ assert(computeChecksumIn("{}"));
+ currentChar = '{';
+ if ((int) currentLine.find_first_not_of(" \t") == charNum)
+ currentLineBeginsWithBrace = true;
+ // remove extra spaces
+ if (!shouldAddOneLineBraces)
+ {
+ size_t lastText = formattedLine.find_last_not_of(" \t");
+ if ((formattedLine.length() - 1) - lastText > 1)
+ formattedLine.erase(lastText + 1);
+ }
+ return true;
+}
+
+/**
+ * Remove braces from a single line statement following a header.
+ * braces are not removed if the proper conditions are not met.
+ * The first brace is replaced by a space.
+ */
+bool ASFormatter::removeBracesFromStatement()
+{
+ assert(isImmediatelyPostHeader);
+ assert(currentChar == '{');
+
+ if (currentHeader != &AS_IF
+ && currentHeader != &AS_ELSE
+ && currentHeader != &AS_FOR
+ && currentHeader != &AS_WHILE
+ && currentHeader != &AS_FOREACH)
+ return false;
+
+ if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while
+ return false;
+
+ bool isFirstLine = true;
+ string nextLine_;
+ // leave nextLine_ empty if end of line comment follows
+ if (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace)
+ nextLine_ = currentLine.substr(charNum + 1);
+ size_t nextChar = 0;
+
+ // find the first non-blank text
+ ASPeekStream stream(sourceIterator);
+ while (stream.hasMoreLines() || isFirstLine)
+ {
+ if (isFirstLine)
+ isFirstLine = false;
+ else
+ {
+ nextLine_ = stream.peekNextLine();
+ nextChar = 0;
+ }
+
+ nextChar = nextLine_.find_first_not_of(" \t", nextChar);
+ if (nextChar != string::npos)
+ break;
+ }
+
+ // don't remove if comments or a header follow the brace
+ if ((nextLine_.compare(nextChar, 2, "/*") == 0)
+ || (nextLine_.compare(nextChar, 2, "//") == 0)
+ || (isCharPotentialHeader(nextLine_, nextChar)
+ && ASBase::findHeader(nextLine_, nextChar, headers) != nullptr))
+ return false;
+
+ // find the next semi-colon
+ size_t nextSemiColon = nextChar;
+ if (nextLine_[nextChar] != ';')
+ nextSemiColon = findNextChar(nextLine_, ';', nextChar + 1);
+ if (nextSemiColon == string::npos)
+ return false;
+
+ // find the closing brace
+ isFirstLine = true;
+ nextChar = nextSemiColon + 1;
+ while (stream.hasMoreLines() || isFirstLine)
+ {
+ if (isFirstLine)
+ isFirstLine = false;
+ else
+ {
+ nextLine_ = stream.peekNextLine();
+ nextChar = 0;
+ }
+ nextChar = nextLine_.find_first_not_of(" \t", nextChar);
+ if (nextChar != string::npos)
+ break;
+ }
+ if (nextLine_.length() == 0 || nextLine_[nextChar] != '}')
+ return false;
+
+ // remove opening brace
+ currentLine[charNum] = currentChar = ' ';
+ assert(adjustChecksumIn(-'{'));
+ return true;
+}
+
+/**
+ * Find the next character that is not in quotes or a comment.
+ *
+ * @param line the line to be searched.
+ * @param searchChar the char to find.
+ * @param searchStart the start position on the line (default is 0).
+ * @return the position on the line or string::npos if not found.
+ */
+size_t ASFormatter::findNextChar(const string& line, char searchChar, int searchStart /*0*/) const
+{
+ // find the next searchChar
+ size_t i;
+ for (i = searchStart; i < line.length(); i++)
+ {
+ if (line.compare(i, 2, "//") == 0)
+ return string::npos;
+ if (line.compare(i, 2, "/*") == 0)
+ {
+ size_t endComment = line.find("*/", i + 2);
+ if (endComment == string::npos)
+ return string::npos;
+ i = endComment + 2;
+ if (i >= line.length())
+ return string::npos;
+ }
+ if (line[i] == '"'
+ || (line[i] == '\'' && !isDigitSeparator(line, i)))
+ {
+ char quote = line[i];
+ while (i < line.length())
+ {
+ size_t endQuote = line.find(quote, i + 1);
+ if (endQuote == string::npos)
+ return string::npos;
+ i = endQuote;
+ if (line[endQuote - 1] != '\\') // check for '\"'
+ break;
+ if (line[endQuote - 2] == '\\') // check for '\\'
+ break;
+ }
+ }
+
+ if (line[i] == searchChar)
+ break;
+
+ // for now don't process C# 'delegate' braces
+ // do this last in case the search char is a '{'
+ if (line[i] == '{')
+ return string::npos;
+ }
+ if (i >= line.length()) // didn't find searchChar
+ return string::npos;
+
+ return i;
+}
+
+/**
+ * Look ahead in the file to see if a struct has access modifiers.
+ *
+ * @param firstLine a reference to the line to indent.
+ * @param index the current line index.
+ * @return true if the struct has access modifiers.
+ */
+bool ASFormatter::isStructAccessModified(const string& firstLine, size_t index) const
+{
+ assert(firstLine[index] == '{');
+ assert(isCStyle());
+
+ bool isFirstLine = true;
+ size_t braceCount = 1;
+ string nextLine_ = firstLine.substr(index + 1);
+ ASPeekStream stream(sourceIterator);
+
+ // find the first non-blank text, bypassing all comments and quotes.
+ bool isInComment_ = false;
+ bool isInQuote_ = false;
+ char quoteChar_ = ' ';
+ while (stream.hasMoreLines() || isFirstLine)
+ {
+ if (isFirstLine)
+ isFirstLine = false;
+ else
+ nextLine_ = stream.peekNextLine();
+ // parse the line
+ for (size_t i = 0; i < nextLine_.length(); i++)
+ {
+ if (isWhiteSpace(nextLine_[i]))
+ continue;
+ if (nextLine_.compare(i, 2, "/*") == 0)
+ isInComment_ = true;
+ if (isInComment_)
+ {
+ if (nextLine_.compare(i, 2, "*/") == 0)
+ {
+ isInComment_ = false;
+ ++i;
+ }
+ continue;
+ }
+ if (nextLine_[i] == '\\')
+ {
+ ++i;
+ continue;
+ }
+
+ if (isInQuote_)
+ {
+ if (nextLine_[i] == quoteChar_)
+ isInQuote_ = false;
+ continue;
+ }
+
+ if (nextLine_[i] == '"'
+ || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
+ {
+ isInQuote_ = true;
+ quoteChar_ = nextLine_[i];
+ continue;
+ }
+ if (nextLine_.compare(i, 2, "//") == 0)
+ {
+ i = nextLine_.length();
+ continue;
+ }
+ // handle braces
+ if (nextLine_[i] == '{')
+ ++braceCount;
+ if (nextLine_[i] == '}')
+ --braceCount;
+ if (braceCount == 0)
+ return false;
+ // check for access modifiers
+ if (isCharPotentialHeader(nextLine_, i))
+ {
+ if (findKeyword(nextLine_, i, AS_PUBLIC)
+ || findKeyword(nextLine_, i, AS_PRIVATE)
+ || findKeyword(nextLine_, i, AS_PROTECTED))
+ return true;
+ string name = getCurrentWord(nextLine_, i);
+ i += name.length() - 1;
+ }
+ } // end of for loop
+ } // end of while loop
+
+ return false;
+}
+
+/**
+* Look ahead in the file to see if a preprocessor block is indentable.
+*
+* @param firstLine a reference to the line to indent.
+* @param index the current line index.
+* @return true if the block is indentable.
+*/
+bool ASFormatter::isIndentablePreprocessorBlock(const string& firstLine, size_t index)
+{
+ assert(firstLine[index] == '#');
+
+ bool isFirstLine = true;
+ bool isInIndentableBlock = false;
+ bool blockContainsBraces = false;
+ bool blockContainsDefineContinuation = false;
+ bool isInClassConstructor = false;
+ bool isPotentialHeaderGuard = false; // ifndef is first preproc statement
+ bool isPotentialHeaderGuard2 = false; // define is within the first proproc
+ int numBlockIndents = 0;
+ int lineParenCount = 0;
+ string nextLine_ = firstLine.substr(index);
+ auto stream = make_shared<ASPeekStream>(sourceIterator);
+
+ // find end of the block, bypassing all comments and quotes.
+ bool isInComment_ = false;
+ bool isInQuote_ = false;
+ char quoteChar_ = ' ';
+ while (stream->hasMoreLines() || isFirstLine)
+ {
+ if (isFirstLine)
+ isFirstLine = false;
+ else
+ nextLine_ = stream->peekNextLine();
+ // parse the line
+ for (size_t i = 0; i < nextLine_.length(); i++)
+ {
+ if (isWhiteSpace(nextLine_[i]))
+ continue;
+ if (nextLine_.compare(i, 2, "/*") == 0)
+ isInComment_ = true;
+ if (isInComment_)
+ {
+ if (nextLine_.compare(i, 2, "*/") == 0)
+ {
+ isInComment_ = false;
+ ++i;
+ }
+ continue;
+ }
+ if (nextLine_[i] == '\\')
+ {
+ ++i;
+ continue;
+ }
+ if (isInQuote_)
+ {
+ if (nextLine_[i] == quoteChar_)
+ isInQuote_ = false;
+ continue;
+ }
+
+ if (nextLine_[i] == '"'
+ || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
+ {
+ isInQuote_ = true;
+ quoteChar_ = nextLine_[i];
+ continue;
+ }
+ if (nextLine_.compare(i, 2, "//") == 0)
+ {
+ i = nextLine_.length();
+ continue;
+ }
+ // handle preprocessor statement
+ if (nextLine_[i] == '#')
+ {
+ string preproc = ASBeautifier::extractPreprocessorStatement(nextLine_);
+ if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef
+ {
+ numBlockIndents += 1;
+ isInIndentableBlock = true;
+ // flag first preprocessor conditional for header include guard check
+ if (!processedFirstConditional)
+ {
+ processedFirstConditional = true;
+ isFirstPreprocConditional = true;
+ if (isNDefPreprocStatement(nextLine_, preproc))
+ isPotentialHeaderGuard = true;
+ }
+ }
+ else if (preproc == "endif")
+ {
+ if (numBlockIndents > 0)
+ numBlockIndents -= 1;
+ // must exit BOTH loops
+ if (numBlockIndents == 0)
+ goto EndOfWhileLoop;
+ }
+ else if (preproc == "define")
+ {
+ if (nextLine_[nextLine_.length() - 1] == '\\')
+ blockContainsDefineContinuation = true;
+ // check for potential header include guards
+ else if (isPotentialHeaderGuard && numBlockIndents == 1)
+ isPotentialHeaderGuard2 = true;
+ }
+ i = nextLine_.length();
+ continue;
+ }
+ // handle exceptions
+ if (nextLine_[i] == '{' || nextLine_[i] == '}')
+ blockContainsBraces = true;
+ else if (nextLine_[i] == '(')
+ ++lineParenCount;
+ else if (nextLine_[i] == ')')
+ --lineParenCount;
+ else if (nextLine_[i] == ':')
+ {
+ // check for '::'
+ if (nextLine_.length() > i && nextLine_[i + 1] == ':')
+ ++i;
+ else
+ isInClassConstructor = true;
+ }
+ // bypass unnecessary parsing - must exit BOTH loops
+ if (blockContainsBraces || isInClassConstructor || blockContainsDefineContinuation)
+ goto EndOfWhileLoop;
+ } // end of for loop, end of line
+ if (lineParenCount != 0)
+ break;
+ } // end of while loop
+EndOfWhileLoop:
+ preprocBlockEnd = sourceIterator->tellg();
+ if (preprocBlockEnd < 0)
+ preprocBlockEnd = sourceIterator->getStreamLength();
+ if (blockContainsBraces
+ || isInClassConstructor
+ || blockContainsDefineContinuation
+ || lineParenCount != 0
+ || numBlockIndents != 0)
+ isInIndentableBlock = false;
+ // find next executable instruction
+ // this WILL RESET the get pointer
+ string nextText = peekNextText("", false, stream);
+ // bypass header include guards
+ if (isFirstPreprocConditional)
+ {
+ isFirstPreprocConditional = false;
+ if (nextText.empty() && isPotentialHeaderGuard2)
+ {
+ isInIndentableBlock = false;
+ preprocBlockEnd = 0;
+ }
+ }
+ // this allows preprocessor blocks within this block to be indented
+ if (!isInIndentableBlock)
+ preprocBlockEnd = 0;
+ // peekReset() is done by previous peekNextText()
+ return isInIndentableBlock;
+}
+
+bool ASFormatter::isNDefPreprocStatement(const string& nextLine_, const string& preproc) const
+{
+ if (preproc == "ifndef")
+ return true;
+ // check for '!defined'
+ if (preproc == "if")
+ {
+ size_t i = nextLine_.find('!');
+ if (i == string::npos)
+ return false;
+ i = nextLine_.find_first_not_of(" \t", ++i);
+ if (i != string::npos && nextLine_.compare(i, 7, "defined") == 0)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check to see if this is an EXEC SQL statement.
+ *
+ * @param line a reference to the line to indent.
+ * @param index the current line index.
+ * @return true if the statement is EXEC SQL.
+ */
+bool ASFormatter::isExecSQL(const string& line, size_t index) const
+{
+ if (line[index] != 'e' && line[index] != 'E') // quick check to reject most
+ return false;
+ string word;
+ if (isCharPotentialHeader(line, index))
+ word = getCurrentWord(line, index);
+ for (size_t i = 0; i < word.length(); i++)
+ word[i] = (char) toupper(word[i]);
+ if (word != "EXEC")
+ return false;
+ size_t index2 = index + word.length();
+ index2 = line.find_first_not_of(" \t", index2);
+ if (index2 == string::npos)
+ return false;
+ word.erase();
+ if (isCharPotentialHeader(line, index2))
+ word = getCurrentWord(line, index2);
+ for (size_t i = 0; i < word.length(); i++)
+ word[i] = (char) toupper(word[i]);
+ if (word != "SQL")
+ return false;
+ return true;
+}
+
+/**
+ * The continuation lines must be adjusted so the leading spaces
+ * is equivalent to the text on the opening line.
+ *
+ * Updates currentLine and charNum.
+ */
+void ASFormatter::trimContinuationLine()
+{
+ size_t len = currentLine.length();
+ size_t tabSize = getTabLength();
+ charNum = 0;
+
+ if (leadingSpaces > 0 && len > 0)
+ {
+ size_t i;
+ size_t continuationIncrementIn = 0;
+ for (i = 0; (i < len) && (i + continuationIncrementIn < leadingSpaces); i++)
+ {
+ if (!isWhiteSpace(currentLine[i])) // don't delete any text
+ {
+ if (i < continuationIncrementIn)
+ leadingSpaces = i + tabIncrementIn;
+ continuationIncrementIn = tabIncrementIn;
+ break;
+ }
+ if (currentLine[i] == '\t')
+ continuationIncrementIn += tabSize - 1 - ((continuationIncrementIn + i) % tabSize);
+ }
+
+ if ((int) continuationIncrementIn == tabIncrementIn)
+ charNum = i;
+ else
+ {
+ // build a new line with the equivalent leading chars
+ string newLine;
+ int leadingChars = 0;
+ if ((int) leadingSpaces > tabIncrementIn)
+ leadingChars = leadingSpaces - tabIncrementIn;
+ newLine.append(leadingChars, ' ');
+ newLine.append(currentLine, i, len - i);
+ currentLine = newLine;
+ charNum = leadingChars;
+ if (currentLine.length() == 0)
+ currentLine = string(" "); // a null is inserted if this is not done
+ }
+ if (i >= len)
+ charNum = 0;
+ }
+}
+
+/**
+ * Determine if a header is a closing header
+ *
+ * @return true if the header is a closing header.
+ */
+bool ASFormatter::isClosingHeader(const string* header) const
+{
+ return (header == &AS_ELSE
+ || header == &AS_CATCH
+ || header == &AS_FINALLY);
+}
+
+/**
+ * Determine if a * following a closing paren is immediately.
+ * after a cast. If so it is a deference and not a multiply.
+ * e.g. "(int*) *ptr" is a deference.
+ */
+bool ASFormatter::isImmediatelyPostCast() const
+{
+ assert(previousNonWSChar == ')' && currentChar == '*');
+ // find preceding closing paren on currentLine or readyFormattedLine
+ string line; // currentLine or readyFormattedLine
+ size_t paren = currentLine.rfind(')', charNum);
+ if (paren != string::npos)
+ line = currentLine;
+ // if not on currentLine it must be on the previous line
+ else
+ {
+ line = readyFormattedLine;
+ paren = line.rfind(')');
+ if (paren == string::npos)
+ return false;
+ }
+ if (paren == 0)
+ return false;
+
+ // find character preceding the closing paren
+ size_t lastChar = line.find_last_not_of(" \t", paren - 1);
+ if (lastChar == string::npos)
+ return false;
+ // check for pointer cast
+ if (line[lastChar] == '*')
+ return true;
+ return false;
+}
+
+/**
+ * Determine if a < is a template definition or instantiation.
+ * Sets the class variables isInTemplate and templateDepth.
+ */
+void ASFormatter::checkIfTemplateOpener()
+{
+ assert(!isInTemplate && currentChar == '<');
+
+ // find first char after the '<' operators
+ size_t firstChar = currentLine.find_first_not_of("< \t", charNum);
+ if (firstChar == string::npos
+ || currentLine[firstChar] == '=')
+ {
+ // this is not a template -> leave...
+ isInTemplate = false;
+ return;
+ }
+
+ bool isFirstLine = true;
+ int parenDepth_ = 0;
+ int maxTemplateDepth = 0;
+ templateDepth = 0;
+ string nextLine_ = currentLine.substr(charNum);
+ ASPeekStream stream(sourceIterator);
+
+ // find the angle braces, bypassing all comments and quotes.
+ bool isInComment_ = false;
+ bool isInQuote_ = false;
+ char quoteChar_ = ' ';
+ while (stream.hasMoreLines() || isFirstLine)
+ {
+ if (isFirstLine)
+ isFirstLine = false;
+ else
+ nextLine_ = stream.peekNextLine();
+ // parse the line
+ for (size_t i = 0; i < nextLine_.length(); i++)
+ {
+ char currentChar_ = nextLine_[i];
+ if (isWhiteSpace(currentChar_))
+ continue;
+ if (nextLine_.compare(i, 2, "/*") == 0)
+ isInComment_ = true;
+ if (isInComment_)
+ {
+ if (nextLine_.compare(i, 2, "*/") == 0)
+ {
+ isInComment_ = false;
+ ++i;
+ }
+ continue;
+ }
+ if (currentChar_ == '\\')
+ {
+ ++i;
+ continue;
+ }
+
+ if (isInQuote_)
+ {
+ if (currentChar_ == quoteChar_)
+ isInQuote_ = false;
+ continue;
+ }
+
+ if (currentChar_ == '"'
+ || (currentChar_ == '\'' && !isDigitSeparator(nextLine_, i)))
+ {
+ isInQuote_ = true;
+ quoteChar_ = currentChar_;
+ continue;
+ }
+ if (nextLine_.compare(i, 2, "//") == 0)
+ {
+ i = nextLine_.length();
+ continue;
+ }
+
+ // not in a comment or quote
+ if (currentChar_ == '<')
+ {
+ ++templateDepth;
+ ++maxTemplateDepth;
+ continue;
+ }
+ else if (currentChar_ == '>')
+ {
+ --templateDepth;
+ if (templateDepth == 0)
+ {
+ if (parenDepth_ == 0)
+ {
+ // this is a template!
+ isInTemplate = true;
+ templateDepth = maxTemplateDepth;
+ }
+ return;
+ }
+ continue;
+ }
+ else if (currentChar_ == '(' || currentChar_ == ')')
+ {
+ if (currentChar_ == '(')
+ ++parenDepth_;
+ else
+ --parenDepth_;
+ if (parenDepth_ >= 0)
+ continue;
+ // this is not a template -> leave...
+ isInTemplate = false;
+ templateDepth = 0;
+ return;
+ }
+ else if (nextLine_.compare(i, 2, AS_AND) == 0
+ || nextLine_.compare(i, 2, AS_OR) == 0)
+ {
+ // this is not a template -> leave...
+ isInTemplate = false;
+ templateDepth = 0;
+ return;
+ }
+ else if (currentChar_ == ',' // comma, e.g. A<int, char>
+ || currentChar_ == '&' // reference, e.g. A<int&>
+ || currentChar_ == '*' // pointer, e.g. A<int*>
+ || currentChar_ == '^' // C++/CLI managed pointer, e.g. A<int^>
+ || currentChar_ == ':' // ::, e.g. std::string
+ || currentChar_ == '=' // assign e.g. default parameter
+ || currentChar_ == '[' // [] e.g. string[]
+ || currentChar_ == ']' // [] e.g. string[]
+ || currentChar_ == '(' // (...) e.g. function definition
+ || currentChar_ == ')' // (...) e.g. function definition
+ || (isJavaStyle() && currentChar_ == '?') // Java wildcard
+ )
+ {
+ continue;
+ }
+ else if (!isLegalNameChar(currentChar_))
+ {
+ // this is not a template -> leave...
+ isInTemplate = false;
+ templateDepth = 0;
+ return;
+ }
+ string name = getCurrentWord(nextLine_, i);
+ i += name.length() - 1;
+ } // end for loop
+ } // end while loop
+}
+
+void ASFormatter::updateFormattedLineSplitPoints(char appendedChar)
+{
+ assert(maxCodeLength != string::npos);
+ assert(formattedLine.length() > 0);
+
+ if (!isOkToSplitFormattedLine())
+ return;
+
+ char nextChar = peekNextChar();
+
+ // don't split before an end of line comment
+ if (nextChar == '/')
+ return;
+
+ // don't split before or after a brace
+ if (appendedChar == '{' || appendedChar == '}'
+ || previousNonWSChar == '{' || previousNonWSChar == '}'
+ || nextChar == '{' || nextChar == '}'
+ || currentChar == '{' || currentChar == '}') // currentChar tests for an appended brace
+ return;
+
+ // don't split before or after a block paren
+ if (appendedChar == '[' || appendedChar == ']'
+ || previousNonWSChar == '['
+ || nextChar == '[' || nextChar == ']')
+ return;
+
+ if (isWhiteSpace(appendedChar))
+ {
+ if (nextChar != ')' // space before a closing paren
+ && nextChar != '(' // space before an opening paren
+ && nextChar != '/' // space before a comment
+ && nextChar != ':' // space before a colon
+ && currentChar != ')' // appended space before and after a closing paren
+ && currentChar != '(' // appended space before and after a opening paren
+ && previousNonWSChar != '(' // decided at the '('
+ // don't break before a pointer or reference aligned to type
+ && !(nextChar == '*'
+ && !isCharPotentialOperator(previousNonWSChar)
+ && pointerAlignment == PTR_ALIGN_TYPE)
+ && !(nextChar == '&'
+ && !isCharPotentialOperator(previousNonWSChar)
+ && (referenceAlignment == REF_ALIGN_TYPE
+ || (referenceAlignment == REF_SAME_AS_PTR && pointerAlignment == PTR_ALIGN_TYPE)))
+ )
+ {
+ if (formattedLine.length() - 1 <= maxCodeLength)
+ maxWhiteSpace = formattedLine.length() - 1;
+ else
+ maxWhiteSpacePending = formattedLine.length() - 1;
+ }
+ }
+ // unpadded closing parens may split after the paren (counts as whitespace)
+ else if (appendedChar == ')')
+ {
+ if (nextChar != ')'
+ && nextChar != ' '
+ && nextChar != ';'
+ && nextChar != ','
+ && nextChar != '.'
+ && !(nextChar == '-' && pointerSymbolFollows())) // check for ->
+ {
+ if (formattedLine.length() <= maxCodeLength)
+ maxWhiteSpace = formattedLine.length();
+ else
+ maxWhiteSpacePending = formattedLine.length();
+ }
+ }
+ // unpadded commas may split after the comma
+ else if (appendedChar == ',')
+ {
+ if (formattedLine.length() <= maxCodeLength)
+ maxComma = formattedLine.length();
+ else
+ maxCommaPending = formattedLine.length();
+ }
+ else if (appendedChar == '(')
+ {
+ if (nextChar != ')' && nextChar != '(' && nextChar != '"' && nextChar != '\'')
+ {
+ // if follows an operator break before
+ size_t parenNum;
+ if (isCharPotentialOperator(previousNonWSChar))
+ parenNum = formattedLine.length() - 1;
+ else
+ parenNum = formattedLine.length();
+ if (formattedLine.length() <= maxCodeLength)
+ maxParen = parenNum;
+ else
+ maxParenPending = parenNum;
+ }
+ }
+ else if (appendedChar == ';')
+ {
+ if (nextChar != ' ' && nextChar != '}' && nextChar != '/') // check for following comment
+ {
+ if (formattedLine.length() <= maxCodeLength)
+ maxSemi = formattedLine.length();
+ else
+ maxSemiPending = formattedLine.length();
+ }
+ }
+}
+
+void ASFormatter::updateFormattedLineSplitPointsOperator(const string& sequence)
+{
+ assert(maxCodeLength != string::npos);
+ assert(formattedLine.length() > 0);
+
+ if (!isOkToSplitFormattedLine())
+ return;
+
+ char nextChar = peekNextChar();
+
+ // don't split before an end of line comment
+ if (nextChar == '/')
+ return;
+
+ // check for logical conditional
+ if (sequence == "||" || sequence == "&&" || sequence == "or" || sequence == "and")
+ {
+ if (shouldBreakLineAfterLogical)
+ {
+ if (formattedLine.length() <= maxCodeLength)
+ maxAndOr = formattedLine.length();
+ else
+ maxAndOrPending = formattedLine.length();
+ }
+ else
+ {
+ // adjust for leading space in the sequence
+ size_t sequenceLength = sequence.length();
+ if (formattedLine.length() > sequenceLength
+ && isWhiteSpace(formattedLine[formattedLine.length() - sequenceLength - 1]))
+ sequenceLength++;
+ if (formattedLine.length() - sequenceLength <= maxCodeLength)
+ maxAndOr = formattedLine.length() - sequenceLength;
+ else
+ maxAndOrPending = formattedLine.length() - sequenceLength;
+ }
+ }
+ // comparison operators will split after the operator (counts as whitespace)
+ else if (sequence == "==" || sequence == "!=" || sequence == ">=" || sequence == "<=")
+ {
+ if (formattedLine.length() <= maxCodeLength)
+ maxWhiteSpace = formattedLine.length();
+ else
+ maxWhiteSpacePending = formattedLine.length();
+ }
+ // unpadded operators that will split BEFORE the operator (counts as whitespace)
+ else if (sequence == "+" || sequence == "-" || sequence == "?")
+ {
+ if (charNum > 0
+ && !(sequence == "+" && isInExponent())
+ && !(sequence == "-" && isInExponent())
+ && (isLegalNameChar(currentLine[charNum - 1])
+ || currentLine[charNum - 1] == ')'
+ || currentLine[charNum - 1] == ']'
+ || currentLine[charNum - 1] == '\"'))
+ {
+ if (formattedLine.length() - 1 <= maxCodeLength)
+ maxWhiteSpace = formattedLine.length() - 1;
+ else
+ maxWhiteSpacePending = formattedLine.length() - 1;
+ }
+ }
+ // unpadded operators that will USUALLY split AFTER the operator (counts as whitespace)
+ else if (sequence == "=" || sequence == ":")
+ {
+ // split BEFORE if the line is too long
+ // do NOT use <= here, must allow for a brace attached to an array
+ size_t splitPoint = 0;
+ if (formattedLine.length() < maxCodeLength)
+ splitPoint = formattedLine.length();
+ else
+ splitPoint = formattedLine.length() - 1;
+ // padded or unpadded arrays
+ if (previousNonWSChar == ']')
+ {
+ if (formattedLine.length() - 1 <= maxCodeLength)
+ maxWhiteSpace = splitPoint;
+ else
+ maxWhiteSpacePending = splitPoint;
+ }
+ else if (charNum > 0
+ && (isLegalNameChar(currentLine[charNum - 1])
+ || currentLine[charNum - 1] == ')'
+ || currentLine[charNum - 1] == ']'))
+ {
+ if (formattedLine.length() <= maxCodeLength)
+ maxWhiteSpace = splitPoint;
+ else
+ maxWhiteSpacePending = splitPoint;
+ }
+ }
+}
+
+/**
+ * Update the split point when a pointer or reference is formatted.
+ * The argument is the maximum index of the last whitespace character.
+ */
+void ASFormatter::updateFormattedLineSplitPointsPointerOrReference(size_t index)
+{
+ assert(maxCodeLength != string::npos);
+ assert(formattedLine.length() > 0);
+ assert(index < formattedLine.length());
+
+ if (!isOkToSplitFormattedLine())
+ return;
+
+ if (index < maxWhiteSpace) // just in case
+ return;
+
+ if (index <= maxCodeLength)
+ maxWhiteSpace = index;
+ else
+ maxWhiteSpacePending = index;
+}
+
+bool ASFormatter::isOkToSplitFormattedLine()
+{
+ assert(maxCodeLength != string::npos);
+ // Is it OK to split the line?
+ if (shouldKeepLineUnbroken
+ || isInLineComment
+ || isInComment
+ || isInQuote
+ || isInCase
+ || isInPreprocessor
+ || isInExecSQL
+ || isInAsm || isInAsmOneLine || isInAsmBlock
+ || isInTemplate)
+ return false;
+
+ if (!isOkToBreakBlock(braceTypeStack->back()) && currentChar != '{')
+ {
+ shouldKeepLineUnbroken = true;
+ clearFormattedLineSplitPoints();
+ return false;
+ }
+ if (isBraceType(braceTypeStack->back(), ARRAY_TYPE))
+ {
+ shouldKeepLineUnbroken = true;
+ if (!isBraceType(braceTypeStack->back(), ARRAY_NIS_TYPE))
+ clearFormattedLineSplitPoints();
+ return false;
+ }
+ return true;
+}
+
+/* This is called if the option maxCodeLength is set.
+ */
+void ASFormatter::testForTimeToSplitFormattedLine()
+{
+ // DO NOT ASSERT maxCodeLength HERE
+ // should the line be split
+ if (formattedLine.length() > maxCodeLength && !isLineReady)
+ {
+ size_t splitPoint = findFormattedLineSplitPoint();
+ if (splitPoint > 0 && splitPoint < formattedLine.length())
+ {
+ string splitLine = formattedLine.substr(splitPoint);
+ formattedLine = formattedLine.substr(0, splitPoint);
+ breakLine(true);
+ formattedLine = splitLine;
+ // if break-blocks is requested and this is a one-line statement
+ string nextWord = ASBeautifier::getNextWord(currentLine, charNum - 1);
+ if (isAppendPostBlockEmptyLineRequested
+ && (nextWord == "break" || nextWord == "continue"))
+ {
+ isAppendPostBlockEmptyLineRequested = false;
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+ else
+ isPrependPostBlockEmptyLineRequested = false;
+ // adjust max split points
+ maxAndOr = (maxAndOr > splitPoint) ? (maxAndOr - splitPoint) : 0;
+ maxSemi = (maxSemi > splitPoint) ? (maxSemi - splitPoint) : 0;
+ maxComma = (maxComma > splitPoint) ? (maxComma - splitPoint) : 0;
+ maxParen = (maxParen > splitPoint) ? (maxParen - splitPoint) : 0;
+ maxWhiteSpace = (maxWhiteSpace > splitPoint) ? (maxWhiteSpace - splitPoint) : 0;
+ if (maxSemiPending > 0)
+ {
+ maxSemi = (maxSemiPending > splitPoint) ? (maxSemiPending - splitPoint) : 0;
+ maxSemiPending = 0;
+ }
+ if (maxAndOrPending > 0)
+ {
+ maxAndOr = (maxAndOrPending > splitPoint) ? (maxAndOrPending - splitPoint) : 0;
+ maxAndOrPending = 0;
+ }
+ if (maxCommaPending > 0)
+ {
+ maxComma = (maxCommaPending > splitPoint) ? (maxCommaPending - splitPoint) : 0;
+ maxCommaPending = 0;
+ }
+ if (maxParenPending > 0)
+ {
+ maxParen = (maxParenPending > splitPoint) ? (maxParenPending - splitPoint) : 0;
+ maxParenPending = 0;
+ }
+ if (maxWhiteSpacePending > 0)
+ {
+ maxWhiteSpace = (maxWhiteSpacePending > splitPoint) ? (maxWhiteSpacePending - splitPoint) : 0;
+ maxWhiteSpacePending = 0;
+ }
+ // don't allow an empty formatted line
+ size_t firstText = formattedLine.find_first_not_of(" \t");
+ if (firstText == string::npos && formattedLine.length() > 0)
+ {
+ formattedLine.erase();
+ clearFormattedLineSplitPoints();
+ if (isWhiteSpace(currentChar))
+ for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
+ goForward(1);
+ }
+ else if (firstText > 0)
+ {
+ formattedLine.erase(0, firstText);
+ maxSemi = (maxSemi > firstText) ? (maxSemi - firstText) : 0;
+ maxAndOr = (maxAndOr > firstText) ? (maxAndOr - firstText) : 0;
+ maxComma = (maxComma > firstText) ? (maxComma - firstText) : 0;
+ maxParen = (maxParen > firstText) ? (maxParen - firstText) : 0;
+ maxWhiteSpace = (maxWhiteSpace > firstText) ? (maxWhiteSpace - firstText) : 0;
+ }
+ // reset formattedLineCommentNum
+ if (formattedLineCommentNum != string::npos)
+ {
+ formattedLineCommentNum = formattedLine.find("//");
+ if (formattedLineCommentNum == string::npos)
+ formattedLineCommentNum = formattedLine.find("/*");
+ }
+ }
+ }
+}
+
+size_t ASFormatter::findFormattedLineSplitPoint() const
+{
+ assert(maxCodeLength != string::npos);
+ // determine where to split
+ size_t minCodeLength = 10;
+ size_t splitPoint = 0;
+ splitPoint = maxSemi;
+ if (maxAndOr >= minCodeLength)
+ splitPoint = maxAndOr;
+ if (splitPoint < minCodeLength)
+ {
+ splitPoint = maxWhiteSpace;
+ // use maxParen instead if it is long enough
+ if (maxParen > splitPoint
+ || maxParen >= maxCodeLength * .7)
+ splitPoint = maxParen;
+ // use maxComma instead if it is long enough
+ // increasing the multiplier causes more splits at whitespace
+ if (maxComma > splitPoint
+ || maxComma >= maxCodeLength * .3)
+ splitPoint = maxComma;
+ }
+ // replace split point with first available break point
+ if (splitPoint < minCodeLength)
+ {
+ splitPoint = string::npos;
+ if (maxSemiPending > 0 && maxSemiPending < splitPoint)
+ splitPoint = maxSemiPending;
+ if (maxAndOrPending > 0 && maxAndOrPending < splitPoint)
+ splitPoint = maxAndOrPending;
+ if (maxCommaPending > 0 && maxCommaPending < splitPoint)
+ splitPoint = maxCommaPending;
+ if (maxParenPending > 0 && maxParenPending < splitPoint)
+ splitPoint = maxParenPending;
+ if (maxWhiteSpacePending > 0 && maxWhiteSpacePending < splitPoint)
+ splitPoint = maxWhiteSpacePending;
+ if (splitPoint == string::npos)
+ splitPoint = 0;
+ }
+ // if remaining line after split is too long
+ else if (formattedLine.length() - splitPoint > maxCodeLength)
+ {
+ // if end of the currentLine, find a new split point
+ size_t newCharNum;
+ if (isCharPotentialHeader(currentLine, charNum))
+ newCharNum = getCurrentWord(currentLine, charNum).length() + charNum;
+ else
+ newCharNum = charNum + 2;
+ if (newCharNum + 1 > currentLine.length())
+ {
+ // don't move splitPoint from before a conditional to after
+ if (maxWhiteSpace > splitPoint + 3)
+ splitPoint = maxWhiteSpace;
+ if (maxParen > splitPoint)
+ splitPoint = maxParen;
+ }
+ }
+
+ return splitPoint;
+}
+
+void ASFormatter::clearFormattedLineSplitPoints()
+{
+ maxSemi = 0;
+ maxAndOr = 0;
+ maxComma = 0;
+ maxParen = 0;
+ maxWhiteSpace = 0;
+ maxSemiPending = 0;
+ maxAndOrPending = 0;
+ maxCommaPending = 0;
+ maxParenPending = 0;
+ maxWhiteSpacePending = 0;
+}
+
+/**
+ * Check if a pointer symbol (->) follows on the currentLine.
+ */
+bool ASFormatter::pointerSymbolFollows() const
+{
+ size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (peekNum == string::npos || currentLine.compare(peekNum, 2, "->") != 0)
+ return false;
+ return true;
+}
+
+/**
+ * Compute the input checksum.
+ * This is called as an assert so it for is debug config only
+ */
+bool ASFormatter::computeChecksumIn(const string& currentLine_)
+{
+ for (size_t i = 0; i < currentLine_.length(); i++)
+ if (!isWhiteSpace(currentLine_[i]))
+ checksumIn += currentLine_[i];
+ return true;
+}
+
+/**
+ * Adjust the input checksum for deleted chars.
+ * This is called as an assert so it for is debug config only
+ */
+bool ASFormatter::adjustChecksumIn(int adjustment)
+{
+ checksumIn += adjustment;
+ return true;
+}
+
+/**
+ * get the value of checksumIn for unit testing
+ *
+ * @return checksumIn.
+ */
+size_t ASFormatter::getChecksumIn() const
+{
+ return checksumIn;
+}
+
+/**
+ * Compute the output checksum.
+ * This is called as an assert so it is for debug config only
+ */
+bool ASFormatter::computeChecksumOut(const string& beautifiedLine)
+{
+ for (size_t i = 0; i < beautifiedLine.length(); i++)
+ if (!isWhiteSpace(beautifiedLine[i]))
+ checksumOut += beautifiedLine[i];
+ return true;
+}
+
+/**
+ * Return isLineReady for the final check at end of file.
+ */
+bool ASFormatter::getIsLineReady() const
+{
+ return isLineReady;
+}
+
+/**
+ * get the value of checksumOut for unit testing
+ *
+ * @return checksumOut.
+ */
+size_t ASFormatter::getChecksumOut() const
+{
+ return checksumOut;
+}
+
+/**
+ * Return the difference in checksums.
+ * If zero all is okay.
+ */
+int ASFormatter::getChecksumDiff() const
+{
+ return checksumOut - checksumIn;
+}
+
+// for unit testing
+int ASFormatter::getFormatterFileType() const
+{
+ return formatterFileType;
+}
+
+// Check if an operator follows the next word.
+// The next word must be a legal name.
+const string* ASFormatter::getFollowingOperator() const
+{
+ // find next word
+ size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextNum == string::npos)
+ return nullptr;
+
+ if (!isLegalNameChar(currentLine[nextNum]))
+ return nullptr;
+
+ // bypass next word and following spaces
+ while (nextNum < currentLine.length())
+ {
+ if (!isLegalNameChar(currentLine[nextNum])
+ && !isWhiteSpace(currentLine[nextNum]))
+ break;
+ nextNum++;
+ }
+
+ if (nextNum >= currentLine.length()
+ || !isCharPotentialOperator(currentLine[nextNum])
+ || currentLine[nextNum] == '/') // comment
+ return nullptr;
+
+ const string* newOperator = ASBase::findOperator(currentLine, nextNum, operators);
+ return newOperator;
+}
+
+// Check following data to determine if the current character is an array operator.
+bool ASFormatter::isArrayOperator() const
+{
+ assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
+ assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE));
+
+ // find next word
+ size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextNum == string::npos)
+ return false;
+
+ if (!isLegalNameChar(currentLine[nextNum]))
+ return false;
+
+ // bypass next word and following spaces
+ while (nextNum < currentLine.length())
+ {
+ if (!isLegalNameChar(currentLine[nextNum])
+ && !isWhiteSpace(currentLine[nextNum]))
+ break;
+ nextNum++;
+ }
+
+ // check for characters that indicate an operator
+ if (currentLine[nextNum] == ','
+ || currentLine[nextNum] == '}'
+ || currentLine[nextNum] == ')'
+ || currentLine[nextNum] == '(')
+ return true;
+ return false;
+}
+
+// Reset the flags that indicate various statement information.
+void ASFormatter::resetEndOfStatement()
+{
+ foundQuestionMark = false;
+ foundNamespaceHeader = false;
+ foundClassHeader = false;
+ foundStructHeader = false;
+ foundInterfaceHeader = false;
+ foundPreDefinitionHeader = false;
+ foundPreCommandHeader = false;
+ foundPreCommandMacro = false;
+ foundTrailingReturnType = false;
+ foundCastOperator = false;
+ isInPotentialCalculation = false;
+ isSharpAccessor = false;
+ isSharpDelegate = false;
+ isInObjCMethodDefinition = false;
+ isInObjCInterface = false;
+ isInObjCSelector = false;
+ isInEnum = false;
+ isInExternC = false;
+ elseHeaderFollowsComments = false;
+ nonInStatementBrace = 0;
+ while (!questionMarkStack->empty())
+ questionMarkStack->pop_back();
+}
+
+// Find the colon alignment for Objective-C method definitions and method calls.
+int ASFormatter::findObjCColonAlignment() const
+{
+ assert(currentChar == '+' || currentChar == '-' || currentChar == '[');
+ assert(getAlignMethodColon());
+
+ bool isFirstLine = true;
+ bool haveFirstColon = false;
+ bool foundMethodColon = false;
+ bool isInComment_ = false;
+ bool isInQuote_ = false;
+ char quoteChar_ = ' ';
+ int sqBracketCount = 0;
+ int colonAdjust = 0;
+ int colonAlign = 0;
+ string nextLine_ = currentLine;
+ ASPeekStream stream(sourceIterator);
+
+ // peek next line
+ while (sourceIterator->hasMoreLines() || isFirstLine)
+ {
+ if (!isFirstLine)
+ nextLine_ = stream.peekNextLine();
+ // parse the line
+ haveFirstColon = false;
+ nextLine_ = ASBeautifier::trim(nextLine_);
+ for (size_t i = 0; i < nextLine_.length(); i++)
+ {
+ if (isWhiteSpace(nextLine_[i]))
+ continue;
+ if (nextLine_.compare(i, 2, "/*") == 0)
+ isInComment_ = true;
+ if (isInComment_)
+ {
+ if (nextLine_.compare(i, 2, "*/") == 0)
+ {
+ isInComment_ = false;
+ ++i;
+ }
+ continue;
+ }
+ if (nextLine_[i] == '\\')
+ {
+ ++i;
+ continue;
+ }
+ if (isInQuote_)
+ {
+ if (nextLine_[i] == quoteChar_)
+ isInQuote_ = false;
+ continue;
+ }
+
+ if (nextLine_[i] == '"'
+ || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
+ {
+ isInQuote_ = true;
+ quoteChar_ = nextLine_[i];
+ continue;
+ }
+ if (nextLine_.compare(i, 2, "//") == 0)
+ {
+ i = nextLine_.length();
+ continue;
+ }
+ // process the current char
+ if ((nextLine_[i] == '{' && (currentChar == '-' || currentChar == '+'))
+ || nextLine_[i] == ';')
+ goto EndOfWhileLoop; // end of method definition
+ if (nextLine_[i] == ']')
+ {
+ --sqBracketCount;
+ if (sqBracketCount == 0)
+ goto EndOfWhileLoop; // end of method call
+ }
+ if (nextLine_[i] == '[')
+ ++sqBracketCount;
+ if (isFirstLine) // colon align does not include the first line
+ continue;
+ if (sqBracketCount > 1)
+ continue;
+ if (haveFirstColon) // multiple colons per line
+ continue;
+ // compute colon adjustment
+ if (nextLine_[i] == ':')
+ {
+ haveFirstColon = true;
+ foundMethodColon = true;
+ if (shouldPadMethodColon)
+ {
+ int spacesStart;
+ for (spacesStart = i; spacesStart > 0; spacesStart--)
+ if (!isWhiteSpace(nextLine_[spacesStart - 1]))
+ break;
+ int spaces = i - spacesStart;
+ if (objCColonPadMode == COLON_PAD_ALL || objCColonPadMode == COLON_PAD_BEFORE)
+ colonAdjust = 1 - spaces;
+ else if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_AFTER)
+ colonAdjust = 0 - spaces;
+ }
+ // compute alignment
+ int colonPosition = i + colonAdjust;
+ if (colonPosition > colonAlign)
+ colonAlign = colonPosition;
+ }
+ } // end of for loop
+ isFirstLine = false;
+ } // end of while loop
+EndOfWhileLoop:
+ if (!foundMethodColon)
+ colonAlign = -1;
+ return colonAlign;
+}
+
+// pad an Objective-C method colon
+void ASFormatter::padObjCMethodColon()
+{
+ assert(currentChar == ':');
+ int commentAdjust = 0;
+ char nextChar = peekNextChar();
+ if (objCColonPadMode == COLON_PAD_NONE
+ || objCColonPadMode == COLON_PAD_AFTER
+ || nextChar == ')')
+ {
+ // remove spaces before
+ for (int i = formattedLine.length() - 1; (i > -1) && isWhiteSpace(formattedLine[i]); i--)
+ {
+ formattedLine.erase(i);
+ --commentAdjust;
+ }
+ }
+ else
+ {
+ // pad space before
+ for (int i = formattedLine.length() - 1; (i > 0) && isWhiteSpace(formattedLine[i]); i--)
+ if (isWhiteSpace(formattedLine[i - 1]))
+ {
+ formattedLine.erase(i);
+ --commentAdjust;
+ }
+ appendSpacePad();
+ }
+ if (objCColonPadMode == COLON_PAD_NONE
+ || objCColonPadMode == COLON_PAD_BEFORE
+ || nextChar == ')')
+ {
+ // remove spaces after
+ int nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextText == (int)string::npos)
+ nextText = currentLine.length();
+ int spaces = nextText - charNum - 1;
+ if (spaces > 0)
+ {
+ // do not use goForward here
+ currentLine.erase(charNum + 1, spaces);
+ spacePadNum -= spaces;
+ }
+ }
+ else
+ {
+ // pad space after
+ int nextText = currentLine.find_first_not_of(" \t", charNum + 1);
+ if (nextText == (int)string::npos)
+ nextText = currentLine.length();
+ int spaces = nextText - charNum - 1;
+ if (spaces == 0)
+ {
+ currentLine.insert(charNum + 1, 1, ' ');
+ spacePadNum += 1;
+ }
+ else if (spaces > 1)
+ {
+ // do not use goForward here
+ currentLine.erase(charNum + 1, spaces - 1);
+ spacePadNum -= spaces - 1;
+ }
+ }
+ spacePadNum += commentAdjust;
+}
+
+// Remove the leading '*' from a comment line and indent to the next tab.
+void ASFormatter::stripCommentPrefix()
+{
+ int firstChar = formattedLine.find_first_not_of(" \t");
+ if (firstChar < 0)
+ return;
+
+ if (isInCommentStartLine)
+ {
+ // comment opener must begin the line
+ if (formattedLine.compare(firstChar, 2, "/*") != 0)
+ return;
+ int commentOpener = firstChar;
+ // ignore single line comments
+ int commentEnd = formattedLine.find("*/", firstChar + 2);
+ if (commentEnd != -1)
+ return;
+ // first char after the comment opener must be at least one indent
+ int followingText = formattedLine.find_first_not_of(" \t", commentOpener + 2);
+ if (followingText < 0)
+ return;
+ if (formattedLine[followingText] == '*' || formattedLine[followingText] == '!')
+ followingText = formattedLine.find_first_not_of(" \t", followingText + 1);
+ if (followingText < 0)
+ return;
+ if (formattedLine[followingText] == '*')
+ return;
+ int indentLen = getIndentLength();
+ int followingTextIndent = followingText - commentOpener;
+ if (followingTextIndent < indentLen)
+ {
+ string stringToInsert(indentLen - followingTextIndent, ' ');
+ formattedLine.insert(followingText, stringToInsert);
+ }
+ return;
+ }
+ // comment body including the closer
+ if (formattedLine[firstChar] == '*')
+ {
+ if (formattedLine.compare(firstChar, 2, "*/") == 0)
+ {
+ // line starts with an end comment
+ formattedLine = "*/";
+ }
+ else
+ {
+ // build a new line with one indent
+ int secondChar = formattedLine.find_first_not_of(" \t", firstChar + 1);
+ if (secondChar < 0)
+ {
+ adjustChecksumIn(-'*');
+ formattedLine.erase();
+ return;
+ }
+ if (formattedLine[secondChar] == '*')
+ return;
+ // replace the leading '*'
+ int indentLen = getIndentLength();
+ adjustChecksumIn(-'*');
+ // second char must be at least one indent
+ if (formattedLine.substr(0, secondChar).find('\t') != string::npos)
+ {
+ formattedLine.erase(firstChar, 1);
+ }
+ else
+ {
+ int spacesToInsert = 0;
+ if (secondChar >= indentLen)
+ spacesToInsert = secondChar;
+ else
+ spacesToInsert = indentLen;
+ formattedLine = string(spacesToInsert, ' ') + formattedLine.substr(secondChar);
+ }
+ // remove a trailing '*'
+ int lastChar = formattedLine.find_last_not_of(" \t");
+ if (lastChar > -1 && formattedLine[lastChar] == '*')
+ {
+ adjustChecksumIn(-'*');
+ formattedLine[lastChar] = ' ';
+ }
+ }
+ }
+ else
+ {
+ // first char not a '*'
+ // first char must be at least one indent
+ if (formattedLine.substr(0, firstChar).find('\t') == string::npos)
+ {
+ int indentLen = getIndentLength();
+ if (firstChar < indentLen)
+ {
+ string stringToInsert(indentLen, ' ');
+ formattedLine = stringToInsert + formattedLine.substr(firstChar);
+ }
+ }
+ }
+}
+
+} // end namespace astyle