You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2198 lines
61 KiB
2198 lines
61 KiB
15 years ago
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||
|
*
|
||
|
* 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
|