diff options
Diffstat (limited to 'lib/astyle/ASFormatter.cpp')
-rw-r--r-- | lib/astyle/ASFormatter.cpp | 2197 |
1 files changed, 2197 insertions, 0 deletions
diff --git a/lib/astyle/ASFormatter.cpp b/lib/astyle/ASFormatter.cpp new file mode 100644 index 00000000..eb418760 --- /dev/null +++ b/lib/astyle/ASFormatter.cpp @@ -0,0 +1,2197 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * ASFormatter.cpp + * + * This file is a part of "Artistic Style" - an indentation and + * reformatting tool for C, C++, C# and Java source files. + * http://astyle.sourceforge.net + * + * The "Artistic Style" project, including all files needed to + * compile it, is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this project; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include "astyle.h" + +#include <algorithm> +#include <fstream> +#include <iostream> +#ifdef __VMS +#include <assert> +#else +#include <cassert> +#endif + +// can trace only if NDEBUG is not defined +#ifndef NDEBUG +// #define TRACEunpad +// #define TRACEcomment +// #define TRACEheader +// #define TRACEbracket +// #define TRACEarray +#if defined(TRACEunpad) || defined(TRACEcomment) || defined(TRACEheader) \ +|| defined(TRACEbracket) || defined(TRACEarray) +ofstream *traceOutF; +#define TRACEF +#endif +#endif + +#ifdef TRACEunpad +#define TRunpad(a,b,c) if(b > 0 || c > 0) *traceOutF << outLineNumber << " " << b << a << c << endl +#else +#define TRunpad(a,b,c) ((void)0) +#endif + +#ifdef TRACEcomment +#define TRcomment(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRcomment(a) ((void)0) +#endif + +#ifdef TRACEheader +#define TRxtra(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRxtra(a) ((void)0) +#endif + +#ifdef TRACEbracket +#define TRbracket(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRbracket(a) ((void)0) +#endif + +#ifdef TRACEarray +#define TRarray(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRarray(a) ((void)0) +#endif + +#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); } +#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container); } +#define IS_A(a,b) ( ((a) & (b)) == (b)) + +using namespace std; + +namespace astyle +{ +vector<const string*> ASFormatter::headers; +vector<const string*> ASFormatter::nonParenHeaders; +vector<const string*> ASFormatter::preDefinitionHeaders; +vector<const string*> ASFormatter::preCommandHeaders; +vector<const string*> ASFormatter::operators; +vector<const string*> ASFormatter::assignmentOperators; +vector<const string*> ASFormatter::castOperators; + +/** + * Constructor of ASFormatter + */ +ASFormatter::ASFormatter() +{ + preBracketHeaderStack = NULL; + bracketTypeStack = NULL; + parenStack = NULL; + lineCommentNoIndent = false; + sourceIterator = NULL; + bracketFormatMode = NONE_MODE; + shouldPadOperators = false; + shouldPadParensOutside = false; + shouldPadParensInside = false; + shouldUnPadParens = false; + shouldBreakOneLineBlocks = true; + shouldBreakOneLineStatements = true; + shouldConvertTabs = false; + shouldBreakBlocks = false; + shouldBreakClosingHeaderBlocks = false; + shouldBreakClosingHeaderBrackets = false; + shouldBreakElseIfs = false; +#ifdef TRACEF + // create a trace text file + string filename = "tracef.txt"; + char* env = getenv("HOME"); + if (env != NULL) + filename = string(env) + string("/tracef.txt"); + else + { + env = getenv("USERPROFILE"); + if (env != NULL) + filename = string(env) + string("\\My Documents\\tracef.txt"); + else + { + cout << "\nCould not open tracef.txt\n" << endl; + exit(1); + } + } + traceOutF = new ofstream(filename.c_str()); +#endif +} + +/** + * Destructor of ASFormatter + */ +ASFormatter::~ASFormatter() +{ + DELETE_CONTAINER(preBracketHeaderStack); +#ifdef TRACEF + delete traceOutF; +#endif +} + +/** + * initialization of static data of ASFormatter. + */ +void ASFormatter::staticInit() +{ + static int formatterFileType = 9; // initialized with an invalid type + + if (fileType == formatterFileType) // don't build unless necessary + return; + + formatterFileType = fileType; + + headers.clear(); + nonParenHeaders.clear(); + assignmentOperators.clear(); + operators.clear(); + preDefinitionHeaders.clear(); + preCommandHeaders.clear(); + castOperators.clear(); + + ASResource::buildHeaders(headers, fileType); + ASResource::buildNonParenHeaders(nonParenHeaders, fileType); + ASResource::buildAssignmentOperators(assignmentOperators); + ASResource::buildOperators(operators); + ASResource::buildPreDefinitionHeaders(preDefinitionHeaders); + ASResource::buildPreCommandHeaders(preCommandHeaders); + ASResource::buildCastOperators(castOperators); +} + +/** + * initialize the ASFormatter. + * + * init() should be called every time a ASFormatter object is to start + * formatting a NEW source file. + * init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object + * that will be used to iterate through the source code. This object will be + * deleted during the ASFormatter's destruction, and thus should not be + * deleted elsewhere. + * + * @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object. + */ +void ASFormatter::init(ASSourceIterator *si) +{ + staticInit(); + + ASBeautifier::init(si); + ASEnhancer::init(ASBeautifier::getIndentLength(), + ASBeautifier::getIndentString(), + ASBeautifier::getCStyle(), + ASBeautifier::getJavaStyle(), + ASBeautifier::getSharpStyle(), + ASBeautifier::getCaseIndent(), + ASBeautifier::getEmptyLineFill()); + sourceIterator = si; + + INIT_CONTAINER(preBracketHeaderStack, new vector<const string*>); + INIT_CONTAINER(bracketTypeStack, new vector<BracketType>); + bracketTypeStack->push_back(NULL_TYPE); + INIT_CONTAINER(parenStack, new vector<int>); + parenStack->push_back(0); + + currentHeader = NULL; + currentLine = string(""); + readyFormattedLine = string(""); + formattedLine = ""; + currentChar = ' '; + previousChar = ' '; + previousCommandChar = ' '; + previousNonWSChar = ' '; + quoteChar = '"'; + charNum = 0; + spacePadNum = 0; + previousReadyFormattedLineLength = string::npos; + templateDepth = 0; + previousBracketType = NULL_TYPE; + previousOperator = NULL; + + isVirgin = true; + isInLineComment = false; + isInComment = false; + isInPreprocessor = false; + doesLineStartComment = false; + isInQuote = false; + isSpecialChar = false; + isNonParenHeader = true; + foundNamespaceHeader = false; + foundClassHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundCastOperator = false; + foundQuestionMark = false; + isInLineBreak = false; + endOfCodeReached = false; + isLineReady = false; + isPreviousBracketBlockRelated = true; + isInPotentialCalculation = false; + shouldReparseCurrentChar = false; + passedSemicolon = false; + passedColon = false; + isInTemplate = false; + isInBlParen = false; + shouldBreakLineAfterComments = false; + isImmediatelyPostComment = false; + isImmediatelyPostLineComment = false; + isImmediatelyPostEmptyBlock = false; + isImmediatelyPostPreprocessor = false; + + isPrependPostBlockEmptyLineRequested = false; + isAppendPostBlockEmptyLineRequested = false; + prependEmptyLine = false; + appendOpeningBracket = false; + + foundClosingHeader = false; + previousReadyFormattedLineLength = 0; + + isImmediatelyPostHeader = false; + isInHeader = false; +#ifdef TRACEF + // fileName will be empty if ASTYLE_LIB is defined + if (fileName.empty()) + *traceOutF << "new file" << endl; + else + *traceOutF << fileName << endl; +#endif +} + +/** + * get the next formatted line. + * + * @return formatted line. + */ + +string ASFormatter::nextLine() +{ + // these are reset with each new line + const string *newHeader; + bool isInVirginLine = isVirgin; + isCharImmediatelyPostComment = false; + isPreviousCharPostComment = false; + isCharImmediatelyPostLineComment = false; + isCharImmediatelyPostOpenBlock = false; + isCharImmediatelyPostCloseBlock = false; + isCharImmediatelyPostTemplate = false; + + while (!isLineReady) + { + if (shouldReparseCurrentChar) + shouldReparseCurrentChar = false; + else if (!getNextChar()) + { + breakLine(); + return beautify(readyFormattedLine); + } + else // stuff to do when reading a new character... + { + // make sure that a virgin '{' at the begining ofthe file will be treated as a block... + if (isInVirginLine && currentChar == '{') + previousCommandChar = '{'; + isPreviousCharPostComment = isCharImmediatelyPostComment; + isCharImmediatelyPostComment = false; + isCharImmediatelyPostTemplate = false; + } + + //if (inLineNumber >= 185) + // int x = 1; + + if (isInLineComment) + { + appendCurrentChar(); + + // explicitely 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. + } + continue; + } + else if (isInComment) + { + if (isSequenceReached("*/")) + { + isInComment = false; + isImmediatelyPostComment = true; + appendSequence(AS_CLOSE_COMMENT); + goForward(1); + } + else + appendCurrentChar(); + + continue; + } + + // not in line comment or comment + + else if (isInQuote) + { + if (isSpecialChar) + { + isSpecialChar = false; + appendCurrentChar(); + } + else if (currentChar == '\\') + { + isSpecialChar = true; + appendCurrentChar(); + } + else if (quoteChar == currentChar) + { + isInQuote = false; + appendCurrentChar(); + } + else + { + appendCurrentChar(); + } + + continue; + } + + // handle white space - needed to simplify the rest. + if (isWhiteSpace(currentChar) || isInPreprocessor) + { + appendCurrentChar(); + continue; + } + + /* not in MIDDLE of quote or comment or white-space of any type ... */ + + if (isSequenceReached("//")) + { + if (currentLine[charNum+2] == '\xf2') // check for windows line marker + isAppendPostBlockEmptyLineRequested = false; + isInLineComment = true; + // do not indent if in column 1 or 2 + if (lineCommentNoIndent == false) + { + if (charNum == 0) + lineCommentNoIndent = true; + else if (charNum == 1 && currentLine[0] == ' ') + lineCommentNoIndent = true; + } + // move comment if spaces were added or deleted + if (lineCommentNoIndent == false && spacePadNum != 0) + adjustComments(); + formattedLineCommentNum = formattedLine.length(); + appendSequence(AS_OPEN_LINE_COMMENT); + goForward(1); + // explicitely 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. + } + continue; + } + else if (isSequenceReached("/*")) + { + isInComment = true; + if (spacePadNum != 0) + adjustComments(); + formattedLineCommentNum = formattedLine.length(); + appendSequence(AS_OPEN_COMMENT); + goForward(1); + continue; + } + else if (currentChar == '"' || currentChar == '\'') + { + isInQuote = true; + quoteChar = currentChar; + appendCurrentChar(); + continue; + } + + /* not in quote or comment or white-space of any type ... */ + + // check if in preprocessor + // ** isInPreprocessor will be automatically reset at the begining + // of a new line in getnextChar() + if (currentChar == '#') + { + isInPreprocessor = true; + appendCurrentChar(); + continue; + } + + /* not in preprocessor ... */ + + if (isImmediatelyPostComment) + { + isImmediatelyPostComment = false; + isCharImmediatelyPostComment = true; + } + + if (isImmediatelyPostLineComment) + { + isImmediatelyPostLineComment = false; + isCharImmediatelyPostLineComment = true; + } + + if (shouldBreakLineAfterComments) + { + shouldBreakLineAfterComments = false; + shouldReparseCurrentChar = true; + breakLine(); + continue; + } + + // reset isImmediatelyPostHeader information + if (isImmediatelyPostHeader) + { + isImmediatelyPostHeader = false; + + // Make sure headers are broken from their succeeding blocks + // (e.g. + // if (isFoo) DoBar(); + // should become + // if (isFoo) + // DoBar; + // ) + // But treat else if() as a special case which should not be broken! + if (shouldBreakOneLineStatements) + { + // if may break 'else if()'s, then simply break the line + + if (shouldBreakElseIfs) + isInLineBreak = true; + } + } + + if (passedSemicolon) // need to break the formattedLine + { + passedSemicolon = false; + if (parenStack->back() == 0 && currentChar != ';') // allow ;; + { + // does a one-line statement have ending comments? + if (IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE)) + { + size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACKET); + assert(blockEnd != string::npos); + // move ending comments to this formattedLine + if (isBeforeLineEndComment(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)); + size_t commentLength = currentLine.length() - commentStart; + int tabCount = getIndentLength(); + appendSpacePad(); + for (int i=1; i<tabCount; i++) + formattedLine.append(1, ' '); + formattedLine.append(currentLine, commentStart, commentLength); + currentLine.erase(commentStart, commentLength); + } + } + shouldReparseCurrentChar = true; + isInLineBreak = true; + continue; + } + } + + if (passedColon) + { + passedColon = false; + if (parenStack->back() == 0 && !isBeforeComment()) + { + shouldReparseCurrentChar = true; + isInLineBreak = true; + continue; + } + } + + // Check if in template declaration, e.g. foo<bar> or foo<bar,fig> + // If so, set isInTemplate to true + if (!isInTemplate && currentChar == '<') + { + int maxTemplateDepth = 0; + templateDepth = 0; + const string *oper; + for (size_t i = charNum; + i < currentLine.length(); + i += (oper ? oper->length() : 1)) + { + oper = ASBeautifier::findHeader(currentLine, i, operators); + + if (oper == &AS_LS) + { + templateDepth++; + maxTemplateDepth++; + } + else if (oper == &AS_GR) + { + templateDepth--; + if (templateDepth == 0) + { + // this is a template! + isInTemplate = true; + templateDepth = maxTemplateDepth; + break; + } + } + else if (oper == &AS_COMMA // comma, e.g. A<int, char> + || oper == &AS_BIT_AND // reference, e.g. A<int&> + || oper == &AS_MULT // pointer, e.g. A<int*> + || oper == &AS_COLON_COLON) // ::, e.g. std::string + { + continue; + } + else if (!isLegalNameChar(currentLine[i]) && !isWhiteSpace(currentLine[i])) + { + // this is not a template -> leave... + isInTemplate = false; + break; + } + } + } + + // handle parenthesies + if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<')) + { + parenStack->back()++; + if (currentChar == '[') + isInBlParen = true; + } + else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>')) + { + parenStack->back()--; + if (isInTemplate && currentChar == '>') + { + templateDepth--; + if (templateDepth == 0) + { + isInTemplate = false; + isCharImmediatelyPostTemplate = true; + } + } + + // check if this parenthesis closes a header, e.g. if (...), while (...) + if (isInHeader && parenStack->back() == 0) + { + isInHeader = false; + isImmediatelyPostHeader = true; + } + if (currentChar == ']') + isInBlParen = false; + if (currentChar == ')') + foundCastOperator = false; + } + + // handle brackets + if (currentChar == '{' || currentChar == '}') + { + if (currentChar == '{') + { + BracketType newBracketType = getBracketType(); + foundNamespaceHeader = false; + foundClassHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + isInPotentialCalculation = false; + + bracketTypeStack->push_back(newBracketType); + preBracketHeaderStack->push_back(currentHeader); + currentHeader = NULL; + + isPreviousBracketBlockRelated = !IS_A(newBracketType, ARRAY_TYPE); + } + + // this must be done before the bracketTypeStack is popped + BracketType bracketType = bracketTypeStack->back(); + bool isOpeningArrayBracket = (IS_A(bracketType, ARRAY_TYPE) + && bracketTypeStack->size() >= 2 + && !IS_A((*bracketTypeStack)[bracketTypeStack->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 bracket, + // then there is not need for the post block empty line. + // + isAppendPostBlockEmptyLineRequested = false; + + if (!bracketTypeStack->empty()) + { + previousBracketType = bracketTypeStack->back(); + bracketTypeStack->pop_back(); + isPreviousBracketBlockRelated = !IS_A(bracketType, ARRAY_TYPE); + } + + if (!preBracketHeaderStack->empty()) + { + currentHeader = preBracketHeaderStack->back(); + preBracketHeaderStack->pop_back(); + } + else + currentHeader = NULL; + } + + // format brackets + if (IS_A(bracketType, ARRAY_TYPE)) + formatArrayBrackets(bracketType, isOpeningArrayBracket); + else + formatBrackets(bracketType); + continue; + } + + if (((previousCommandChar == '{' && isPreviousBracketBlockRelated) + || (previousCommandChar == '}' + && bracketFormatMode != NONE_MODE + && !isImmediatelyPostEmptyBlock + && isPreviousBracketBlockRelated + && !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments + && peekNextChar() != ' ' + && !IS_A(previousBracketType, DEFINITION_TYPE) + && !(ASBeautifier::isJavaStyle && currentChar == ')')) + && !IS_A(bracketTypeStack->back(), DEFINITION_TYPE)) + && (shouldBreakOneLineBlocks + || !IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE))) + { + isCharImmediatelyPostOpenBlock = (previousCommandChar == '{'); + isCharImmediatelyPostCloseBlock = (previousCommandChar == '}'); + + //if (bracketFormatMode != NONE_MODE) + //{ + previousCommandChar = ' '; + isInLineBreak = true; + //} + } + + // reset block handling flags + isImmediatelyPostEmptyBlock = false; + + // look for headers + if (!isInTemplate) + { + if ((newHeader = findHeader(headers)) != NULL) + { + foundClosingHeader = false; + const string *previousHeader; + + // recognize closing headers of do..while, if..else, try..catch..finally + if ((newHeader == &AS_ELSE && currentHeader == &AS_IF) + || (newHeader == &AS_WHILE && currentHeader == &AS_DO) + || (newHeader == &AS_CATCH && currentHeader == &AS_TRY) + || (newHeader == &AS_CATCH && currentHeader == &AS_CATCH) + || (newHeader == &AS_FINALLY && currentHeader == &AS_TRY) + || (newHeader == &AS_FINALLY && currentHeader == &AS_CATCH)) + foundClosingHeader = true; + + previousHeader = currentHeader; + currentHeader = newHeader; + + // If in ATTACH or LINUX bracket modes, attach closing headers (e.g. 'else', 'catch') + // to their preceding bracket, + // But do not perform the attachment if the shouldBreakClosingHeaderBrackets is set! + if (!shouldBreakClosingHeaderBrackets + && foundClosingHeader + && (bracketFormatMode == ATTACH_MODE || bracketFormatMode == BDAC_MODE) + && (shouldBreakOneLineBlocks || !IS_A(previousBracketType, SINGLE_LINE_TYPE)) + && previousNonWSChar == '}') + { + spacePadNum = 0; // don't count as padding + + size_t firstChar = formattedLine.find_first_not_of(" \t"); + if (firstChar != string::npos) // if a blank line does not preceed this + { + isInLineBreak = false; + appendSpacePad(); + } + + if (shouldBreakBlocks) + isAppendPostBlockEmptyLineRequested = false; + } + + // If NONE bracket mode, leave closing headers as they are (e.g. 'else', 'catch') + if (foundClosingHeader && bracketFormatMode == NONE_MODE && previousCommandChar == '}') + { + if (lineBeginsWith('}')) // is closing bracket broken? + { + isInLineBreak = false; + appendSpacePad(); + } + + if (shouldBreakBlocks) + isAppendPostBlockEmptyLineRequested = false; + } + + if (foundClosingHeader && bracketFormatMode == BREAK_MODE && previousCommandChar == '}') + breakLine(); + + //Check if a template definition as been reached, e.g. template<class A> + //if (newHeader == &AS_TEMPLATE) + //{ + // isInTemplate = true; + //} + + // check if the found header is non-paren header + isNonParenHeader = (find(nonParenHeaders.begin(), nonParenHeaders.end(), + newHeader) != nonParenHeaders.end()); + + 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 + if (!isNonParenHeader && charNum < (int) currentLine.length() && !isWhiteSpace(currentLine[charNum+1])) + appendSpacePad(); + + // Signal that a header has been reached + // *** But treat a closing while() (as in do...while) + // as if it where NOT a header since a closing while() + // should never have a block after it! + if (!(foundClosingHeader && currentHeader == &AS_WHILE)) + { + isInHeader = true; + if (isNonParenHeader) + { + isImmediatelyPostHeader = true; + isInHeader = false; + } + } + + if (currentHeader == &AS_IF && previousHeader == &AS_ELSE) + isInLineBreak = false; + + if (shouldBreakBlocks) + { + if (previousHeader == NULL + && !foundClosingHeader + && !isCharImmediatelyPostOpenBlock) + { + isPrependPostBlockEmptyLineRequested = true; + } + + if (currentHeader == &AS_ELSE + || currentHeader == &AS_CATCH + || currentHeader == &AS_FINALLY + || foundClosingHeader) + { + isPrependPostBlockEmptyLineRequested = false; + } + + if (shouldBreakClosingHeaderBlocks + && isCharImmediatelyPostCloseBlock) + { + isPrependPostBlockEmptyLineRequested = true; + } + + } + + continue; + } + else if ((newHeader = findHeader(preDefinitionHeaders)) != NULL + && parenStack->back() == 0) + { + if (newHeader == &AS_NAMESPACE) + foundNamespaceHeader = true; + if (newHeader == &AS_CLASS) + foundClassHeader = true; + foundPreDefinitionHeader = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + if (shouldBreakBlocks) + isPrependPostBlockEmptyLineRequested = true; + + continue; + } + else if ((newHeader = findHeader(preCommandHeaders)) != NULL) + { + if (ASBeautifier::isJavaStyle + || (*newHeader == AS_CONST && previousCommandChar == ')') // 'const' member functions is a command bracket + || *newHeader == AS_EXTERN) + foundPreCommandHeader = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + continue; + } + else if ((newHeader = findHeader(castOperators)) != NULL) + { + foundCastOperator = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + continue; + } + + } + + if (isInLineBreak) // OK to break line here + breakLine(); + + if (previousNonWSChar == '}' || currentChar == ';') + { + if (shouldBreakOneLineStatements && currentChar == ';' + && (shouldBreakOneLineBlocks || !IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE)) + //&& (! bracketFormatMode == NONE_MODE) + ) + { + passedSemicolon = true; + } + + if (shouldBreakBlocks && currentHeader != NULL && parenStack->back() == 0) + { + isAppendPostBlockEmptyLineRequested = true; + } + + if (currentChar != ';') + currentHeader = NULL; + + foundQuestionMark = false; + foundNamespaceHeader = false; + foundClassHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundCastOperator = false; + isInPotentialCalculation = false; + isNonInStatementArray = false; + } + + if (currentChar == ':' + && shouldBreakOneLineStatements + && !foundQuestionMark // not in a ... ? ... : ... sequence + && !foundPreDefinitionHeader // not in a definition block (e.g. class foo : public bar + && previousCommandChar != ')' // not immediately after closing paren of a method header, e.g. ASFormatter::ASFormatter(...) : ASBeautifier(...) + && previousChar != ':' // not part of '::' + && peekNextChar() != ':') // not part of '::' + { + passedColon = true; + if (shouldBreakBlocks) + isPrependPostBlockEmptyLineRequested = true; + } + + if (currentChar == '?') + foundQuestionMark = true; + + // determine if this is a potential calculation + newHeader = findHeader(operators); + + if (newHeader != NULL) + { + if (!isInPotentialCalculation) + { + if (find(assignmentOperators.begin(), assignmentOperators.end(), newHeader) + != assignmentOperators.end()) + { + char peekedChar = peekNextChar(); + isInPotentialCalculation = (newHeader != &AS_RETURN + && !(newHeader == &AS_EQUAL && peekedChar == '*') + && !(newHeader == &AS_EQUAL && peekedChar == '&')); + } + } + } + else + { + // the following are not calculations + if (currentLine.compare(charNum, 3, "new") == 0 && !isLegalNameChar(currentLine[charNum+3])) + isInPotentialCalculation = false; + } + + if (shouldPadOperators && newHeader != NULL) + { + padOperators(newHeader); + continue; + } + + if ((shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens) + && (currentChar == '(' || currentChar == ')')) + { + padParens(); + 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(); + + if (prependEmptyLine // prepend a blank line before this formatted line + && readyFormattedLineLength > 0 + && previousReadyFormattedLineLength > 0) + { + isLineReady = true; // signal that a readyFormattedLine is still waiting + beautifiedLine = beautify(""); + previousReadyFormattedLineLength = 0; + } + else // format the current formatted line + { + isLineReady = false; + beautifiedLine = beautify(readyFormattedLine); + previousReadyFormattedLineLength = readyFormattedLineLength; + lineCommentNoBeautify = lineCommentNoIndent; + lineCommentNoIndent = false; + if (appendOpeningBracket) // insert bracket after this formatted line + { + appendOpeningBracket = false; + isLineReady = true; // signal that a readyFormattedLine is still waiting + readyFormattedLine = "{"; + isPrependPostBlockEmptyLineRequested = false; // next line should not be empty + } + } + + prependEmptyLine = false; + enhance(beautifiedLine); // call the enhancer function + 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; +} + +/** + * set the bracket formatting mode. + * options: + * astyle::NONE_MODE no formatting of brackets. + * astyle::ATTACH_MODE Java, K&R style bracket placement. + * astyle::BREAK_MODE ANSI C/C++ style bracket placement. + * + * @param mode the bracket formatting mode. + */ +void ASFormatter::setBracketFormatMode(BracketMode mode) +{ + bracketFormatMode = mode; +} + +/** + * set closing header bracket breaking mode + * options: + * true brackets just before closing headers (e.g. 'else', 'catch') + * will be broken, even if standard brackets are attached. + * false closing header brackets will be treated as standard brackets. + * + * @param state the closing header bracket breaking mode. + */ +void ASFormatter::setBreakClosingHeaderBracketsMode(bool state) +{ + shouldBreakClosingHeaderBrackets = 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 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 parenthesiss will be padded with spaces around them. +* false statement parenthesiss 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 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 option to break/not break one-line blocks + * + * @param state true = break, false = don't break. + */ +void ASFormatter::setBreakOneLineBlocksMode(bool state) +{ + shouldBreakOneLineBlocks = state; +} + +/** + * set option to break/not break lines consisting of multiple statements. + * + * @param state true = break, false = don't break. + */ +void ASFormatter::setSingleStatementsMode(bool state) +{ + shouldBreakOneLineStatements = 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 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; +} + +/** + * 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]; + +// if (shouldConvertTabs && ch == '\t') +// ch = ' '; + + return ch; +} + +/** +* check if current placement is before a comment or line-comment +* +* @return is before a comment or line-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 + || 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::isBeforeLineEndComment(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) + if (currentLine.find_first_not_of(" \t", endNum + 2) == string::npos) + foundLineEndComment = true; + } + } + return foundLineEndComment; +} + + +/** +* get the next character, increasing the current placement in the process. +* the new character is inserted into the variable currentChar. +* +* @return whether succeded to recieve the new character. +*/ +bool ASFormatter::getNextChar() +{ + isInLineBreak = false; + previousChar = currentChar; + + if (!isWhiteSpace(currentChar)) + { + previousNonWSChar = currentChar; + if (!isInComment && !isInLineComment && !isInQuote + && !isImmediatelyPostComment + && !isImmediatelyPostLineComment + && !isSequenceReached("/*") + && !isSequenceReached("//")) + previousCommandChar = previousNonWSChar; + } + + int currentLineLength = currentLine.length(); + + if (charNum + 1 < currentLineLength + && (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment)) + { + currentChar = currentLine[++charNum]; + + if (shouldConvertTabs && currentChar == '\t') + currentChar = ' '; + + return true; + } + else // end of line has been reached + { + if (sourceIterator->hasMoreLines()) + { + currentLine = sourceIterator->nextLine(); + spacePadNum = 0; + inLineNumber++; + + if (currentLine.length() == 0) + { + currentLine = string(" "); // a null is inserted if this is not done + } + + // unless reading in the first line of the file, + // break a new line. + if (!isVirgin) + isInLineBreak = true; + else + isVirgin = false; + + if (isInLineComment) + isImmediatelyPostLineComment = true; + isInLineComment = false; + + // check if is in preprocessor before line trimming + isImmediatelyPostPreprocessor = isInPreprocessor; + if (previousNonWSChar != '\\') + isInPreprocessor = false; + + trimNewLine(); + currentChar = currentLine[charNum]; + + if (shouldConvertTabs && currentChar == '\t') + currentChar = ' '; + + return true; + } + else + { + endOfCodeReached = true; + return false; + } + } +} + +/** +* 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::trimNewLine() +{ + int len = currentLine.length(); + charNum = 0; + + if (isInComment || isInPreprocessor) + return; + + while (isWhiteSpace(currentLine[charNum]) && charNum + 1 < len) + ++charNum; + + doesLineStartComment = false; + if (isSequenceReached("/*")) + { + charNum = 0; + doesLineStartComment = true; + } +} + +/** + * append a character to the current formatted line. + * Unless disabled (via canBreakLine == false), first check if a + * line-break has been registered, and if so break the + * formatted line, and only then append the character into + * the next formatted line. + * + * @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); +} + +/** + * append a string sequence to the current formatted line. + * Unless disabled (via canBreakLine == false), first check if a + * line-break has been registered, and if so break the + * formatted line, and only then append the sequence into + * the next formatted line. + * + * @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); +} + +/** + * 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++; + } +} + +/** + * 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++; + } +} + +/** + * register a line break for the formatted line. + */ +void ASFormatter::breakLine() +{ + isLineReady = true; + isInLineBreak = false; + spacePadNum = 0; + formattedLineCommentNum = string::npos; + + // queue an empty line prepend request if one exists + prependEmptyLine = isPrependPostBlockEmptyLineRequested; + + readyFormattedLine = formattedLine; + if (isAppendPostBlockEmptyLineRequested) + { + isAppendPostBlockEmptyLineRequested = false; + isPrependPostBlockEmptyLineRequested = true; + } + else + { + isPrependPostBlockEmptyLineRequested = false; + } + + formattedLine = ""; +} + +/** + * check if the currently reached open-bracket (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 bracket. + * + * @return the type of the opened block. + */ +BracketType ASFormatter::getBracketType() const +{ + BracketType returnVal; + + if (foundPreDefinitionHeader) + { + returnVal = DEFINITION_TYPE; + if (foundNamespaceHeader) + returnVal = (BracketType)(returnVal | NAMESPACE_TYPE); + else if (foundClassHeader) + returnVal = (BracketType)(returnVal | CLASS_TYPE); + } + else + { + bool isCommandType = false; + + if (previousNonWSChar != '=') + isCommandType = (foundPreCommandHeader + || (currentHeader != NULL && isNonParenHeader) + || (previousCommandChar == ')') + || (previousCommandChar == ':' && !foundQuestionMark) + || (previousCommandChar == ';') + || ((previousCommandChar == '{' || previousCommandChar == '}') + && isPreviousBracketBlockRelated)); + + returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE); + } + + if (isOneLineBlockReached()) + returnVal = (BracketType)(returnVal | SINGLE_LINE_TYPE); + + TRbracket(returnVal); + return returnVal; +} + +/** + * check if the currently reached '*' or '&' character is + * a pointer-or-reference symbol, or another operator. + * this method takes for granted that the current character + * is either a '*' or '&'. + * + * @return whether current character is a reference-or-pointer + */ +bool ASFormatter::isPointerOrReference() const +{ + bool isPR; + isPR = (!isInPotentialCalculation + || IS_A(bracketTypeStack->back(), DEFINITION_TYPE) + || (!isLegalNameChar(previousNonWSChar) + && previousNonWSChar != ')' + && previousNonWSChar != ']') + ); + + if (!isPR) + { + char nextChar = peekNextChar(); + isPR |= (!isWhiteSpace(nextChar) + && nextChar != '-' + && nextChar != '(' + && nextChar != '[' + && !isLegalNameChar(nextChar)); + } + + return isPR; +} + + +/** + * check if the currently reached '-' character is + * a unary minus + * this method takes for granted that the current character + * is a '-'. + * + * @return whether the current '-' is a unary minus. + */ +bool ASFormatter::isUnaryMinus() const +{ + return ((previousOperator == &AS_RETURN || !isalnum(previousCommandChar)) + && previousCommandChar != '.' + && previousCommandChar != ')' + && previousCommandChar != ']'); +} + + +/** + * check if the currently reached '-' or '+' character is + * part of an exponent, i.e. 0.2E-5. + * this method takes for granted that the current character + * is a '-' or '+'. + * + * @return whether the current '-' is in an exponent. + */ +bool ASFormatter::isInExponent() const +{ + int formattedLineLength = formattedLine.length(); + if (formattedLineLength >= 2) + { + char prevPrevFormattedChar = formattedLine[formattedLineLength - 2]; + char prevFormattedChar = formattedLine[formattedLineLength - 1]; + + return ((prevFormattedChar == 'e' || prevFormattedChar == 'E') + && (prevPrevFormattedChar == '.' || isdigit(prevPrevFormattedChar))); + } + else + return false; +} + +/** + * check if a one-line bracket has been reached, + * i.e. if the currently reached '{' character is closed + * with a complimentry '}' elsewhere on the current line, + *. + * @return has a one-line bracket been reached? + */ +bool ASFormatter::isOneLineBlockReached() const +{ + bool isInComment = false; + bool isInQuote = false; + int bracketCount = 1; + int currentLineLength = currentLine.length(); + char quoteChar = ' '; + + for (int i = charNum + 1; i < currentLineLength; ++i) + { + char ch = currentLine[i]; + + if (isInComment) + { + if (currentLine.compare(i, 2, "*/") == 0) + { + isInComment = false; + ++i; + } + continue; + } + + if (ch == '\\') + { + ++i; + continue; + } + + if (isInQuote) + { + if (ch == quoteChar) + isInQuote = false; + continue; + } + + if (ch == '"' || ch == '\'') + { + isInQuote = true; + quoteChar = ch; + continue; + } + + if (currentLine.compare(i, 2, "//") == 0) + break; + + if (currentLine.compare(i, 2, "/*") == 0) + { + isInComment = true; + ++i; + continue; + } + + if (ch == '{') + ++bracketCount; + else if (ch == '}') + --bracketCount; + + if (bracketCount == 0) + return true; + } + + return false; +} + +/** + * check if one of a set of headers has been reached in the + * current position of the current line. + * + * @return a pointer to the found header. Or a NULL if no header has been reached. + * @param headers a vector of headers. + * @param checkBoundry + */ +const string *ASFormatter::findHeader(const vector<const string*> &headers, bool checkBoundry) +{ + return ASBeautifier::findHeader(currentLine, charNum, headers, checkBoundry); +} + +/** + * check if a line begins with the specified character + * i.e. if the current line begins with a open bracket. + * + * @return true or false + */ +bool ASFormatter::lineBeginsWith(char charToCheck) const +{ + bool beginsWith = false; + size_t i = currentLine.find_first_not_of(" \t"); + + if (i != string::npos) + if (currentLine[i] == charToCheck && (int) i == charNum) + beginsWith = true; + + return beginsWith; +} + +/** + * adjust comment position because of adding or deleting spaces + * the spaces are added or deleted to formattedLine + * spacePadNum contains the adjustment + */ +void ASFormatter::adjustComments(void) +{ + assert(spacePadNum != 0); + assert(currentLine.compare(charNum, 2, "//") == 0 + || currentLine.compare(charNum, 2, "/*") == 0); + + + // block comment must be closed on this line with nothing after it + if (currentLine.compare(charNum, 2, "/*") == 0) + { + size_t endNum = currentLine.find("*/", charNum + 2); + if (endNum == string::npos) + return; + if (currentLine.find_first_not_of(" \t", endNum + 2) != string::npos) + return; + } + + size_t len = formattedLine.length(); + // if spaces were removed, need to add spaces before the comment + if (spacePadNum < 0) + { + int adjust = -spacePadNum; // make the number positive + if (formattedLine[len-1] != '\t') // don't adjust if a tab + formattedLine.append(adjust, ' '); +// else // comment out to avoid compiler warning +// adjust = 0; +// TRcomment(adjust); // trace macro + } + // if spaces were added, need to delete spaces before the comment, if possible + else if (spacePadNum > 0) + { + int adjust = spacePadNum; + if (formattedLine.find_last_not_of(' ') < len - adjust - 1 + && formattedLine[len-1] != '\t') // don't adjust a tab + formattedLine.resize(len - adjust); + // the following are commented out to avoid a Borland compiler warning + //else + // adjust = 0; + TRcomment(-adjust); // trace macro + } +} + +/** + * append the current bracket inside the end of line comments + * currentChar contains the bracket, it will be appended to formattedLine + * formattedLineCommentNum is the comment location on formattedLine + */ +void ASFormatter::appendCharInsideComments(void) +{ + if (formattedLineCommentNum == string::npos // does the comment start on the previous line? + || isBeforeComment()) // does a comment follow on this line? + { + appendCurrentChar(true); // 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) // is the previous line comment only? + { + appendCurrentChar(true); // don't attach + return; + } + beg++; + + // insert the bracket + 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; +} + +/** + * add or remove space padding to operators + * currentChar contains the paren + * 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 != NULL); + + bool shouldPad = (newOperator != &AS_COLON_COLON + && newOperator != &AS_PAREN_PAREN + && newOperator != &AS_BLPAREN_BLPAREN + && newOperator != &AS_PLUS_PLUS + && newOperator != &AS_MINUS_MINUS + && newOperator != &AS_NOT + && newOperator != &AS_BIT_NOT + && newOperator != &AS_ARROW + && newOperator != &AS_OPERATOR + && newOperator != &AS_RETURN + && !(newOperator == &AS_MINUS && isInExponent()) + && !(newOperator == &AS_MINUS // check for negative number + && (previousNonWSChar == '(' + || previousNonWSChar == '=' + || previousNonWSChar == ',')) + && !(newOperator == &AS_PLUS && isInExponent()) + && previousOperator != &AS_OPERATOR + && !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND) + && isPointerOrReference()) + && !(newOperator == &AS_MULT + && (previousNonWSChar == '.' + || previousNonWSChar == '>')) // check for -> + && !((isInTemplate || isCharImmediatelyPostTemplate) + && (newOperator == &AS_LS || newOperator == &AS_GR)) + ); + // pad before operator + if (shouldPad + && !isInBlParen + && !(newOperator == &AS_COLON && !foundQuestionMark) + && newOperator != &AS_SEMICOLON + && newOperator != &AS_COMMA) + appendSpacePad(); + appendSequence(*newOperator); + goForward(newOperator->length() - 1); + + // since this block handles '()' and '[]', + // the parenStack must be updated here accordingly! + if (newOperator == &AS_PAREN_PAREN + || newOperator == &AS_BLPAREN_BLPAREN) + parenStack->back()--; + + currentChar = (*newOperator)[newOperator->length() - 1]; + // pad after operator + // but do not pad after a '-' that is a unary-minus. + if (shouldPad + && !isInBlParen + && !isBeforeComment() + && !(newOperator == &AS_MINUS && isUnaryMinus()) + && !(currentLine.compare(charNum + 1, 1, ";") == 0) + && !(currentLine.compare(charNum + 1, 2, "::") == 0)) + appendSpaceAfter(); + + previousOperator = newOperator; + return; +} + +/** + * 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(void) +{ + assert(shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens); + assert (currentChar == '(' || currentChar == ')'); + + if (currentChar == '(') + { + int spacesOutsideToDelete = formattedLine.length() - 1; + int 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) + { + size_t end = i; + spacesOutsideToDelete -= i; + lastChar = formattedLine[i]; + // was last word a paren header? + int start; // start of the previous word + for (start = i; start > 0; start--) + { + if (isLegalNameChar(formattedLine[start]) || formattedLine[start] == '*') + continue; + start++; + break; + } + string prevWord = formattedLine.substr(start, end-start+1); + // if previous word is a header, it will be a paren header + const string *prevWordH = ASBeautifier::findHeader(formattedLine, start, headers); + if (prevWordH != NULL) + { + prevIsParenHeader = true; + TRxtra(*prevWordH); // trace macro + } + else if (prevWord == "return" // don't unpad return statements + || prevWord == "*") // don't unpad multiply or pointer + { + prevIsParenHeader = true; + TRxtra(prevWord); // trace macro + } + // don't unpad variables + else if (prevWord == "bool" + || prevWord == "int" + || prevWord == "void" + || prevWord == "void*" + || (prevWord.length() >= 6 // check end of word for _t + && prevWord.compare(prevWord.length()-2, 2, "_t") == 0) + || prevWord == "BOOL" + || prevWord == "DWORD" + || prevWord == "HWND" + || prevWord == "INT" + || prevWord == "LPSTR" + || prevWord == "VOID" + || prevWord == "LPVOID" + ) + { + prevIsParenHeader = true; + TRxtra(prevWord); // trace macro + } + } + // do not unpad operators, but leave them if already padded + if (shouldPadParensOutside || prevIsParenHeader) + spacesOutsideToDelete--; + else if (lastChar == '|' // check for || + || lastChar == '&' // check for && + || lastChar == ',' + || (lastChar == '>' && !foundCastOperator) + || lastChar == '<' + || lastChar == '?' + || lastChar == ':' + || lastChar == ';' + || lastChar == '=' + || lastChar == '+' + || lastChar == '-' + || (lastChar == '*' && isInPotentialCalculation) + || lastChar == '/' + || lastChar == '%') + spacesOutsideToDelete--; + + if (spacesOutsideToDelete > 0) + { + formattedLine.erase(i + 1, spacesOutsideToDelete); + spacePadNum -= spacesOutsideToDelete; + } + } + + // pad open paren outside + char peekedCharOutside = peekNextChar(); + 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; + } + } + + // pad open paren inside + char peekedCharInside = peekNextChar(); + if (shouldPadParensInside) + if (!(currentChar == '(' && peekedCharInside == ')')) + appendSpaceAfter(); + + TRunpad('(', spacesOutsideToDelete, spacesInsideToDelete); // trace macro + } + else if (currentChar == ')' /*|| currentChar == ']'*/) + { + int spacesOutsideToDelete = 0; + int spacesInsideToDelete = formattedLine.length(); + + // unpad close paren inside + if (shouldUnPadParens) + { + 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 + if (shouldUnPadParens) + { + // may have end of line comments + size_t j = currentLine.find_first_not_of(" \t", charNum + 1); + if (j != string::npos) + if (currentLine[j] == '[' || currentLine[j] == ']') + spacesOutsideToDelete = j - charNum - 1; + if (shouldPadParensOutside) + spacesOutsideToDelete--; +// spacesOutsideToDelete--; // always leave 1 space + + 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 -> +// && !(currentChar == ']' && peekedCharOutside == '[')) + appendSpaceAfter(); + + TRunpad(')', spacesInsideToDelete, 0 /*spacesOutsideToDelete*/); // trace macro + } + return; +} + +/** + * format brackets as attached or broken + * currentChar contains the bracket + * the brackets 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 bracketType the type of bracket to be formatted. + */ +void ASFormatter::formatBrackets(BracketType bracketType) +{ + assert(!IS_A(bracketType, ARRAY_TYPE)); + assert (currentChar == '{' || currentChar == '}'); + + if (currentChar == '{') + { + parenStack->push_back(0); + } + else if (currentChar == '}') + { + if (!parenStack->empty()) + { + parenStack->pop_back(); + } + } + + if (currentChar == '{') + { + bool bdacBreak = false; + // should a Linux bracket be broken? + if (bracketFormatMode == BDAC_MODE) + { + // always break a class + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], CLASS_TYPE)) + bdacBreak = true; + // break a namespace and the first bracket if a function + else if (bracketTypeStack->size() <= 2) + { + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], NAMESPACE_TYPE) + || IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE)) + bdacBreak = true; + } + // break the first bracket after a namespace if a function + else if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], NAMESPACE_TYPE)) + { + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE)) + bdacBreak = true; + } + // if not C style then break the first bracket after a class if a function + else if (!ASBeautifier::isCStyle) + { + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], CLASS_TYPE) + && IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE)) + bdacBreak = true; + } + } + if (bracketFormatMode == ATTACH_MODE + || (bracketFormatMode == BDAC_MODE && !bdacBreak)) + { + // are there comments before the bracket? + if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment) + { + if ((shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) + && peekNextChar() != '}') + appendCharInsideComments(); + else + appendCurrentChar(true); // don't attach + } + else if (previousCommandChar == '{' + || previousCommandChar == '}' + || previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';' + { + appendCurrentChar(true); // don't attach + } + else + { + size_t firstChar = formattedLine.find_first_not_of(" \t"); + if (firstChar == string::npos) // if a blank line preceeds this + appendCurrentChar(true); // don't attach + else if (shouldBreakOneLineBlocks + || !IS_A(bracketType, SINGLE_LINE_TYPE) + || peekNextChar() == '}') + { + appendSpacePad(); + appendCurrentChar(false); // OK to attach + } + else + appendCurrentChar(true); // don't attach + } + } + else if (bracketFormatMode == BREAK_MODE + || (bracketFormatMode == BDAC_MODE && bdacBreak)) + { + if (isBeforeComment()) + { + // do not break unless comment is at line end + if (isBeforeLineEndComment(charNum)) + { + currentChar = ' '; // remove bracket from current line + appendOpeningBracket = true; // append bracket to following line + } + } + else if (!IS_A(bracketType, SINGLE_LINE_TYPE)) + breakLine(); + else if (shouldBreakOneLineBlocks && peekNextChar() != '}') + breakLine(); + + appendCurrentChar(); + } + else if (bracketFormatMode == NONE_MODE) + { + if (lineBeginsWith('{')) // is opening bracket broken? + appendCurrentChar(true); + else + appendCurrentChar(false); + } + } + else if (currentChar == '}') + { + // mark state of immediately after empty block + // this state will be used for locating brackets that appear immedately AFTER an empty block (e.g. '{} \n}'). + if (previousCommandChar == '{') + isImmediatelyPostEmptyBlock = true; + + if ((!(previousCommandChar == '{' && isPreviousBracketBlockRelated)) // this '{' does not close an empty block + && (shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) // astyle is allowed to break on line blocks + && (!(bracketFormatMode == NONE_MODE && IS_A(bracketType, SINGLE_LINE_TYPE))) + && !isImmediatelyPostEmptyBlock) // this '}' does not immediately follow an empty block + { + breakLine(); + appendCurrentChar(); + } + else + { + if (!isCharImmediatelyPostComment + && !bracketFormatMode == NONE_MODE + && !isImmediatelyPostEmptyBlock) + isInLineBreak = false; + + appendCurrentChar(); + + //if (!bracketFormatMode == NONE_MODE) + // if ((shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) + // && !(currentChar == '}' && peekNextChar() == ';')) // fixes }; placed on separate lines + // shouldBreakLineAfterComments = true; + } + + if (shouldBreakBlocks) + { + isAppendPostBlockEmptyLineRequested = true; + } + } + return; +} + +/** + * format array brackets as attached or broken + * determine if the brackets can have an inStatement indent + * currentChar contains the bracket + * the brackets 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 bracketType the type of bracket to be formatted, must be an ARRAY_TYPE. + * @param isOpeningArrayBracket indicates if this is the opening bracket for the array block. + */ +void ASFormatter::formatArrayBrackets(BracketType bracketType, bool isOpeningArrayBracket) +{ + assert(IS_A(bracketType, ARRAY_TYPE)); + assert (currentChar == '{' || currentChar == '}'); + + if (currentChar == '{') + { + // is this the first opening bracket in the array? + if (isOpeningArrayBracket) + { + if (bracketFormatMode == ATTACH_MODE || bracketFormatMode == BDAC_MODE) + { + // don't attach to a preprocessor directive + if (isImmediatelyPostPreprocessor) + appendCurrentChar(true); // don't attach + // are there comments before the bracket? + else if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment) + { + appendCharInsideComments(); + } + else + { + // if bracket is broken or not an assignment + if (lineBeginsWith('{') || previousNonWSChar != '=') + appendSpacePad(); + appendCurrentChar(false); // OK to attach + } + } + else if (bracketFormatMode == BREAK_MODE) + { + if (isWhiteSpace(peekNextChar())) + breakLine(); + else if (isBeforeComment()) + { + // do not break unless comment is at line end + if (isBeforeLineEndComment(charNum)) + { + currentChar = ' '; // remove bracket from current line + appendOpeningBracket = true; // append bracket to following line + } + } + appendCurrentChar(); + } + else if (bracketFormatMode == NONE_MODE) + { + if (lineBeginsWith('{')) // is opening bracket broken? + appendCurrentChar(); + else + appendCurrentChar(false); + } + } + else + appendCurrentChar(); // not the first opening bracket - don't change + + // if an opening bracket ends the line there will be no inStatement indent + char nextChar = peekNextChar(); + if (isWhiteSpace(nextChar) + || isBeforeLineEndComment(charNum) + || nextChar == '{') + isNonInStatementArray = true; + if (isNonInStatementArray) + TRarray('x'); + else + TRarray(' '); + + } + else if (currentChar == '}') + { + // does this close the first opening bracket in the array? + if (isOpeningArrayBracket && !IS_A(bracketType, SINGLE_LINE_TYPE) ) + { + breakLine(); + appendCurrentChar(); + } + else + appendCurrentChar(); + } +} + + +} // end namespace astyle |