diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2017-05-09 15:28:09 +0200 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2017-05-09 20:46:16 +0200 |
| commit | d4e54e9f35d532062533f1d369c159810b01d224 (patch) | |
| tree | 7046ff72a41a8b0ad2e40e1d4aa3b191d03f6380 /thirdparty/astyle/ASFormatter.cpp | |
| parent | 8650b70e06408d394c1708846b6fc2d86cf14079 (diff) | |
Add mechanisms to reformant and check code style (#128)
Use an internal version of astyle (astyle 3.0). Scripts taken from QGIS.
astyle.options from https://github.com/uclouvain/openjpeg/issues/128
scripts/prepare-commit.sh can be used locally to automatically reformat
edited files.
Travis-CI will run scripts/verify-indentation.sh to verify committed files.
Diffstat (limited to 'thirdparty/astyle/ASFormatter.cpp')
| -rwxr-xr-x | thirdparty/astyle/ASFormatter.cpp | 7714 |
1 files changed, 7714 insertions, 0 deletions
diff --git a/thirdparty/astyle/ASFormatter.cpp b/thirdparty/astyle/ASFormatter.cpp new file mode 100755 index 00000000..b292305b --- /dev/null +++ b/thirdparty/astyle/ASFormatter.cpp @@ -0,0 +1,7714 @@ +// 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 |
