// ASFormatter.cpp // Copyright (c) 2018 by Jim Pattee . // 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 #include //----------------------------------------------------------------------------- // 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; shouldBreakReturnType = false; shouldBreakReturnTypeDecl = false; shouldAttachReturnType = false; shouldAttachReturnTypeDecl = 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; nonParenHeaders = new vector; preDefinitionHeaders = new vector; preCommandHeaders = new vector; operators = new vector; assignmentOperators = new vector; castOperators = new vector; // initialize ASEnhancer member vectors indentableMacros = new vector* >; } /** * 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); initContainer(parenStack, new vector); initContainer(structStack, new vector); initContainer(questionMarkStack, new vector); parenStack->emplace_back(0); // parenStack must contain this default entry initContainer(braceTypeStack, new vector); braceTypeStack->emplace_back(NULL_TYPE); // braceTypeStack must contain this default entry clearFormattedLineSplitPoints(); currentHeader = nullptr; currentLine = ""; readyFormattedLine = ""; formattedLine = ""; verbatimDelimiter = ""; currentChar = ' '; previousChar = ' '; previousCommandChar = ' '; previousNonWSChar = ','; // not a potential name or operator quoteChar = '"'; preprocBlockEnd = 0; charNum = 0; checksumIn = 0; checksumOut = 0; currentLineFirstBraceNum = string::npos; formattedLineCommentNum = 0; leadingSpaces = 0; previousReadyFormattedLineLength = string::npos; preprocBraceTypeStackSize = 0; spacePadNum = 0; methodAttachCharNum = string::npos; methodAttachLineNum = 0; methodBreakCharNum = string::npos; methodBreakLineNum = 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; isInObjCParam = false; isInObjCSelector = false; breakCurrentOneLineBlock = false; shouldRemoveNextClosingBrace = false; isInBraceRunIn = false; returnTypeChecked = 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_RATLIFF) { // 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 break-return-type and attach-return-type if (shouldBreakReturnType) shouldAttachReturnType = false; if (shouldBreakReturnTypeDecl) shouldAttachReturnTypeDecl = 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 == '#' && currentLine.find_first_not_of(" \t") == (size_t) charNum && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)) { 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 && currentChar != '#' // don't add to preprocessor && (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 or foo if (!isInTemplate && currentChar == '<') { checkIfTemplateOpener(); } // Check for break return type if ((size_t) charNum >= methodBreakCharNum && methodBreakLineNum == 0) { if ((size_t) charNum == methodBreakCharNum) isInLineBreak = true; methodBreakCharNum = string::npos; methodBreakLineNum = 0; } // Check for attach return type if ((size_t) charNum >= methodAttachCharNum && methodAttachLineNum == 0) { if ((size_t) charNum == methodAttachCharNum) { int pa = pointerAlignment; int ra = referenceAlignment; int itemAlignment = (previousNonWSChar == '*' || previousNonWSChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra); isInLineBreak = false; if (previousNonWSChar == '*' || previousNonWSChar == '&' || previousNonWSChar == '^') { if (itemAlignment == REF_ALIGN_TYPE) { if (formattedLine.length() > 0 && !isWhiteSpace(formattedLine[formattedLine.length() - 1])) formattedLine.append(1, ' '); } else if (itemAlignment == REF_ALIGN_MIDDLE) { if (formattedLine.length() > 0 && !isWhiteSpace(formattedLine[formattedLine.length() - 1])) formattedLine.append(1, ' '); } else if (itemAlignment == REF_ALIGN_NAME) { if (formattedLine.length() > 0 && isWhiteSpace(formattedLine[formattedLine.length() - 1])) formattedLine.erase(formattedLine.length() - 1); } else { if (formattedLine.length() > 1 && !isWhiteSpace(formattedLine[formattedLine.length() - 2])) formattedLine.append(1, ' '); } } else formattedLine.append(1, ' '); } methodAttachCharNum = string::npos; methodAttachLineNum = 0; } // 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; isImmediatelyPostObjCMethodPrefix = false; isInObjCInterface = false; isInEnum = false; isJavaStaticConstructor = false; isCharImmediatelyPostNonInStmt = false; needHeaderOpeningBrace = false; shouldKeepLineUnbroken = false; returnTypeChecked = false; objCColonAlign = 0; //assert(methodBreakCharNum == string::npos); // comment out //assert(methodBreakLineNum == 0); // comment out methodBreakCharNum = string::npos; methodBreakLineNum = 0; methodAttachCharNum = string::npos; methodAttachLineNum = 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; // Objective-C method prefix with no return type if (isImmediatelyPostObjCMethodPrefix && currentChar != '(') { if (shouldPadMethodPrefix || shouldUnPadMethodPrefix) padObjCMethodPrefix(); isImmediatelyPostObjCMethodPrefix = false; } // look for headers bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum); if (isPotentialHeader && !isInTemplate && squareBracketCount == 0) { isNonParenHeader = false; foundClosingHeader = false; newHeader = findHeader(headers); // java can have a 'default' not in a switch if (newHeader == &AS_DEFAULT && ASBeautifier::peekNextChar( currentLine, charNum + (*newHeader).length() - 1) != ':') newHeader = nullptr; // 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 && !foundNamespaceHeader && !foundClassHeader) foundInterfaceHeader = true; foundPreDefinitionHeader = true; appendSequence(*newHeader); goForward(newHeader->length() - 1); continue; } else if ((newHeader = findHeader(preCommandHeaders)) != nullptr) { // must be after function arguments 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; //assert(methodBreakCharNum == string::npos); // comment out //assert(methodBreakLineNum == 0); // comment out methodBreakCharNum = string::npos; methodBreakLineNum = 0; methodAttachCharNum = string::npos; methodAttachLineNum = 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() && (squareBracketCount > 0 || isInObjCMethodDefinition || isInObjCSelector) && !foundQuestionMark) // not in a ?: sequence { isImmediatelyPostObjCMethodPrefix = false; isInObjCReturnType = false; isInObjCParam = true; if (shouldPadMethodColon) 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; isImmediatelyPostReturn = true; // return is the same as an = sign } 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(), DEFINITION_TYPE))) foundTrailingReturnType = true; // check for break/attach return type if (shouldBreakReturnType || shouldBreakReturnTypeDecl || shouldAttachReturnType || shouldAttachReturnTypeDecl) { if ((isBraceType(braceTypeStack->back(), NULL_TYPE) || isBraceType(braceTypeStack->back(), DEFINITION_TYPE)) && !returnTypeChecked && !foundNamespaceHeader && !foundClassHeader && !isInObjCMethodDefinition // bypass objective-C and java @ character && charNum == (int) currentLine.find_first_not_of(" \t") && !(isCStyle() && isCharPotentialHeader(currentLine, charNum) && (findKeyword(currentLine, charNum, AS_PUBLIC) || findKeyword(currentLine, charNum, AS_PRIVATE) || findKeyword(currentLine, charNum, AS_PROTECTED)))) { findReturnTypeSplitPoint(currentLine); returnTypeChecked = 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 == '@' && isCStyle() && (int) currentLine.length() > charNum + 1 && !isWhiteSpace(currentLine[charNum + 1]) && 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 == '@' && isCStyle() && (int) currentLine.length() > charNum + 1 && !isWhiteSpace(currentLine[charNum + 1]) && 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 == '+') && isCStyle() && (int) currentLine.find_first_not_of(" \t") == charNum && !isInPotentialCalculation && !isInObjCMethodDefinition && (isBraceType(braceTypeStack->back(), NULL_TYPE) || (isBraceType(braceTypeStack->back(), EXTERN_TYPE)))) { isInObjCMethodDefinition = true; isImmediatelyPostObjCMethodPrefix = true; isInObjCParam = false; 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 (isInObjCParam && (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 compatibility 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 compatibility 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 compatibility 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 compatibility 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; } void ASFormatter::setBreakReturnType(bool state) { shouldBreakReturnType = state; } void ASFormatter::setBreakReturnTypeDecl(bool state) { shouldBreakReturnTypeDecl = state; } void ASFormatter::setAttachReturnType(bool state) { shouldAttachReturnType = state; } void ASFormatter::setAttachReturnTypeDecl(bool state) { shouldAttachReturnTypeDecl = 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 if (methodBreakLineNum > 0) --methodBreakLineNum; if (methodAttachLineNum > 0) --methodAttachLineNum; // 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) && 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; } bool ASFormatter::isNumericVariable(string word) const { if (word == "bool" || word == "int" || word == "void" || word == "char" || word == "long" || word == "short" || word == "double" || word == "float" || (word.length() >= 4 // check end of word for _t && word.compare(word.length() - 2, 2, "_t") == 0) // removed release 3.1 // || word == "Int32" // || word == "UInt32" // || word == "Int64" // || word == "UInt64" || word == "BOOL" || word == "DWORD" || word == "HWND" || word == "INT" || word == "LPSTR" || word == "VOID" || word == "LPVOID" || word == "wxFontEncoding" ) return true; return false; } /** * 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 = " "; if (isDigit(lastWord[0]) || isDigit(nextText[0]) || nextText[0] == '!' || nextText[0] == '~') return false; // check for multiply then a dereference (a * *b) char nextChar = peekNextChar(); if (currentChar == '*' && nextChar == '*' && !isPointerToPointer(currentLine, charNum)) 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 (lastWord == AS_AUTO) return true; 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 *& 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 * * is a pointer to a pointer or a multiply then a dereference. * * @return true if a pointer *. */ bool ASFormatter::isPointerToPointer(const string& line, int currPos) const { assert(line[currPos] == '*' && peekNextChar() == '*'); if ((int) line.length() > currPos + 1 && line[currPos + 1] == '*') return true; size_t nextText = line.find_first_not_of(" \t", currPos + 1); if (nextText == string::npos || line[nextText] != '*') return false; size_t nextText2 = line.find_first_not_of(" \t", nextText + 1); if (nextText == string::npos) return false; if (line[nextText2] == ')' || line[nextText2] == '*') return true; return false; } /** * 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 == '-'); // does a digit follow a c-style cast if (previousCommandChar == ')') { if (!isdigit(peekNextChar())) return false; size_t end = currentLine.rfind(')', charNum); if (end == string::npos) return false; size_t lastChar = currentLine.find_last_not_of(" \t", end - 1); if (lastChar == string::npos) return false; if (currentLine[lastChar] == '*') end = lastChar; string prevWord = getPreviousWord(currentLine, end); if (prevWord.empty()) return false; if (!isNumericVariable(prevWord)) return false; return true; } return ((isCharImmediatelyPostReturn || !isLegalNameChar(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 && (size_t) charNum == 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 (isInQuote_) { if (ch == '\\') ++i; else 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) || 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 streamArg /*nullptr*/) const { assert(sourceIterator->getPeekStart() == 0 || streamArg != nullptr); // Borland may need != 0 bool isFirstLine = true; string nextLine_ = firstLine; size_t firstChar = string::npos; shared_ptr stream = streamArg; if (stream == nullptr) // Borland may need == 0 stream = make_shared(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; // following line comments may be a tag from AStyleWx //[[)> size_t nextNum = currentLine.find_first_not_of(" \t", endNum + 2); if (nextNum != string::npos && currentLine.compare(nextNum, 2, "//") != 0) 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(currentLine.substr(charNum, ptrLength)); 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(); string sequenceToInsert(1, currentChar); // get the sequence if (currentChar == peekNextChar()) { for (size_t i = charNum + 1; currentLine.length() > i; i++) { if (currentLine[i] == sequenceToInsert[0]) { sequenceToInsert.append(1, currentLine[i]); goForward(1); continue; } break; } } // append the seqence string charSave; size_t prevCh = formattedLine.find_last_not_of(" \t"); if (prevCh < formattedLine.length()) { charSave = formattedLine.substr(prevCh + 1); formattedLine.resize(prevCh + 1); } formattedLine.append(sequenceToInsert); if (peekNextChar() != ')') formattedLine.append(charSave); else spacePadNum -= charSave.length(); // 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 && formattedLine.length() > 0) { 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 (currentChar == peekNextChar()) { for (size_t i = charNum + 1; currentLine.length() > i; i++) { if (currentLine[i] == sequenceToInsert[0]) { sequenceToInsert.append(1, currentLine[i]); goForward(1); continue; } break; } } // 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; if (index < formattedLine.length()) formattedLine.insert(index, sequenceToInsert); else formattedLine.append(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 (currentChar == peekNextChar()) { for (size_t i = charNum + 1; currentLine.length() > i; i++) { if (currentLine[i] == sequenceToInsert[0]) { sequenceToInsert.append(1, currentLine[i]); goForward(1); continue; } break; } } // 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 ((isLegalNameChar(peekedChar) || peekedChar == '(' || peekedChar == '[' || peekedChar == '=') && (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", i); if (start != string::npos && currentLine[start] != ')') break; } goForward(1); if (formattedLine.length() > 0) formattedLine.append(1, currentLine[charNum]); 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]) && peekedChar != '*' // check for '* *' && !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 (itemAlignment == PTR_ALIGN_TYPE && currentChar == '*' && prevCh == '*') { // '* *' may be a multiply followed by a dereference if (prevNum + 2 < formattedLine.length() && isWhiteSpace(formattedLine[prevNum + 2])) { spacePadNum -= (formattedLine.length() - 2 - prevNum); formattedLine.erase(prevNum + 2); } } else 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; // don't unpad else if (prevWord == AS_RETURN) prevIsParenHeader = true; // don't unpad else if ((prevWord == AS_NEW || prevWord == AS_DELETE) && shouldPadHeader) prevIsParenHeader = true; // don't unpad else if (isCStyle() && prevWord == AS_THROW && shouldPadHeader) prevIsParenHeader = true; // don't unpad else if (prevWord == "and" || prevWord == "or" || prevWord == "in") prevIsParenHeader = true; // don't unpad // don't unpad variables else if (isNumericVariable(prevWord)) prevIsParenHeader = true; // don't unpad } } // 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 method prefix (- or +) * if this is a '(' it begins a return type * these options have precedence over the padParens methods * the padParens method has already been called, this method adjusts */ void ASFormatter::padObjCMethodPrefix() { assert(isInObjCMethodDefinition && isImmediatelyPostObjCMethodPrefix); assert(shouldPadMethodPrefix || shouldUnPadMethodPrefix); size_t prefix = formattedLine.find_first_of("+-"); if (prefix == string::npos) return; size_t firstChar = formattedLine.find_first_not_of(" \t", prefix + 1); if (firstChar == string::npos) firstChar = formattedLine.length(); int spaces = firstChar - prefix - 1; if (shouldPadMethodPrefix) { if (spaces == 0) { formattedLine.insert(prefix + 1, 1, ' '); spacePadNum += 1; } else if (spaces > 1) { formattedLine.erase(prefix + 1, spaces - 1); formattedLine[prefix + 1] = ' '; // convert any tab to space 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); currentLine[charNum + 1] = ' '; // convert any tab to space 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] == ' ') { int lastText = formattedLine.find_last_not_of(" \t"); spacePadNum -= formattedLine.length() - lastText - 1; formattedLine.resize(lastText + 1); } // 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); formattedLine[prevText + 1] = ' '; // convert any tab to space 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); currentLine[charNum + 1] = ' '; // convert any tab to space 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) && sourceIterator->hasMoreLines()) { // 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 && (size_t) charNum == 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 && currentLineFirstBraceNum == (size_t) 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() && sourceIterator->hasMoreLines()) { // 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 && currentLineFirstBraceNum == (size_t) charNum && !isBraceType(braceType, SINGLE_LINE_TYPE)) shouldBreakLineAtNextChar = true; } else if (braceFormatMode == RUN_IN_MODE) { if (isWhiteSpace(peekNextChar()) && !isInVirginLine) breakLine(); else if (isBeforeAnyComment() && sourceIterator->hasMoreLines()) { // 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 && (size_t) charNum == 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 != '.') || 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*& 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 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*& container, vector* 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 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(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 && currentLineFirstBraceNum == (size_t) 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 "} 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 "} 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; } if (!stream.hasMoreLines()) return false; // 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; } /** * Find split point for break/attach return type. */ void ASFormatter::findReturnTypeSplitPoint(const string& firstLine) { assert((isBraceType(braceTypeStack->back(), NULL_TYPE) || isBraceType(braceTypeStack->back(), DEFINITION_TYPE))); assert(shouldBreakReturnType || shouldBreakReturnTypeDecl || shouldAttachReturnType || shouldAttachReturnTypeDecl); bool isFirstLine = true; bool isInComment_ = false; bool isInQuote_ = false; bool foundSplitPoint = false; bool isAlreadyBroken = false; char quoteChar_ = ' '; char currNonWSChar = ' '; char prevNonWSChar = ' '; size_t parenCount = 0; size_t squareCount = 0; size_t angleCount = 0; size_t breakLineNum = 0; size_t breakCharNum = string::npos; string line = firstLine; // Process the lines until a ';' or '{'. ASPeekStream stream(sourceIterator); while (stream.hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { if (isInQuote_) return; line = stream.peekNextLine(); if (!foundSplitPoint) ++breakLineNum; } size_t firstCharNum = line.find_first_not_of(" \t"); if (firstCharNum == string::npos) continue; if (line[firstCharNum] == '#') { // don't attach to a preprocessor if (shouldAttachReturnType || shouldAttachReturnTypeDecl) return; else continue; } // parse the line for (size_t i = 0; i < line.length(); i++) { if (!isWhiteSpace(line[i])) { prevNonWSChar = currNonWSChar; currNonWSChar = line[i]; } else if (line[i] == '\t' && shouldConvertTabs) { size_t tabSize = getTabLength(); size_t numSpaces = tabSize - ((tabIncrementIn + i) % tabSize); line.replace(i, 1, numSpaces, ' '); currentChar = line[i]; } if (line.compare(i, 2, "/*") == 0) isInComment_ = true; if (isInComment_) { if (line.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (line[i] == '\\') { ++i; continue; } if (isInQuote_) { if (line[i] == quoteChar_) isInQuote_ = false; continue; } if (line[i] == '"' || (line[i] == '\'' && !isDigitSeparator(line, i))) { isInQuote_ = true; quoteChar_ = line[i]; continue; } if (line.compare(i, 2, "//") == 0) { i = line.length(); continue; } // not in quote or comment if (!foundSplitPoint) { if (line[i] == '<') { ++angleCount; continue; } if (line[i] == '>') { if (angleCount) --angleCount; if (!angleCount) { size_t nextCharNum = line.find_first_not_of(" \t*&", i + 1); if (nextCharNum == string::npos) { breakCharNum = string::npos; continue; } if (line[nextCharNum] != ':') // scope operator breakCharNum = nextCharNum; } continue; } if (angleCount) continue; if (line[i] == '[') { ++squareCount; continue; } if (line[i] == ']') { if (squareCount) --squareCount; continue; } // an assignment before the parens is not a function if (line[i] == '=') return; if (isWhiteSpace(line[i]) || line[i] == '*' || line[i] == '&') { size_t nextNum = line.find_first_not_of(" \t", i + 1); if (nextNum == string::npos) breakCharNum = string::npos; else { if (line.length() > nextNum + 1 && line[nextNum] == ':' && line[nextNum + 1] == ':') i = --nextNum; else if (line[nextNum] != '(') breakCharNum = string::npos; } continue; } if ((isLegalNameChar(line[i]) || line[i] == '~') && breakCharNum == string::npos) { breakCharNum = i; if (isLegalNameChar(line[i]) && findKeyword(line, i, AS_OPERATOR)) { if (breakCharNum == firstCharNum) isAlreadyBroken = true; foundSplitPoint = true; // find the operator, may be parens size_t parenNum = line.find_first_not_of(" \t", i + AS_OPERATOR.length()); if (parenNum == string::npos) return; // find paren after the operator parenNum = line.find('(', parenNum + 1); if (parenNum == string::npos) return; i = --parenNum; } continue; } if (line[i] == ':' && line.length() > i + 1 && line[i + 1] == ':') { size_t nextCharNum = line.find_first_not_of(" \t:", i + 1); if (nextCharNum == string::npos) return; if (isLegalNameChar(line[nextCharNum]) && findKeyword(line, nextCharNum, AS_OPERATOR)) { i = nextCharNum; if (breakCharNum == firstCharNum) isAlreadyBroken = true; foundSplitPoint = true; // find the operator, may be parens size_t parenNum = line.find_first_not_of(" \t", i + AS_OPERATOR.length()); if (parenNum == string::npos) return; // find paren after the operator parenNum = line.find('(', parenNum + 1); if (parenNum == string::npos) return; i = --parenNum; } else i = --nextCharNum; continue; } if (line[i] == '(' && !squareCount) { // is line is already broken? if (breakCharNum == firstCharNum && breakLineNum > 0) isAlreadyBroken = true; ++parenCount; foundSplitPoint = true; continue; } } // end !foundSplitPoint if (line[i] == '(') { // consecutive ')(' parens is probably a function pointer if (prevNonWSChar == ')' && !parenCount) return; ++parenCount; continue; } if (line[i] == ')') { if (parenCount) --parenCount; continue; } if (line[i] == '{') { if (shouldBreakReturnType && foundSplitPoint && !isAlreadyBroken) { methodBreakCharNum = breakCharNum; methodBreakLineNum = breakLineNum; } if (shouldAttachReturnType && foundSplitPoint && isAlreadyBroken) { methodAttachCharNum = breakCharNum; methodAttachLineNum = breakLineNum; } return; } if (line[i] == ';') { if (shouldBreakReturnTypeDecl && foundSplitPoint && !isAlreadyBroken) { methodBreakCharNum = breakCharNum; methodBreakLineNum = breakLineNum; } if ((shouldAttachReturnTypeDecl && foundSplitPoint && isAlreadyBroken)) { methodAttachCharNum = breakCharNum; methodAttachLineNum = breakLineNum; } return; } if (line[i] == '}') return; } // end of for loop if (!foundSplitPoint) breakCharNum = string::npos; } // end of while loop } /** * 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(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 + 1 && 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 || currentChar_ == '&' // reference, e.g. A || currentChar_ == '*' // pointer, e.g. A || currentChar_ == '^' // C++/CLI managed pointer, e.g. A || 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 (previousNonWSChar != ' ' && 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 (!isWhiteSpace(currentChar) && 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; isImmediatelyPostObjCMethodPrefix = false; isInObjCReturnType = false; isInObjCParam = false; isInObjCInterface = false; isInObjCSelector = false; isInEnum = false; isInExternC = false; elseHeaderFollowsComments = false; returnTypeChecked = 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; bool haveTernary = 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; if (nextLine_[i] == '?') { haveTernary = true; continue; } // compute colon adjustment if (nextLine_[i] == ':') { if (haveTernary) { haveTernary = false; continue; } 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; } if (formattedLine.length() > 0) { appendSpacePad(); formattedLine.back() = ' '; // convert any tab to space } } if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_BEFORE || nextChar == ')') { // remove spaces after size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); if (nextText == 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 size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); if (nextText == 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); currentLine[charNum + 1] = ' '; // convert any tab to space 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