kde-extraapps/kdevelop/formatters/astyle/lib/ASFormatter.cpp
2015-07-26 14:23:17 +03:00

5539 lines
157 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ASFormatter.cpp
*
* Copyright (C) 2006-2011 by Jim Pattee <jimp03@email.com>
* Copyright (C) 1998-2002 by Tal Davidson
* <http://www.gnu.org/licenses/lgpl-3.0.html>
*
* 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>
*
* Artistic Style 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 3 of the License, or
* (at your option) any later version.
*
* Artistic Style 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 Artistic Style. If not, see <http://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#include "astyle.h"
#include <algorithm>
#include <fstream>
#include <iostream>
namespace astyle
{
/**
* Constructor of ASFormatter
*/
ASFormatter::ASFormatter()
{
sourceIterator = NULL;
enhancer = new ASEnhancer;
preBracketHeaderStack = NULL;
bracketTypeStack = NULL;
parenStack = NULL;
structStack = NULL;
lineCommentNoIndent = false;
formattingStyle = STYLE_NONE;
bracketFormatMode = NONE_MODE;
pointerAlignment = PTR_ALIGN_NONE;
referenceAlignment = REF_SAME_AS_PTR;
lineEnd = LINEEND_DEFAULT;
maxCodeLength = string::npos;
shouldPadOperators = false;
shouldPadParensOutside = false;
shouldPadFirstParen = false;
shouldPadParensInside = false;
shouldPadHeader = false;
shouldUnPadParens = false;
shouldAttachClosingBracket = false;
shouldBreakOneLineBlocks = true;
shouldBreakOneLineStatements = true;
shouldConvertTabs = false;
shouldIndentCol1Comments = false;
shouldCloseTemplates = false;
shouldBreakBlocks = false;
shouldBreakClosingHeaderBlocks = false;
shouldBreakClosingHeaderBrackets = false;
shouldDeleteEmptyLines = false;
shouldBreakElseIfs = false;
shouldBreakLineAfterLogical = false;
shouldAddBrackets = false;
shouldAddOneLineBrackets = false;
// initialize ASFormatter member vectors
formatterFileType = 9; // reset to an invalid type
headers = new vector<const string*>;
nonParenHeaders = new vector<const string*>;
preDefinitionHeaders = new vector<const string*>;
preCommandHeaders = new vector<const string*>;
operators = new vector<const string*>;
assignmentOperators = new vector<const string*>;
castOperators = new vector<const string*>;
}
/**
* Destructor of ASFormatter
*/
ASFormatter::~ASFormatter()
{
// delete ASFormatter stack vectors
deleteContainer(preBracketHeaderStack);
deleteContainer(bracketTypeStack);
deleteContainer(parenStack);
deleteContainer(structStack);
// delete ASFormatter member vectors
formatterFileType = 9; // reset to an invalid type
delete headers;
delete nonParenHeaders;
delete preDefinitionHeaders;
delete preCommandHeaders;
delete operators;
delete assignmentOperators;
delete castOperators;
// delete ASBeautifier member vectors
// must be done when the ASFormatter object is deleted (not ASBeautifier)
ASBeautifier::deleteBeautifierVectors();
delete enhancer;
}
/**
* initialize the ASFormatter.
*
* init() should be called every time a ASFormatter object is to start
* formatting a NEW source file.
* init() recieves a pointer to a ASSourceIterator object that will be
* used to iterate through the source code.
*
* @param sourceIterator a pointer to the ASSourceIterator or ASStreamIterator object.
*/
void ASFormatter::init(ASSourceIterator* si)
{
buildLanguageVectors();
fixOptionVariableConflicts();
ASBeautifier::init(si);
enhancer->init(getFileType(),
getIndentLength(),
getTabLength(),
getIndentString() == "\t" ? true : false,
getForceTabIndentation(),
getCaseIndent(),
getPreprocessorIndent(),
getEmptyLineFill());
sourceIterator = si;
initContainer(preBracketHeaderStack, new vector<const string*>);
initContainer(parenStack, new vector<int>);
initContainer(structStack, new vector<bool>);
parenStack->push_back(0); // parenStack must contain this default entry
initContainer(bracketTypeStack, new vector<BracketType>);
bracketTypeStack->push_back(NULL_TYPE); // bracketTypeStack must contain this default entry
clearFormattedLineSplitPoints();
currentHeader = NULL;
currentLine = "";
readyFormattedLine = "";
formattedLine = "";
currentChar = ' ';
previousChar = ' ';
previousCommandChar = ' ';
previousNonWSChar = ' ';
quoteChar = '"';
charNum = 0;
checksumIn = 0;
checksumOut = 0;
currentLineFirstBracketNum = string::npos;
formattedLineCommentNum = 0;
leadingSpaces = 0;
previousReadyFormattedLineLength = string::npos;
preprocBracketTypeStackSize = 0;
spacePadNum = 0;
nextLineSpacePadNum = 0;
templateDepth = 0;
traceLineNumber = 0;
horstmannIndentChars = 0;
tabIncrementIn = 0;
previousBracketType = NULL_TYPE;
previousOperator = NULL;
isVirgin = true;
isInLineComment = false;
isInComment = false;
isInCommentStartLine = false;
noTrimCommentContinuation = false;
isInPreprocessor = false;
isInPreprocessorBeautify = false;
doesLineStartComment = false;
lineEndsInCommentOnly = false;
lineIsLineCommentOnly = false;
lineIsEmpty = false;
isImmediatelyPostCommentOnly = false;
isImmediatelyPostEmptyLine = false;
isInQuote = false;
isInVerbatimQuote = false;
haveLineContinuationChar = false;
isInQuoteContinuation = false;
isInBlParen = false;
isSpecialChar = false;
isNonParenHeader = false;
foundNamespaceHeader = false;
foundClassHeader = false;
foundStructHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundCastOperator = false;
foundQuestionMark = false;
isInLineBreak = false;
endOfAsmReached = false;
endOfCodeReached = false;
isInEnum = false;
isInExecSQL = false;
isInAsm = false;
isInAsmOneLine = false;
isInAsmBlock = false;
isLineReady = false;
isPreviousBracketBlockRelated = false;
isInPotentialCalculation = false;
shouldReparseCurrentChar = false;
needHeaderOpeningBracket = false;
shouldBreakLineAtNextChar = false;
shouldKeepLineUnbroken = false;
passedSemicolon = false;
passedColon = false;
isImmediatelyPostNonInStmt = false;
isCharImmediatelyPostNonInStmt = false;
isInTemplate = false;
isImmediatelyPostComment = false;
isImmediatelyPostLineComment = false;
isImmediatelyPostEmptyBlock = false;
isImmediatelyPostPreprocessor = false;
isImmediatelyPostReturn = false;
isImmediatelyPostThrow = false;
isImmediatelyPostOperator = false;
isImmediatelyPostTemplate = false;
isImmediatelyPostPointerOrReference = false;
isCharImmediatelyPostReturn = false;
isCharImmediatelyPostThrow = false;
isCharImmediatelyPostOperator = false;
isCharImmediatelyPostComment = false;
isPreviousCharPostComment = false;
isCharImmediatelyPostLineComment = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
isCharImmediatelyPostTemplate = false;
isCharImmediatelyPostPointerOrReference = false;
breakCurrentOneLineBlock = false;
isInHorstmannRunIn = false;
currentLineBeginsWithBracket = false;
isPrependPostBlockEmptyLineRequested = false;
isAppendPostBlockEmptyLineRequested = false;
prependEmptyLine = false;
appendOpeningBracket = false;
foundClosingHeader = false;
isImmediatelyPostHeader = false;
isInHeader = false;
isInCase = false;
isJavaStaticConstructor = false;
}
/**
* build vectors for each programing language
* depending on the file extension.
*/
void ASFormatter::buildLanguageVectors()
{
if (getFileType() == formatterFileType) // don't build unless necessary
return;
formatterFileType = getFileType();
headers->clear();
nonParenHeaders->clear();
preDefinitionHeaders->clear();
preCommandHeaders->clear();
operators->clear();
assignmentOperators->clear();
castOperators->clear();
ASResource::buildHeaders(headers, getFileType());
ASResource::buildNonParenHeaders(nonParenHeaders, getFileType());
ASResource::buildPreDefinitionHeaders(preDefinitionHeaders, getFileType());
ASResource::buildPreCommandHeaders(preCommandHeaders, getFileType());
if (operators->empty())
ASResource::buildOperators(operators, getFileType());
if (assignmentOperators->empty())
ASResource::buildAssignmentOperators(assignmentOperators);
if (castOperators->empty())
ASResource::buildCastOperators(castOperators);
}
/**
* set the variables for each preefined style.
* this will override any previous settings.
*/
void ASFormatter::fixOptionVariableConflicts()
{
if (formattingStyle == STYLE_ALLMAN)
{
setBracketFormatMode(BREAK_MODE);
}
else if (formattingStyle == STYLE_JAVA)
{
setBracketFormatMode(ATTACH_MODE);
}
else if (formattingStyle == STYLE_KR)
{
setBracketFormatMode(LINUX_MODE);
}
else if (formattingStyle == STYLE_STROUSTRUP)
{
setBracketFormatMode(STROUSTRUP_MODE);
}
else if (formattingStyle == STYLE_WHITESMITH)
{
setBracketFormatMode(BREAK_MODE);
setBracketIndent(true);
setClassIndent(true);
setSwitchIndent(true);
}
else if (formattingStyle == STYLE_BANNER)
{
setBracketFormatMode(ATTACH_MODE);
setBracketIndent(true);
setClassIndent(true);
setSwitchIndent(true);
}
else if (formattingStyle == STYLE_GNU)
{
setBracketFormatMode(BREAK_MODE);
setBlockIndent(true);
}
else if (formattingStyle == STYLE_LINUX)
{
setBracketFormatMode(LINUX_MODE);
// always for Linux style
setMinConditionalIndentOption(MINCOND_ONEHALF);
}
else if (formattingStyle == STYLE_HORSTMANN)
{
setBracketFormatMode(RUN_IN_MODE);
setSwitchIndent(true);
}
else if (formattingStyle == STYLE_1TBS)
{
setBracketFormatMode(LINUX_MODE);
setAddBracketsMode(true);
}
else if (formattingStyle == STYLE_PICO)
{
setBracketFormatMode(RUN_IN_MODE);
setAttachClosingBracket(true);
setSwitchIndent(true);
setBreakOneLineBlocksMode(false);
setSingleStatementsMode(false);
// add-brackets won't work for pico, but it could be fixed if necessary
// both options should be set to true
if (shouldAddBrackets)
shouldAddOneLineBrackets = true;
}
else if (formattingStyle == STYLE_LISP)
{
setBracketFormatMode(ATTACH_MODE);
setAttachClosingBracket(true);
setSingleStatementsMode(false);
// add-one-line-brackets won't work for lisp
// only shouldAddBrackets should be set to true
if (shouldAddOneLineBrackets)
{
shouldAddBrackets = true;
shouldAddOneLineBrackets = false;
}
}
setMinConditionalIndentLength();
// if not set by indent=force-tab-x set equal to indentLength
if (!getTabLength())
setDefaultTabLength();
// add-one-line-brackets implies keep-one-line-blocks
if (shouldAddOneLineBrackets)
setBreakOneLineBlocksMode(false);
}
/**
* get the next formatted line.
*
* @return formatted line.
*/
string ASFormatter::nextLine()
{
const string* newHeader;
bool isInVirginLine = isVirgin;
isCharImmediatelyPostComment = false;
isPreviousCharPostComment = false;
isCharImmediatelyPostLineComment = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
isCharImmediatelyPostTemplate = false;
traceLineNumber++;
while (!isLineReady)
{
if (shouldReparseCurrentChar)
shouldReparseCurrentChar = false;
else if (!getNextChar())
{
breakLine();
continue;
}
else // stuff to do when reading a new character...
{
// make sure that a virgin '{' at the begining of the file will be treated as a block...
if (isInVirginLine && currentChar == '{'
&& currentLineBeginsWithBracket // lineBeginsWith('{')
&& previousCommandChar == ' ')
previousCommandChar = '{';
if (isInHorstmannRunIn)
isInLineBreak = false;
if (!isWhiteSpace(currentChar))
isInHorstmannRunIn = false;
isPreviousCharPostComment = isCharImmediatelyPostComment;
isCharImmediatelyPostComment = false;
isCharImmediatelyPostTemplate = false;
isCharImmediatelyPostReturn = false;
isCharImmediatelyPostThrow = false;
isCharImmediatelyPostOperator = false;
isCharImmediatelyPostPointerOrReference = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
}
if (shouldBreakLineAtNextChar)
{
if (isWhiteSpace(currentChar) && !lineIsEmpty)
continue;
isInLineBreak = true;
shouldBreakLineAtNextChar = false;
}
if (isInExecSQL && !passedSemicolon)
{
if (currentChar == ';')
passedSemicolon = true;
appendCurrentChar();
continue;
}
if (isInLineComment)
{
formatLineCommentBody();
continue;
}
else if (isInComment)
{
formatCommentBody();
continue;
}
// not in line comment or comment
else if (isInQuote)
{
formatQuoteBody();
continue;
}
if (isSequenceReached("//"))
{
formatLineCommentOpener();
testForTimeToSplitFormattedLine();
continue;
}
else if (isSequenceReached("/*"))
{
formatCommentOpener();
testForTimeToSplitFormattedLine();
continue;
}
else if (currentChar == '"' || currentChar == '\'')
{
formatQuoteOpener();
testForTimeToSplitFormattedLine();
continue;
}
// treat these preprocessor statements as a line comment
else if (currentChar =='#')
{
string preproc = trim(currentLine.c_str() + charNum + 1);
if (preproc.compare(0, 6, "region") == 0
|| preproc.compare(0, 9, "endregion") == 0
|| preproc.compare(0, 5, "error") == 0
|| preproc.compare(0, 7, "warning") == 0)
{
// check for horstmann run-in
if (formattedLine.length() > 0 && formattedLine[0] == '{')
{
isInLineBreak = true;
isInHorstmannRunIn = false;
}
isInLineComment = true;
appendCurrentChar();
continue;
}
}
if (isInPreprocessor)
{
appendCurrentChar();
continue;
}
if (isInTemplate && shouldCloseTemplates)
{
if (previousCommandChar == '<' && isWhiteSpace(currentChar))
continue;
if (isWhiteSpace(currentChar) && peekNextChar() == '>')
continue;
}
// handle white space - needed to simplify the rest.
if (isWhiteSpace(currentChar))
{
appendCurrentChar();
continue;
}
/* not in MIDDLE of quote or comment or SQL or white-space of any type ... */
// check if in preprocessor
// ** isInPreprocessor will be automatically reset at the begining
// of a new line in getnextChar()
if (currentChar == '#')
{
isInPreprocessor = true;
// check for horstmann run-in
if (formattedLine.length() > 0 && formattedLine[0] == '{')
{
isInLineBreak = true;
isInHorstmannRunIn = false;
}
processPreprocessor();
// need to fall thru here to reset the variables
}
/* not in preprocessor ... */
if (isImmediatelyPostComment)
{
isImmediatelyPostComment = false;
isCharImmediatelyPostComment = true;
}
if (isImmediatelyPostLineComment)
{
isImmediatelyPostLineComment = false;
isCharImmediatelyPostLineComment = true;
}
if (isImmediatelyPostReturn)
{
isImmediatelyPostReturn = false;
isCharImmediatelyPostReturn = true;
}
if (isImmediatelyPostThrow)
{
isImmediatelyPostThrow = false;
isCharImmediatelyPostThrow = true;
}
if (isImmediatelyPostOperator)
{
isImmediatelyPostOperator = false;
isCharImmediatelyPostOperator = true;
}
if (isImmediatelyPostTemplate)
{
isImmediatelyPostTemplate = false;
isCharImmediatelyPostTemplate = true;
}
if (isImmediatelyPostPointerOrReference)
{
isImmediatelyPostPointerOrReference = false;
isCharImmediatelyPostPointerOrReference = true;
}
// reset isImmediatelyPostHeader information
if (isImmediatelyPostHeader)
{
// should brackets be added
if (currentChar != '{' && shouldAddBrackets)
{
bool bracketsAdded = addBracketsToStatement();
if (bracketsAdded && !shouldAddOneLineBrackets)
{
size_t firstText = currentLine.find_first_not_of(" \t");
assert(firstText != string::npos);
if ((int) firstText == charNum)
breakCurrentOneLineBlock = true;
}
}
// 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
&& isOkToBreakBlock(bracketTypeStack->back()))
{
// if may break 'else if()'s, then simply break the line
if (shouldBreakElseIfs)
isInLineBreak = true;
}
isImmediatelyPostHeader = false;
}
if (passedSemicolon) // need to break the formattedLine
{
passedSemicolon = false;
if (parenStack->back() == 0 && !isCharImmediatelyPostComment && currentChar != ';') // allow ;;
{
// does a one-line statement have ending comments?
if (isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE))
{
size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACKET);
assert(blockEnd != string::npos);
// move ending comments to this formattedLine
if (isBeforeAnyLineEndComment(blockEnd))
{
size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1);
assert(commentStart != string::npos);
assert((currentLine.compare(commentStart, 2, "//") == 0)
|| (currentLine.compare(commentStart, 2, "/*") == 0));
size_t commentLength = currentLine.length() - commentStart;
formattedLine.append(getIndentLength() - 1, ' ');
formattedLine.append(currentLine, commentStart, commentLength);
currentLine.erase(commentStart, commentLength);
testForTimeToSplitFormattedLine();
}
}
isInExecSQL = false;
shouldReparseCurrentChar = true;
isInLineBreak = true;
if (needHeaderOpeningBracket)
{
isCharImmediatelyPostCloseBlock = true;
needHeaderOpeningBracket = false;
}
continue;
}
}
if (passedColon)
{
passedColon = false;
if (parenStack->back() == 0 && !isBeforeAnyComment())
{
shouldReparseCurrentChar = true;
isInLineBreak = true;
continue;
}
}
// Check if in template declaration, e.g. foo<bar> or foo<bar,fig>
if (!isInTemplate && currentChar == '<')
{
checkIfTemplateOpener();
}
// handle parenthesies
if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<'))
{
parenStack->back()++;
if (currentChar == '[')
isInBlParen = true;
}
else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>'))
{
foundPreCommandHeader = false;
parenStack->back()--;
if (isInTemplate && currentChar == '>')
{
templateDepth--;
if (templateDepth == 0)
{
isInTemplate = false;
isImmediatelyPostTemplate = true;
}
}
// check if this parenthesis closes a header, e.g. if (...), while (...)
if (isInHeader && parenStack->back() == 0)
{
isInHeader = false;
isImmediatelyPostHeader = true;
foundQuestionMark = false;
}
if (currentChar == ']')
isInBlParen = false;
if (currentChar == ')')
{
foundCastOperator = false;
if (parenStack->back() == 0)
endOfAsmReached = true;
}
}
// handle brackets
if (currentChar == '{' || currentChar == '}')
{
// if appendOpeningBracket this was already done for the original bracket
if (currentChar == '{' && !appendOpeningBracket)
{
BracketType newBracketType = getBracketType();
foundNamespaceHeader = false;
foundClassHeader = false;
foundStructHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
isInPotentialCalculation = false;
isInEnum = false;
isJavaStaticConstructor = false;
isCharImmediatelyPostNonInStmt = false;
needHeaderOpeningBracket = false;
isPreviousBracketBlockRelated = !isBracketType(newBracketType, ARRAY_TYPE);
bracketTypeStack->push_back(newBracketType);
preBracketHeaderStack->push_back(currentHeader);
currentHeader = NULL;
structStack->push_back(isInIndentableStruct);
if (isBracketType(newBracketType, STRUCT_TYPE) && isCStyle())
isInIndentableStruct = isStructAccessModified(currentLine, charNum);
else
isInIndentableStruct = false;
}
// this must be done before the bracketTypeStack is popped
BracketType bracketType = bracketTypeStack->back();
bool isOpeningArrayBracket = (isBracketType(bracketType, ARRAY_TYPE)
&& bracketTypeStack->size() >= 2
&& !isBracketType((*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 no need for the post block empty line.
isAppendPostBlockEmptyLineRequested = false;
breakCurrentOneLineBlock = false;
isInAsmBlock = false;
// added for release 1.24
// TODO: remove at the appropriate time
assert(isInAsm == false || endOfAsmReached == true);
assert(isInAsmOneLine == false);
assert(isInQuote == false);
isInAsm = isInAsmOneLine = isInQuote = false;
// end remove
if (bracketTypeStack->size() > 1)
{
previousBracketType = bracketTypeStack->back();
bracketTypeStack->pop_back();
isPreviousBracketBlockRelated = !isBracketType(bracketType, ARRAY_TYPE);
}
else
{
previousBracketType = NULL_TYPE;
isPreviousBracketBlockRelated = false;
}
if (!preBracketHeaderStack->empty())
{
currentHeader = preBracketHeaderStack->back();
preBracketHeaderStack->pop_back();
}
else
currentHeader = NULL;
if (!structStack->empty())
{
isInIndentableStruct = structStack->back();
structStack->pop_back();
}
else
isInIndentableStruct = false;
if (isNonInStatementArray
&& (!isBracketType(bracketTypeStack->back(), ARRAY_TYPE) // check previous bracket
|| peekNextChar() == ';')) // check for "};" added V2.01
isImmediatelyPostNonInStmt = true;
}
// format brackets
appendOpeningBracket = false;
if (isBracketType(bracketType, ARRAY_TYPE))
{
formatArrayBrackets(bracketType, isOpeningArrayBracket);
}
else
{
if (currentChar == '{')
formatOpeningBracket(bracketType);
else
formatClosingBracket(bracketType);
}
continue;
}
if ((((previousCommandChar == '{' && isPreviousBracketBlockRelated)
|| ((previousCommandChar == '}'
&& !isImmediatelyPostEmptyBlock
&& isPreviousBracketBlockRelated
&& !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments
&& peekNextChar() != ' '
&& !isBracketType(previousBracketType, DEFINITION_TYPE))
&& !isBracketType(bracketTypeStack->back(), DEFINITION_TYPE)))
&& isOkToBreakBlock(bracketTypeStack->back()))
// check for array
|| (previousCommandChar == '{' // added 9/30/2010
&& isBracketType(bracketTypeStack->back(), ARRAY_TYPE)
&& !isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE)
&& isNonInStatementArray))
{
isCharImmediatelyPostOpenBlock = (previousCommandChar == '{');
isCharImmediatelyPostCloseBlock = (previousCommandChar == '}');
if (isCharImmediatelyPostOpenBlock
&& !isCharImmediatelyPostComment
&& !isCharImmediatelyPostLineComment)
{
previousCommandChar = ' ';
if (bracketFormatMode == NONE_MODE)
{
if (shouldBreakOneLineBlocks
&& isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE))
isInLineBreak = true;
else if (currentLineBeginsWithBracket)
formatRunIn();
else
breakLine();
}
else if (bracketFormatMode == RUN_IN_MODE
&& currentChar != '#')
formatRunIn();
else
isInLineBreak = true;
}
else if (isCharImmediatelyPostCloseBlock
&& shouldBreakOneLineStatements
&& (isLegalNameChar(currentChar) && currentChar != '.')
&& !isCharImmediatelyPostComment)
{
previousCommandChar = ' ';
isInLineBreak = true;
}
}
// reset block handling flags
isImmediatelyPostEmptyBlock = false;
// look for headers
bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum);
if (isPotentialHeader && !isInTemplate)
{
isNonParenHeader = false;
foundClosingHeader = false;
newHeader = findHeader(headers);
if (newHeader != NULL)
{
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)
|| (newHeader == &_AS_FINALLY && currentHeader == &_AS_TRY)
|| (newHeader == &_AS_EXCEPT && currentHeader == &_AS_TRY)
|| (newHeader == &AS_SET && currentHeader == &AS_GET)
|| (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
foundClosingHeader = true;
previousHeader = currentHeader;
currentHeader = newHeader;
needHeaderOpeningBracket = true;
if (foundClosingHeader && previousNonWSChar == '}')
{
if (isOkToBreakBlock(bracketTypeStack->back()))
isLineBreakBeforeClosingHeader();
// get the adjustment for a comment following the closing header
if (isInLineBreak)
nextLineSpacePadNum = getNextLineCommentAdjustment();
else
spacePadNum = getCurrentLineCommentAdjustment();
}
// check if the found header is non-paren header
isNonParenHeader = findHeader(nonParenHeaders) != NULL;
// join 'else if' statements
if (currentHeader == &AS_IF && previousHeader == &AS_ELSE && isInLineBreak
&& !shouldBreakElseIfs && !isCharImmediatelyPostLineComment)
{
// 'else' must be last thing on the line, but must not be #else
size_t start = formattedLine.length() >= 6 ? formattedLine.length()-6 : 0;
if (formattedLine.find("else", start) != string::npos
&& formattedLine.find("#else", start) == string::npos)
{
appendSpacePad();
isInLineBreak = false;
}
}
appendSequence(*currentHeader);
goForward(currentHeader->length() - 1);
// if a paren-header is found add a space after it, if needed
// this checks currentLine, appendSpacePad() checks formattedLine
// in 'case' and C# 'catch' can be either a paren or non-paren header
if (shouldPadHeader
&& (!isNonParenHeader
|| (currentHeader == &AS_CASE && peekNextChar() == '(')
|| (currentHeader == &AS_CATCH && peekNextChar() == '('))
&& charNum < (int) currentLine.length() - 1 && !isWhiteSpace(currentLine[charNum+1]))
appendSpacePad();
// Signal that a header has been reached
// *** But treat a closing while() (as in do...while)
// as if it were NOT a header since a closing while()
// should never have a block after it!
if (currentHeader != &AS_CASE
&& !(foundClosingHeader && currentHeader == &AS_WHILE))
{
isInHeader = true;
// in C# 'catch' and 'delegate' can be a paren or non-paren header
if (isNonParenHeader && !isSharpStyleWithParen(currentHeader))
{
isImmediatelyPostHeader = true;
isInHeader = false;
}
}
if (shouldBreakBlocks
&& isOkToBreakBlock(bracketTypeStack->back()))
{
if (previousHeader == NULL
&& !foundClosingHeader
&& !isCharImmediatelyPostOpenBlock
&& !isImmediatelyPostCommentOnly)
{
isPrependPostBlockEmptyLineRequested = true;
}
if (currentHeader == &AS_ELSE
|| currentHeader == &AS_CATCH
|| currentHeader == &AS_FINALLY
|| foundClosingHeader)
{
isPrependPostBlockEmptyLineRequested = false;
}
if (shouldBreakClosingHeaderBlocks
&& isCharImmediatelyPostCloseBlock
&& !isImmediatelyPostCommentOnly
&& currentHeader != &AS_WHILE) // closing do-while block
{
isPrependPostBlockEmptyLineRequested = true;
}
}
if (currentHeader == &AS_CASE
|| currentHeader == &AS_DEFAULT)
isInCase = true;
continue;
}
else if ((newHeader = findHeader(preDefinitionHeaders)) != NULL
&& parenStack->back() == 0)
{
if (newHeader == &AS_NAMESPACE)
foundNamespaceHeader = true;
if (newHeader == &AS_CLASS)
foundClassHeader = true;
if (newHeader == &AS_STRUCT)
foundStructHeader = true;
if (newHeader == &AS_INTERFACE)
foundInterfaceHeader = true;
foundPreDefinitionHeader = true;
appendSequence(*newHeader);
goForward(newHeader->length() - 1);
continue;
}
else if ((newHeader = findHeader(preCommandHeaders)) != NULL)
{
foundPreCommandHeader = true;
// fall thru here for a 'const' that is not a precommand header
}
else if ((newHeader = findHeader(castOperators)) != NULL)
{
foundCastOperator = true;
appendSequence(*newHeader);
goForward(newHeader->length() - 1);
continue;
}
} // (isPotentialHeader && !isInTemplate)
if (isInLineBreak) // OK to break line here
{
breakLine();
if (isInVirginLine) // adjust for the first line
{
lineCommentNoBeautify = lineCommentNoIndent;
lineCommentNoIndent = false;
}
}
if (previousNonWSChar == '}' || currentChar == ';')
{
if (currentChar == ';')
{
if (((shouldBreakOneLineStatements
|| isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE))
&& isOkToBreakBlock(bracketTypeStack->back()))
&& !(shouldAttachClosingBracket && peekNextChar() == '}'))
{
passedSemicolon = true;
}
// append post block empty line for unbracketed header
if (shouldBreakBlocks
&& currentHeader != NULL
&& currentHeader != &AS_CASE
&& currentHeader != &AS_DEFAULT
&& parenStack->back() == 0)
{
isAppendPostBlockEmptyLineRequested = true;
}
}
// end of block if a closing bracket was found
// or an opening bracket was not found (';' closes)
if (currentChar != ';'
|| (needHeaderOpeningBracket && parenStack->back() == 0))
currentHeader = NULL;
foundQuestionMark = false;
foundNamespaceHeader = false;
foundClassHeader = false;
foundStructHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundCastOperator = false;
isInPotentialCalculation = false;
isSharpAccessor = false;
isSharpDelegate = false;
isInEnum = false;
isInExtern = false;
nonInStatementBracket = 0;
}
if (currentChar == ':')
{
if (isInCase
&& previousChar != ':' // not part of '::'
&& peekNextChar() != ':') // not part of '::'
{
isInCase = false;
if (shouldBreakOneLineStatements)
passedColon = true;
}
else if (isCStyle() // for C/C++ only
&& 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 '::'
&& !isDigit(peekNextChar()) // not a bit field
&& !isInEnum // not an enum with a base type
&& !isInAsm // not in extended assembler
&& !isInAsmOneLine // not in extended assembler
&& !isInAsmBlock) // not in extended assembler
{
passedColon = true;
}
}
if (currentChar == '?')
foundQuestionMark = true;
if (isPotentialHeader && !isInTemplate)
{
if (findKeyword(currentLine, charNum, AS_NEW))
isInPotentialCalculation = false;
if (findKeyword(currentLine, charNum, AS_RETURN))
{
isInPotentialCalculation = true; // return is the same as an = sign
isImmediatelyPostReturn = true;
}
if (isCStyle()
&& findKeyword(currentLine, charNum, AS_THROW)
&& previousCommandChar != ')'
&& !foundPreCommandHeader) // 'const' throw()
isImmediatelyPostThrow = true;
if (findKeyword(currentLine, charNum, AS_OPERATOR))
isImmediatelyPostOperator = true;
if (isCStyle() && findKeyword(currentLine, charNum, AS_ENUM))
isInEnum = true;
if (isCStyle() && findKeyword(currentLine, charNum, AS_EXTERN))
isInExtern = true;
if (isCStyle() && isExecSQL(currentLine, charNum))
isInExecSQL = true;
if (isCStyle())
{
if (findKeyword(currentLine, charNum, AS_ASM)
|| findKeyword(currentLine, charNum, AS__ASM__))
{
isInAsm = true;
}
else if (findKeyword(currentLine, charNum, AS_MS_ASM) // microsoft specific
|| findKeyword(currentLine, charNum, AS_MS__ASM))
{
int index = 4;
if (peekNextChar() == '_') // check for __asm
index = 5;
char peekedChar = ASBase::peekNextChar(currentLine, charNum + index);
if (peekedChar == '{' || peekedChar == ' ')
isInAsmBlock = true;
else
isInAsmOneLine = true;
}
}
if (isJavaStyle()
&& (findKeyword(currentLine, charNum, AS_STATIC)
&& isNextCharOpeningBracket(charNum + 6)))
isJavaStaticConstructor = true;
if (isSharpStyle()
&& (findKeyword(currentLine, charNum, AS_DELEGATE)
|| findKeyword(currentLine, charNum, AS_UNCHECKED)))
isSharpDelegate = true;
// append the entire name
string name = getCurrentWord(currentLine, charNum);
// must pad the 'and' and 'or' operators if required
if (shouldPadOperators
&& (name == "and" || name == "or"))
{
appendSpacePad();
appendSequence(name);
goForward(name.length() - 1);
if (!isBeforeAnyComment()
&& !(currentLine.compare(charNum + 1, 1, ";") == 0)
&& !(currentLine.compare(charNum + 1, 2, "::") == 0))
appendSpaceAfter();
}
else
{
appendSequence(name);
goForward(name.length() - 1);
}
continue;
} // (isPotentialHeader && !isInTemplate)
// determine if this is a potential calculation
bool isPotentialOperator = isCharPotentialOperator(currentChar);
newHeader = NULL;
if (isPotentialOperator)
{
newHeader = findOperator(operators);
if (newHeader != NULL)
{
// correct mistake of two >> closing a template
if (isInTemplate && (newHeader == &AS_GR_GR || newHeader == &AS_GR_GR_GR))
newHeader = &AS_GR;
if (!isInPotentialCalculation)
{
// must determine if newHeader is an assignment operator
// do NOT use findOperator!!!
if (find(assignmentOperators->begin(), assignmentOperators->end(), newHeader)
!= assignmentOperators->end())
{
foundPreCommandHeader = false;
char peekedChar = peekNextChar();
isInPotentialCalculation = (!(newHeader == &AS_EQUAL && peekedChar == '*')
&& !(newHeader == &AS_EQUAL && peekedChar == '&'));
}
}
}
}
// process pointers and references
// check newHeader to elimnate things like '&&' sequence
if (!isJavaStyle()
&& (newHeader == &AS_MULT || newHeader == &AS_BIT_AND || newHeader == &AS_BIT_XOR)
&& isPointerOrReference()
&& !isDereferenceOrAddressOf())
{
formatPointerOrReference();
isImmediatelyPostPointerOrReference = true;
continue;
}
if (shouldPadOperators && newHeader != NULL)
{
padOperators(newHeader);
continue;
}
// pad commas and semi-colons
if (currentChar == ';'
|| (currentChar == ',' && shouldPadOperators))
{
char nextChar = ' ';
if (charNum + 1 < (int) currentLine.length())
nextChar = currentLine[charNum+1];
if (!isWhiteSpace(nextChar)
&& nextChar != '}'
&& nextChar != ')'
&& nextChar != ']'
&& nextChar != '>'
&& nextChar != ';'
&& !isBeforeAnyComment()
/* && !(isBracketType(bracketTypeStack->back(), ARRAY_TYPE)) */
)
{
appendCurrentChar();
appendSpaceAfter();
continue;
}
}
// do NOT use 'continue' after this, it must do padParens if necessary
if (currentChar == '('
&& shouldPadHeader
&& (isCharImmediatelyPostReturn || isCharImmediatelyPostThrow))
appendSpacePad();
if ((currentChar == '(' || currentChar == ')')
&& (shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen))
{
padParens();
continue;
}
// bypass the entire operator
if (newHeader != NULL && newHeader->length() > 1)
{
appendSequence(*newHeader);
goForward(newHeader->length() - 1);
continue;
}
appendCurrentChar();
} // end of while loop * end of while loop * end of while loop * end of while loop
// return a beautified (i.e. correctly indented) line.
string beautifiedLine;
size_t readyFormattedLineLength = trim(readyFormattedLine).length();
if (prependEmptyLine // prepend a blank line before this formatted line
&& readyFormattedLineLength > 0
&& previousReadyFormattedLineLength > 0)
{
isLineReady = true; // signal a waiting readyFormattedLine
beautifiedLine = beautify("");
previousReadyFormattedLineLength = 0;
// call the enhancer for new empty lines
enhancer->enhance(beautifiedLine, isInPreprocessorBeautify, isInBeautifySQL);
}
else // format the current formatted line
{
isLineReady = false;
horstmannIndentInStatement = horstmannIndentChars;
beautifiedLine = beautify(readyFormattedLine);
previousReadyFormattedLineLength = readyFormattedLineLength;
// the enhancer is not called for no-indent line comments
if (!lineCommentNoBeautify)
enhancer->enhance(beautifiedLine, isInPreprocessorBeautify, isInBeautifySQL);
horstmannIndentChars = 0;
lineCommentNoBeautify = lineCommentNoIndent;
lineCommentNoIndent = false;
if (isCharImmediatelyPostNonInStmt)
{
isNonInStatementArray = false;
isCharImmediatelyPostNonInStmt = false;
}
isInPreprocessorBeautify = isInPreprocessor; // used by ASEnhancer
isInBeautifySQL = isInExecSQL; // used by ASEnhancer
}
prependEmptyLine = false;
assert(computeChecksumOut(beautifiedLine));
return beautifiedLine;
}
/**
* check if there are any indented lines ready to be read by nextLine()
*
* @return are there any indented lines ready?
*/
bool ASFormatter::hasMoreLines() const
{
return !endOfCodeReached;
}
/**
* comparison function for BracketType enum
*/
bool ASFormatter::isBracketType(BracketType a, BracketType b) const
{
return ((a & b) == b);
}
/**
* set the formatting style.
*
* @param mode the formatting style.
*/
void ASFormatter::setFormattingStyle(FormatStyle style)
{
formattingStyle = style;
}
/**
* set the add brackets mode.
* options:
* true brackets added to headers for single line statements.
* false brackets NOT added to headers for single line statements.
*
* @param mode the bracket formatting mode.
*/
void ASFormatter::setAddBracketsMode(bool state)
{
shouldAddBrackets = state;
}
/**
* set the add one line brackets mode.
* options:
* true one line brackets added to headers for single line statements.
* false one line brackets NOT added to headers for single line statements.
*
* @param mode the bracket formatting mode.
*/
void ASFormatter::setAddOneLineBracketsMode(bool state)
{
shouldAddBrackets = state;
shouldAddOneLineBrackets = state;
}
/**
* set the bracket formatting mode.
* options:
*
* @param mode the bracket formatting mode.
*/
void ASFormatter::setBracketFormatMode(BracketMode mode)
{
bracketFormatMode = mode;
}
/**
* set 'break after' mode for maximum code length
*
* @param state the 'break after' mode.
*/
void ASFormatter::setBreakAfterMode(bool state)
{
shouldBreakLineAfterLogical = state;
}
/**
* set closing header 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 maximum code length
*
* @param max the maximum code length.
*/
void ASFormatter::setMaxCodeLength(int max)
{
maxCodeLength = max;
}
/**
* set operator padding mode.
* options:
* true statement operators will be padded with spaces around them.
* false statement operators will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setOperatorPaddingMode(bool state)
{
shouldPadOperators = state;
}
/**
* set parenthesis outside padding mode.
* options:
* true statement 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 padding mode before one or more open parentheses.
* options:
* true first open parenthesis will be padded with a space before.
* false first open parenthesis will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensFirstPaddingMode(bool state)
{
shouldPadFirstParen = state;
}
/**
* set header padding mode.
* options:
* true headers will be padded with spaces around them.
* false headers will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensHeaderPaddingMode(bool state)
{
shouldPadHeader = state;
}
/**
* set parenthesis unpadding mode.
* options:
* true statement parenthesis will be unpadded with spaces removed around them.
* false statement parenthesis will not be unpadded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensUnPaddingMode(bool state)
{
shouldUnPadParens = state;
}
/**
* set option to attach closing brackets
*
* @param state true = attach, false = don't attach.
*/
void ASFormatter::setAttachClosingBracket(bool state)
{
shouldAttachClosingBracket = 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;
}
void ASFormatter::setCloseTemplatesMode(bool state)
{
shouldCloseTemplates = 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 indent comments in column 1.
*
* @param state true = indent, false = don't indent.
*/
void ASFormatter::setIndentCol1CommentsMode(bool state)
{
shouldIndentCol1Comments = state;
}
/**
* set option to force all line ends to a particular style.
*
* @param fmt format enum value
*/
void ASFormatter::setLineEndFormat(LineEndFormat fmt)
{
lineEnd = fmt;
}
/**
* set option to break unrelated blocks of code with empty lines.
*
* @param state true = convert, false = don't convert.
*/
void ASFormatter::setBreakBlocksMode(bool state)
{
shouldBreakBlocks = state;
}
/**
* set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines.
*
* @param state true = convert, false = don't convert.
*/
void ASFormatter::setBreakClosingHeaderBlocksMode(bool state)
{
shouldBreakClosingHeaderBlocks = state;
}
/**
* set option to delete empty lines.
*
* @param state true = delete, false = don't delete.
*/
void ASFormatter::setDeleteEmptyLinesMode(bool state)
{
shouldDeleteEmptyLines = state;
}
/**
* set the pointer alignment.
* options:
*
* @param alignment the pointer alignment.
*/
void ASFormatter::setPointerAlignment(PointerAlign alignment)
{
pointerAlignment = alignment;
}
void ASFormatter::setReferenceAlignment(ReferenceAlign alignment)
{
referenceAlignment = alignment;
}
/**
* jump over several characters.
*
* @param i the number of characters to jump over.
*/
void ASFormatter::goForward(int i)
{
while (--i >= 0)
getNextChar();
}
/**
* peek at the next unread character.
*
* @return the next unread character.
*/
char ASFormatter::peekNextChar() const
{
char ch = ' ';
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos)
return ch;
ch = currentLine[peekNum];
return ch;
}
/**
* check if current placement is before a comment
*
* @return is before a comment.
*/
bool ASFormatter::isBeforeComment() const
{
bool foundComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos)
return foundComment;
foundComment = (currentLine.compare(peekNum, 2, "/*") == 0);
return foundComment;
}
/**
* check if current placement is before a comment or line-comment
*
* @return is before a comment or line-comment.
*/
bool ASFormatter::isBeforeAnyComment() const
{
bool foundComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos)
return foundComment;
foundComment = (currentLine.compare(peekNum, 2, "/*") == 0
|| currentLine.compare(peekNum, 2, "//") == 0);
return foundComment;
}
/**
* check if current placement is before a comment or line-comment
* if a block comment it must be at the end of the line
*
* @return is before a comment or line-comment.
*/
bool ASFormatter::isBeforeAnyLineEndComment(int startPos) const
{
bool foundLineEndComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
if (peekNum != string::npos)
{
if (currentLine.compare(peekNum, 2, "//") == 0)
foundLineEndComment = true;
else if (currentLine.compare(peekNum, 2, "/*") == 0)
{
// comment must be closed on this line with nothing after it
size_t endNum = currentLine.find("*/", peekNum + 2);
if (endNum != string::npos)
{
size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
if (nextChar == string::npos)
foundLineEndComment = true;
}
}
}
return foundLineEndComment;
}
/**
* check if current placement is before a comment followed by a line-comment
*
* @return is before a multiple line-end comment.
*/
bool ASFormatter::isBeforeMultipleLineEndComments(int startPos) const
{
bool foundMultipleLineEndComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
if (peekNum != string::npos)
{
if (currentLine.compare(peekNum, 2, "/*") == 0)
{
// comment must be closed on this line with nothing after it
size_t endNum = currentLine.find("*/", peekNum + 2);
if (endNum != string::npos)
{
size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
if (nextChar != string::npos
&& currentLine.compare(nextChar, 2, "//") == 0)
foundMultipleLineEndComment = true;
}
}
}
return foundMultipleLineEndComment;
}
/**
* get the next character, increasing the current placement in the process.
* the new character is inserted into the variable currentChar.
*
* @return whether succeded to recieve the new character.
*/
bool ASFormatter::getNextChar()
{
isInLineBreak = false;
previousChar = currentChar;
if (!isWhiteSpace(currentChar))
{
previousNonWSChar = currentChar;
if (!isInComment && !isInLineComment && !isInQuote
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment
&& !isInPreprocessor
&& !isSequenceReached("/*")
&& !isSequenceReached("//"))
previousCommandChar = currentChar;
}
if (charNum + 1 < (int) currentLine.length()
&& (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment))
{
currentChar = currentLine[++charNum];
if (shouldConvertTabs && currentChar == '\t')
convertTabToSpaces();
return true;
}
// end of line has been reached
return getNextLine();
}
/**
* get the next line of input, increasing the current placement in the process.
*
* @param sequence the sequence to append.
* @return whether succeded in reading the next line.
*/
bool ASFormatter::getNextLine(bool emptyLineWasDeleted /*false*/)
{
if (sourceIterator->hasMoreLines())
{
if (appendOpeningBracket)
currentLine = "{"; // append bracket that was removed from the previous line
else
{
currentLine = sourceIterator->nextLine(emptyLineWasDeleted);
assert(computeChecksumIn(currentLine));
}
// reset variables for new line
inLineNumber++;
if (endOfAsmReached)
endOfAsmReached = isInAsm = false;
shouldKeepLineUnbroken = false;
isInCommentStartLine = false;
isInCase = false;
isInAsmOneLine = false;
isInQuoteContinuation = isInVerbatimQuote | haveLineContinuationChar;
haveLineContinuationChar= false;
isImmediatelyPostEmptyLine = lineIsEmpty;
previousChar = ' ';
if (currentLine.length() == 0)
currentLine = string(" "); // a null is inserted if this is not done
// unless reading in the first line of the file, break a new line.
if (!isVirgin)
isInLineBreak = true;
else
isVirgin = false;
// TODO: FIX FOR BROKEN CASE STATEMANTS - RELEASE 2.02.1
// REMOVE AT AN APPROPRIATE TIME
if (currentHeader == &AS_CASE
&& isInLineBreak
&& !isImmediatelyPostLineComment)
{
// check for split line
if ((formattedLine.length() >= 4
&& formattedLine.substr(formattedLine.length() - 4, 4) == "case")
|| (formattedLine[formattedLine.length() - 1] == '\''
&& findNextChar(currentLine, ':') != string::npos)
)
{
isInLineBreak = false;
isInCase = true;
if (formattedLine.substr(formattedLine.length() - 4, 4) == "case")
appendSpacePad();
}
}
// END OF FIX
if (isImmediatelyPostNonInStmt)
{
isCharImmediatelyPostNonInStmt = true;
isImmediatelyPostNonInStmt = false;
}
// check if is in preprocessor before line trimming
// a blank line after a \ will remove the flag
isImmediatelyPostPreprocessor = isInPreprocessor;
if (!isInComment
&& (previousNonWSChar != '\\'
|| isEmptyLine(currentLine)))
isInPreprocessor = false;
if (passedSemicolon)
isInExecSQL = false;
initNewLine();
currentChar = currentLine[charNum];
if (isInHorstmannRunIn && previousNonWSChar == '{' && !isInComment)
isInLineBreak = false;
isInHorstmannRunIn = false;
if (shouldConvertTabs && currentChar == '\t')
convertTabToSpaces();
// check for an empty line inside a command bracket.
// if yes then read the next line (calls getNextLine recursively).
// must be after initNewLine.
if (shouldDeleteEmptyLines
&& lineIsEmpty
&& isBracketType((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE))
{
if (!shouldBreakBlocks || previousNonWSChar == '{' || !commentAndHeaderFollows())
{
isInPreprocessor = isImmediatelyPostPreprocessor; // restore
lineIsEmpty = false;
return getNextLine(true);
}
}
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::initNewLine()
{
assert(getTabLength() > 0);
size_t len = currentLine.length();
size_t tabSize = getTabLength();
charNum = 0;
// don't trim these
if (isInQuoteContinuation
|| (isInPreprocessor && !getPreprocessorIndent()))
return;
// SQL continuation lines must be adjusted so the leading spaces
// is equivalent to the opening EXEC SQL
if (isInExecSQL)
{
// replace leading tabs with spaces
// so that continuation indent will be spaces
size_t tabCount_ = 0;
size_t i;
for (i = 0; i < currentLine.length(); i++)
{
if (!isWhiteSpace(currentLine[i])) // stop at first text
break;
if (currentLine[i] == '\t')
{
size_t numSpaces = tabSize - ((tabCount_ + i) % tabSize);
currentLine.replace(i, 1, numSpaces, ' ');
tabCount_++;
i += tabSize - 1;
}
}
// this will correct the format if EXEC SQL is not a hanging indent
trimContinuationLine();
return;
}
// comment continuation lines must be adjusted so the leading spaces
// is equivalent to the opening comment
if (isInComment)
{
if (noTrimCommentContinuation)
leadingSpaces = tabIncrementIn = 0;
trimContinuationLine();
return;
}
// compute leading spaces
isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly;
lineIsLineCommentOnly = false;
lineEndsInCommentOnly = false;
doesLineStartComment = false;
currentLineBeginsWithBracket = false;
lineIsEmpty = false;
currentLineFirstBracketNum = string::npos;
tabIncrementIn = 0;
// bypass whitespace at the start of a line
// preprocessor tabs are replaced later in the program
for (charNum = 0; isWhiteSpace(currentLine[charNum]) && charNum + 1 < (int) len; charNum++)
{
if (currentLine[charNum] == '\t' && !isInPreprocessor)
tabIncrementIn += tabSize - 1 - ((tabIncrementIn + charNum) % tabSize);
}
leadingSpaces = charNum + tabIncrementIn;
if (isSequenceReached("/*"))
{
doesLineStartComment = true;
}
else if (isSequenceReached("//"))
{
lineIsLineCommentOnly = true;
}
else if (isSequenceReached("{"))
{
currentLineBeginsWithBracket = true;
currentLineFirstBracketNum = charNum;
size_t firstText = currentLine.find_first_not_of(" \t", charNum + 1);
if (firstText != string::npos)
{
if (currentLine.compare(firstText, 2, "//") == 0)
lineIsLineCommentOnly = true;
else if (currentLine.compare(firstText, 2, "/*") == 0
|| isExecSQL(currentLine, firstText))
{
// get the extra adjustment
size_t j;
for (j = charNum + 1; isWhiteSpace(currentLine[j]) && j < firstText; j++)
{
if (currentLine[j] == '\t')
tabIncrementIn += tabSize - 1 - ((tabIncrementIn + j) % tabSize);
}
leadingSpaces = j + tabIncrementIn;
if (currentLine.compare(firstText, 2, "/*") == 0)
doesLineStartComment = true;
}
}
}
else if (isWhiteSpace(currentLine[charNum]) && !(charNum + 1 < (int) currentLine.length()))
{
lineIsEmpty = true;
}
// do not trim indented preprocessor define (except for comment continuation lines)
if (isInPreprocessor)
{
if (!doesLineStartComment)
leadingSpaces = 0;
charNum = 0;
}
}
/**
* Append a character to the current formatted line.
*
* @param char the character to append.
* @param canBreakLine if true, a registered line-break
*/
void ASFormatter::appendChar(char ch, bool canBreakLine)
{
if (canBreakLine && isInLineBreak)
breakLine();
formattedLine.append(1, ch);
isImmediatelyPostCommentOnly = false;
if (maxCodeLength != string::npos)
{
updateFormattedLineSplitPoints(ch);
testForTimeToSplitFormattedLine(1);
}
}
/**
* Append a string sequence to the current 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);
if (maxCodeLength != string::npos)
{
updateFormattedLineSplitPointSequence(sequence);
testForTimeToSplitFormattedLine(sequence.length());
}
}
/**
* append a space to the current formattedline, UNLESS the
* last character is already a white-space character.
*/
void ASFormatter::appendSpacePad()
{
int len = formattedLine.length();
if (len > 0 && !isWhiteSpace(formattedLine[len-1]))
{
formattedLine.append(1, ' ');
spacePadNum++;
if (maxCodeLength != string::npos)
{
updateFormattedLineSplitPoints(' ');
testForTimeToSplitFormattedLine(1);
}
}
}
/**
* append a space to the current formattedline, UNLESS the
* next character is already a white-space character.
*/
void ASFormatter::appendSpaceAfter()
{
int len = currentLine.length();
if (charNum + 1 < len && !isWhiteSpace(currentLine[charNum+1]))
{
formattedLine.append(1, ' ');
spacePadNum++;
if (maxCodeLength != string::npos)
{
updateFormattedLineSplitPoints(' ');
testForTimeToSplitFormattedLine(1);
}
}
}
/**
* register a line break for the formatted line.
*/
void ASFormatter::breakLine(bool isSplitLine /*false*/)
{
isLineReady = true;
isInLineBreak = false;
spacePadNum = nextLineSpacePadNum;
nextLineSpacePadNum = 0;
readyFormattedLine = formattedLine;
formattedLine = "";
if (!isSplitLine)
{
formattedLineCommentNum = string::npos;
clearFormattedLineSplitPoints();
}
// queue an empty line prepend request if one exists
prependEmptyLine = isPrependPostBlockEmptyLineRequested;
if (!isSplitLine && isAppendPostBlockEmptyLineRequested)
{
isAppendPostBlockEmptyLineRequested = false;
isPrependPostBlockEmptyLineRequested = true;
}
else
isPrependPostBlockEmptyLineRequested = false;
}
/**
* 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()
{
assert(currentChar == '{');
BracketType returnVal;
if ((previousNonWSChar == '='
|| isBracketType(bracketTypeStack->back(), ARRAY_TYPE))
&& previousCommandChar != ')')
returnVal = ARRAY_TYPE;
else if (foundPreDefinitionHeader && previousCommandChar != ')')
{
returnVal = DEFINITION_TYPE;
if (foundNamespaceHeader)
returnVal = (BracketType)(returnVal | NAMESPACE_TYPE);
else if (foundClassHeader)
returnVal = (BracketType)(returnVal | CLASS_TYPE);
else if (foundStructHeader)
returnVal = (BracketType)(returnVal | STRUCT_TYPE);
else if (foundInterfaceHeader)
returnVal = (BracketType)(returnVal | INTERFACE_TYPE);
}
else
{
bool isCommandType = (foundPreCommandHeader
|| (currentHeader != NULL && isNonParenHeader)
|| (previousCommandChar == ')')
|| (previousCommandChar == ':' && !foundQuestionMark)
|| (previousCommandChar == ';')
|| ((previousCommandChar == '{' || previousCommandChar == '}')
&& isPreviousBracketBlockRelated)
|| isJavaStaticConstructor
|| isSharpDelegate);
// C# methods containing 'get', 'set', 'add', and 'remove' do NOT end with parens
if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1))
{
isCommandType = true;
isSharpAccessor = true;
}
if (!isCommandType && isInExtern)
returnVal = EXTERN_TYPE;
else
returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE);
}
int foundOneLineBlock = isOneLineBlockReached(currentLine, charNum);
// this assumes each array definition is on a single line
// (foundOneLineBlock == 2) is a one line block followed by a comma
if (foundOneLineBlock == 2 && returnVal == COMMAND_TYPE)
returnVal = ARRAY_TYPE;
if (foundOneLineBlock > 0) // found one line block
returnVal = (BracketType)(returnVal | SINGLE_LINE_TYPE);
if (isBracketType(returnVal, ARRAY_TYPE) && isNonInStatementArrayBracket())
{
returnVal = (BracketType)(returnVal | ARRAY_NIS_TYPE);
isNonInStatementArray = true;
nonInStatementBracket = formattedLine.length() - 1;
}
return returnVal;
}
/**
* check if a line is empty
*
* @return whether line is empty
*/
bool ASFormatter::isEmptyLine(const string &line) const
{
return line.find_first_not_of(" \t") == string::npos;
}
/**
* Check if the currently reached '*', '&' or '^' character is
* a pointer-or-reference symbol, or another operator.
* A pointer dereference (*) or an "address of" character (&)
* counts as a pointer or reference because it is not an
* arithmetic operator.
*
* @return whether current character is a reference-or-pointer
*/
bool ASFormatter::isPointerOrReference() const
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
if (isJavaStyle())
return false;
if ((currentChar == '&' && previousChar == '&')
|| isCharImmediatelyPostOperator)
return false;
if (previousNonWSChar == '='
|| previousNonWSChar == '('
|| previousNonWSChar == '['
|| isCharImmediatelyPostReturn
|| isInTemplate
|| isCharImmediatelyPostTemplate
|| currentHeader == &AS_CATCH)
return true;
// get the last legal word (may be a number)
string lastWord = getPreviousWord(currentLine, charNum);
if (lastWord.empty())
lastWord = " ";
char nextChar = peekNextChar();
// check for preceding or following numeric values
if (isDigit(lastWord[0])
|| isDigit(nextChar)
|| nextChar == '!')
return false;
if (isBracketType(bracketTypeStack->back(), ARRAY_TYPE)
&& isLegalNameChar(lastWord[0])
&& isLegalNameChar(nextChar)
&& previousNonWSChar != ')')
{
if (isArrayOperator())
return false;
}
// checks on operators in parens
if (parenStack->back() > 0
&& isLegalNameChar(lastWord[0])
&& isLegalNameChar(nextChar))
{
// if followed by an assignment it is a pointer or reference
const string* followingOperator = getFollowingOperator();
if (followingOperator
&& followingOperator != &AS_MULT
&& followingOperator != &AS_BIT_AND)
{
if (followingOperator == &AS_ASSIGN)
return true;
else
return false;
}
if (!isBracketType(bracketTypeStack->back(), COMMAND_TYPE))
return true;
else
return false;
}
// checks on operators in parens with following '('
if (parenStack->back() > 0
&& nextChar == '('
&& previousNonWSChar != ','
&& previousNonWSChar != '('
&& previousNonWSChar != '!'
&& previousNonWSChar != '&'
&& previousNonWSChar != '*'
&& previousNonWSChar != '|')
return false;
if (nextChar == '-'
|| nextChar == '+')
{
size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextNum != string::npos)
{
if (currentLine.compare(nextNum, 2, "++") != 0
&& currentLine.compare(nextNum, 2, "--") != 0)
return false;
}
}
bool isPR = (!isInPotentialCalculation
|| isBracketType(bracketTypeStack->back(), DEFINITION_TYPE)
|| (!isLegalNameChar(previousNonWSChar)
&& !(previousNonWSChar == ')' && nextChar == '(')
&& !(previousNonWSChar == ')' && currentChar == '*' && !isImmediatelyPostCast())
&& previousNonWSChar != ']')
);
if (!isPR)
{
isPR |= (!isWhiteSpace(nextChar)
&& nextChar != '-'
&& nextChar != '('
&& nextChar != '['
&& !isLegalNameChar(nextChar));
}
return isPR;
}
/**
* Check if the currently reached '*' or '&' character is
* a dereferenced pointer or "address of" symbol.
* NOTE: this MUST be a pointer or reference as determined by
* the function isPointerOrReference().
*
* @return whether current character is a dereference or address of
*/
bool ASFormatter::isDereferenceOrAddressOf() const
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
if (isCharImmediatelyPostTemplate)
return false;
if (previousNonWSChar == '='
|| previousNonWSChar == ','
|| previousNonWSChar == '.'
|| previousNonWSChar == '{'
|| previousNonWSChar == '>'
|| previousNonWSChar == '<'
|| isCharImmediatelyPostLineComment
|| isCharImmediatelyPostComment
|| isCharImmediatelyPostReturn)
return true;
// check for **
if (currentChar == '*'
&& (int) currentLine.length() > charNum
&& currentLine[charNum+1] == '*')
{
if (previousNonWSChar == '(')
return true;
if ((int) currentLine.length() < charNum + 2)
return true;
return false;
}
// check first char on the line
if (charNum == (int) currentLine.find_first_not_of(" \t"))
return true;
char nextChar = peekNextChar();
if (nextChar == ')' || nextChar == '>' || nextChar == ',')
return false;
// check for reference to a pointer *& (cannot have &*)
if (( currentChar == '*' && nextChar == '&')
|| (previousNonWSChar == '*' && currentChar == '&'))
return false;
if (!isBracketType(bracketTypeStack->back(), COMMAND_TYPE)
&& parenStack->back() == 0)
return false;
string lastWord = getPreviousWord(currentLine, charNum);
if (lastWord == "else" || lastWord == "delete")
return true;
bool isDA = (!(isLegalNameChar(previousNonWSChar) || previousNonWSChar == '>')
|| (!isLegalNameChar(nextChar) && nextChar != '/')
|| (ispunct((unsigned char)previousNonWSChar) && previousNonWSChar != '.')
|| isCharImmediatelyPostReturn);
return isDA;
}
/**
* Check if the currently reached '*' or '&' character is
* centered with one space on each side.
* Only spaces are checked, not tabs.
* If true then a space will be deleted on the output.
*
* @return whether current character is centered.
*/
bool ASFormatter::isPointerOrReferenceCentered() const
{
assert(currentLine[charNum] == '*' || currentLine[charNum] == '&' || currentLine[charNum] == '^');
int prNum = charNum;
int lineLength = (int) currentLine.length();
// check for end of line
if (peekNextChar() == ' ')
return false;
// check space before
if (prNum < 1
|| currentLine[prNum-1] != ' ')
return false;
// check no space before that
if (prNum < 2
|| currentLine[prNum-2] == ' ')
return false;
// check for **
if (prNum + 1 < lineLength
&& currentLine[prNum+1] == '*')
prNum++;
// check space after
if (prNum + 1 <= lineLength
&& currentLine[prNum+1] != ' ')
return false;
// check no space after that
if (prNum + 2 < lineLength
&& currentLine[prNum+2] == ' ')
return false;
return true;
}
/**
* check if the currently reached '+' or '-' character is a unary operator
* this method takes for granted that the current character
* is a '+' or '-'.
*
* @return whether the current '+' or '-' is a unary operator.
*/
bool ASFormatter::isUnaryOperator() const
{
assert(currentChar == '+' || currentChar == '-');
return ((isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar))
&& previousCommandChar != '.'
&& previousCommandChar != '\"'
&& previousCommandChar != '\''
&& previousCommandChar != ')'
&& previousCommandChar != ']');
}
/**
* check if the currently reached '+' 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 '+' or '-' is in an exponent.
*/
bool ASFormatter::isInExponent() const
{
assert(currentChar == '+' || currentChar == '-');
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 an array bracket should NOT have an in-statement indent
*
* @return the array is non in-statement
*/
bool ASFormatter::isNonInStatementArrayBracket() const
{
bool returnVal = false;
char nextChar = peekNextChar();
// if this opening bracket begins the line there will be no inStatement indent
if (currentLineBeginsWithBracket
&& charNum == (int) currentLineFirstBracketNum
&& nextChar != '}')
returnVal = true;
// if an opening bracket ends the line there will be no inStatement indent
if (isWhiteSpace(nextChar)
|| isBeforeAnyLineEndComment(charNum)
|| nextChar == '{')
returnVal = true;
// Java "new Type [] {...}" IS an inStatement indent
if (isJavaStyle() && previousNonWSChar == ']')
returnVal = false;
// trace
//if (isNonInStatementArray)
// cout << traceLineNumber << " " << 'x' << endl;
//else
// cout << traceLineNumber << " " << ' ' << endl
return returnVal;
}
/**
* 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 0 = one-line bracket has not been reached.
* 1 = one-line bracket has been reached.
* 2 = one-line bracket has been reached and is followed by a comma.
*/
int ASFormatter::isOneLineBlockReached(string &line, int startChar) const
{
assert(line[startChar] == '{');
bool isInComment_ = false;
bool isInQuote_ = false;
int bracketCount = 1;
int lineLength = line.length();
char quoteChar_ = ' ';
char ch = ' ';
char prevCh = ' ';
for (int i = startChar + 1; i < lineLength; ++i)
{
ch = line[i];
if (isInComment_)
{
if (line.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (ch == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (ch == quoteChar_)
isInQuote_ = false;
continue;
}
if (ch == '"' || ch == '\'')
{
isInQuote_ = true;
quoteChar_ = ch;
continue;
}
if (line.compare(i, 2, "//") == 0)
break;
if (line.compare(i, 2, "/*") == 0)
{
isInComment_ = true;
++i;
continue;
}
if (ch == '{')
++bracketCount;
else if (ch == '}')
--bracketCount;
if (bracketCount == 0)
{
// is this an array?
if (parenStack->back() == 0 && prevCh != '}')
{
size_t peekNum = line.find_first_not_of(" \t", i + 1);
if (peekNum != string::npos && line[peekNum] == ',')
return 2;
}
return 1;
}
if (!isWhiteSpace(ch))
prevCh = ch;
}
return 0;
}
/**
* peek at the next word to determine if it is a C# non-paren header.
* will look ahead in the input file if necessary.
*
* @param char position on currentLine to start the search
* @return true if the next word is get or set.
*/
bool ASFormatter::isNextWordSharpNonParenHeader(int startChar) const
{
// look ahead to find the next non-comment text
string nextText = peekNextText(currentLine.substr(startChar));
if (nextText.length() == 0)
return false;
if (nextText[0] == '[')
return true;
if (!isCharPotentialHeader(nextText, 0))
return false;
if (findKeyword(nextText, 0, AS_GET) || findKeyword(nextText, 0, AS_SET)
|| findKeyword(nextText, 0, AS_ADD) || findKeyword(nextText, 0, AS_REMOVE))
return true;
return false;
}
/**
* peek at the next char to determine if it is an opening bracket.
* will look ahead in the input file if necessary.
* this determines a java static constructor.
*
* @param char position on currentLine to start the search
* @return true if the next word is an opening bracket.
*/
bool ASFormatter::isNextCharOpeningBracket(int startChar) const
{
bool retVal = false;
string nextText = peekNextText(currentLine.substr(startChar));
if (nextText.compare(0, 1, "{") == 0)
retVal = true;
return retVal;
}
/**
* get the next non-whitespace substring on following lines, bypassing all comments.
*
* @param the first line to check
* @return the next non-whitespace substring.
*/
string ASFormatter::peekNextText(const string &firstLine, bool endOnEmptyLine /*false*/, bool shouldReset /*false*/) const
{
bool isFirstLine = true;
bool needReset = shouldReset;
string nextLine_ = firstLine;
size_t firstChar= string::npos;
// find the first non-blank text, bypassing all comments.
bool isInComment_ = false;
while (sourceIterator->hasMoreLines())
{
if (isFirstLine)
isFirstLine = false;
else
{
nextLine_ = sourceIterator->peekNextLine();
needReset = true;
}
firstChar = nextLine_.find_first_not_of(" \t");
if (firstChar == string::npos)
{
if (endOnEmptyLine && !isInComment_)
break;
continue;
}
if (nextLine_.compare(firstChar, 2, "/*") == 0)
{
firstChar += 2;
isInComment_ = true;
}
if (isInComment_)
{
firstChar = nextLine_.find("*/", firstChar);
if (firstChar == string::npos)
continue;
firstChar += 2;
isInComment_ = false;
firstChar = nextLine_.find_first_not_of(" \t", firstChar);
if (firstChar == string::npos)
continue;
}
if (nextLine_.compare(firstChar, 2, "//") == 0)
continue;
// found the next text
break;
}
if (needReset)
sourceIterator->peekReset();
if (firstChar == string::npos)
nextLine_ = "";
else
nextLine_ = nextLine_.substr(firstChar);
return nextLine_;
}
/**
* adjust comment position because of adding or deleting spaces
* the spaces are added or deleted to formattedLine
* spacePadNum contains the adjustment
*/
void ASFormatter::adjustComments(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();
// don't adjust a tab
if (formattedLine[len-1] == '\t')
return;
// if spaces were removed, need to add spaces before the comment
if (spacePadNum < 0)
{
int adjust = -spacePadNum; // make the number positive
formattedLine.append(adjust, ' ');
}
// if spaces were added, need to delete extra spaces before the comment
// if cannot be done put the comment one space after the last text
else if (spacePadNum > 0)
{
int adjust = spacePadNum;
size_t lastText = formattedLine.find_last_not_of(' ');
if (lastText != string::npos
&& lastText < len - adjust - 1)
formattedLine.resize(len - adjust);
else if (len > lastText + 2)
formattedLine.resize(lastText + 2);
else if (len < lastText + 2)
formattedLine.append(len - lastText, ' ');
}
}
/**
* append the current 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?
{
appendCurrentChar(); // don't attach
return;
}
assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0
|| formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0);
// find the previous non space char
size_t end = formattedLineCommentNum;
size_t beg = formattedLine.find_last_not_of(" \t", end-1);
if (beg == string::npos)
{
appendCurrentChar(); // don't attach
return;
}
beg++;
// insert the 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;
testForTimeToSplitFormattedLine();
if (isBeforeComment())
breakLine();
else if (isCharImmediatelyPostLineComment)
shouldBreakLineAtNextChar = true;
return;
}
/**
* 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(newOperator != NULL);
bool shouldPad = (newOperator != &AS_COLON_COLON
&& newOperator != &AS_PLUS_PLUS
&& newOperator != &AS_MINUS_MINUS
&& newOperator != &AS_NOT
&& newOperator != &AS_BIT_NOT
&& newOperator != &AS_ARROW
&& !(newOperator == &AS_MINUS && isInExponent())
&& !((newOperator == &AS_PLUS || newOperator == &AS_MINUS) // check for unary plus or minus
&& (previousNonWSChar == '('
|| previousNonWSChar == '['
|| previousNonWSChar == '='
|| previousNonWSChar == ','))
&& !(newOperator == &AS_PLUS && isInExponent())
&& !isCharImmediatelyPostOperator
&& !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND)
&& isPointerOrReference())
&& !(newOperator == &AS_MULT
&& (previousNonWSChar == '.'
|| previousNonWSChar == '>')) // check for ->
&& !((isInTemplate || isImmediatelyPostTemplate)
&& (newOperator == &AS_LS || newOperator == &AS_GR))
&& !(newOperator == &AS_GCC_MIN_ASSIGN
&& ASBase::peekNextChar(currentLine, charNum+1) == '>')
&& !(newOperator == &AS_GR && previousNonWSChar == '?')
&& !(newOperator == &AS_QUESTION // check for Java wildcard
&& (previousNonWSChar == '<'
|| ASBase::peekNextChar(currentLine, charNum) == '>'
|| ASBase::peekNextChar(currentLine, charNum) == '.'))
&& !isInCase
&& !isInAsm
&& !isInAsmOneLine
&& !isInAsmBlock
);
// pad before operator
if (shouldPad
&& !(newOperator == &AS_COLON
&& (!foundQuestionMark && !isInEnum) && currentHeader != &AS_FOR)
&& !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
&& currentLine.find(':', charNum+1) == string::npos)
)
appendSpacePad();
appendSequence(*newOperator);
goForward(newOperator->length() - 1);
currentChar = (*newOperator)[newOperator->length() - 1];
// pad after operator
// but do not pad after a '-' that is a unary-minus.
if (shouldPad
&& !isBeforeAnyComment()
&& !(newOperator == &AS_PLUS && isUnaryOperator())
&& !(newOperator == &AS_MINUS && isUnaryOperator())
&& !(currentLine.compare(charNum + 1, 1, ";") == 0)
&& !(currentLine.compare(charNum + 1, 2, "::") == 0)
&& !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
&& peekNextChar() == '[')
)
appendSpaceAfter();
previousOperator = newOperator;
return;
}
/**
* format pointer or reference
* currentChar contains the pointer or reference
* the symbol and necessary padding will be appended to formattedLine
* the calling function should have a continue statement after calling this method
*
* NOTE: Do NOT use appendCurrentChar() in this method. The line should not be
* broken once the calculation starts.
*/
void ASFormatter::formatPointerOrReference(void)
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
int pa = pointerAlignment;
int ra = referenceAlignment;
int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
// check for cast
char peekedChar = peekNextChar();
if (currentChar == '*'
&& (int) currentLine.length() > charNum + 1
&& currentLine[charNum+1] == '*')
{
size_t nextChar = currentLine.find_first_not_of(" \t", charNum+2);
if (nextChar == string::npos)
peekedChar = ' ';
else
peekedChar = currentLine[nextChar];
}
if (peekedChar == ')' || peekedChar =='>' || peekedChar ==',')
{
formatPointerOrReferenceCast();
return;
}
// check for a padded space and remove it
if (charNum > 0
&& !isWhiteSpace(currentLine[charNum-1])
&& formattedLine.length() > 0
&& isWhiteSpace(formattedLine[formattedLine.length()-1]))
formattedLine.erase(formattedLine.length()-1);
// do this before bumping charNum
bool isOldPRCentered = isPointerOrReferenceCentered();
if (itemAlignment == PTR_ALIGN_TYPE)
{
size_t prevCh = formattedLine.find_last_not_of(" \t");
if (prevCh == string::npos)
prevCh = 0;
if (formattedLine.length() == 0 || prevCh == formattedLine.length() - 1)
formattedLine.append(1, currentChar);
else
{
// exchange * or & with character following the type
// this may not work every time with a tab character
string charSave = formattedLine.substr(prevCh+1, 1);
formattedLine[prevCh+1] = currentChar;
formattedLine.append(charSave);
}
if (isSequenceReached("**"))
{
if (formattedLine.length() == 1)
formattedLine.append("*");
else
formattedLine.insert(prevCh+2, "*");
goForward(1);
}
// if no space after * then add one
if (charNum < (int) currentLine.length() - 1
&& !isWhiteSpace(currentLine[charNum+1])
&& currentLine[charNum+1] != ')')
appendSpacePad();
// if old pointer or reference is centered, remove a space
if (isOldPRCentered
&& isWhiteSpace(formattedLine[formattedLine.length()-1]))
{
formattedLine.erase(formattedLine.length()-1, 1);
spacePadNum--;
}
}
else if (itemAlignment == PTR_ALIGN_MIDDLE)
{
// compute current whitespace before
size_t wsBefore = currentLine.find_last_not_of(" \t", charNum - 1);
if (wsBefore == string::npos)
wsBefore = 0;
else
wsBefore = charNum - wsBefore - 1;
string sequenceToInsert(1, currentChar);
if (isSequenceReached("**"))
{
sequenceToInsert = "**";
goForward(1);
}
// if reference to a pointer check for conflicting alignment
else if (currentChar == '*' && peekedChar == '&'
&& (referenceAlignment == REF_ALIGN_TYPE
|| referenceAlignment == REF_ALIGN_MIDDLE
|| referenceAlignment == REF_SAME_AS_PTR))
{
sequenceToInsert = "*&";
goForward(1);
for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
goForward(1);
}
bool isAfterScopeResolution = previousNonWSChar == ':'; // check for ::
size_t charNumSave = charNum;
// if a comment follows don't align, just space pad
if (isBeforeAnyComment())
{
appendSpacePad();
formattedLine.append(sequenceToInsert);
appendSpaceAfter();
return;
}
// if this is not the last thing on the line
if ((int) currentLine.find_first_not_of(" \t", charNum + 1) > charNum)
{
// goForward() to convert tabs to spaces, if necessary,
// and move following characters to preceding characters
// this may not work every time with tab characters
for (size_t i = charNum+1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
{
goForward(1);
formattedLine.append(1, currentLine[i]);
}
}
// find space padding after
size_t wsAfter = currentLine.find_first_not_of(" \t", charNumSave + 1);
if (wsAfter == string::npos || isBeforeAnyComment())
wsAfter = 0;
else
wsAfter = wsAfter - charNumSave - 1;
// don't pad before scope resolution operator, but pad after
if (isAfterScopeResolution)
{
size_t lastText = formattedLine.find_last_not_of(" \t");
formattedLine.insert(lastText + 1, sequenceToInsert);
appendSpacePad();
}
// whitespace should be at least 2 chars to center
else
{
if (wsBefore + wsAfter < 2)
{
size_t charsToAppend = (2 - (wsBefore + wsAfter));
formattedLine.append(charsToAppend, ' ');
spacePadNum += charsToAppend;
if (wsBefore == 0) wsBefore++;
if (wsAfter == 0) wsAfter++;
}
// insert the pointer or reference char
size_t padAfter = (wsBefore + wsAfter) / 2;
formattedLine.insert(formattedLine.length() - padAfter, sequenceToInsert);
}
}
else if (itemAlignment == PTR_ALIGN_NAME)
{
size_t startNum = formattedLine.find_last_not_of(" \t");
string sequenceToInsert(1, currentChar);
if (isSequenceReached("**"))
{
sequenceToInsert = "**";
goForward(1);
}
// if reference to a pointer align both to type
else if (currentChar == '*' && peekedChar == '&')
{
sequenceToInsert = "*&";
goForward(1);
for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
goForward(1);
}
bool isAfterScopeResolution = previousNonWSChar == ':'; // check for ::
// if this is not the last thing on the line
if (!isBeforeAnyComment()
&& (int) currentLine.find_first_not_of(" \t", charNum + 1) > charNum)
{
// goForward() to convert tabs to spaces, if necessary,
// and move following characters to preceding characters
// this may not work every time with tab characters
for (size_t i = charNum+1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
{
// if a padded paren follows don't move
if (shouldPadParensOutside && peekedChar == '(' && !isOldPRCentered)
break;
goForward(1);
formattedLine.append(1, currentLine[i]);
}
}
// don't pad before scope resolution operator
if (startNum != string::npos && isAfterScopeResolution)
{
size_t lastText = formattedLine.find_last_not_of(" \t");
if (lastText + 1 < formattedLine.length())
formattedLine.erase(lastText + 1);
}
// if no space before * then add one
else if (formattedLine.length() <= startNum + 1
|| !isWhiteSpace(formattedLine[startNum+1]))
{
formattedLine.insert(startNum+1 , 1, ' ');
spacePadNum++;
}
appendSequence(sequenceToInsert, false);
// if old pointer or reference is centered, remove a space
if (isOldPRCentered
&& formattedLine.length() > startNum+1
&& isWhiteSpace(formattedLine[startNum+1])
&& !isBeforeAnyComment())
{
formattedLine.erase(startNum+1, 1);
spacePadNum--;
}
}
else // pointerAlignment == PTR_ALIGN_NONE
{
formattedLine.append(1, currentChar);
}
return;
}
/**
* format pointer or reference cast
* currentChar contains the pointer or reference
* NOTE: the pointers and references in function definitions
* are processed as a cast (e.g. void foo(void*, void*))
* is processed here.
*/
void ASFormatter::formatPointerOrReferenceCast(void)
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
int pa = pointerAlignment;
int ra = referenceAlignment;
int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
string sequenceToInsert(1, currentChar);
if (isSequenceReached("**"))
{
sequenceToInsert = "**";
goForward(1);
}
if (itemAlignment == PTR_ALIGN_NONE)
{
appendSequence(sequenceToInsert, false);
return;
}
// remove trailing whitespace
size_t prevCh = formattedLine.find_last_not_of(" \t");
if (prevCh == string::npos)
prevCh = 0;
if (prevCh + 1 < formattedLine.length()
&& isWhiteSpace(formattedLine[prevCh+1]))
{
spacePadNum -= (formattedLine.length() - 1 - prevCh);
formattedLine.erase(prevCh+1);
}
if (itemAlignment == PTR_ALIGN_MIDDLE
|| itemAlignment == PTR_ALIGN_NAME)
{
appendSpacePad();
appendSequence(sequenceToInsert, false);
}
else
appendSequence(sequenceToInsert, false);
}
/**
* add or remove space padding to parens
* currentChar contains the paren
* the parens and necessary padding will be appended to formattedLine
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::padParens(void)
{
assert(currentChar == '(' || currentChar == ')');
int spacesOutsideToDelete = 0;
int spacesInsideToDelete = 0;
if (currentChar == '(')
{
spacesOutsideToDelete = formattedLine.length() - 1;
spacesInsideToDelete = 0;
// compute spaces outside the opening paren to delete
if (shouldUnPadParens)
{
char lastChar = ' ';
bool prevIsParenHeader = false;
size_t i = formattedLine.find_last_not_of(" \t");
if (i != string::npos)
{
// if last char is a bracket the previous whitespace is an indent
if (formattedLine[i] == '{')
spacesOutsideToDelete = 0;
else if (isCharImmediatelyPostPointerOrReference)
spacesOutsideToDelete = 0;
else
{
spacesOutsideToDelete -= i;
lastChar = formattedLine[i];
// if previous word is a header, it will be a paren header
string prevWord = getPreviousWord(formattedLine, formattedLine.length());
const string* prevWordH = NULL;
if (shouldPadHeader
&& prevWord.length() > 0
&& isCharPotentialHeader(prevWord, 0))
prevWordH = ASBeautifier::findHeader(prevWord, 0, headers);
if (prevWordH != NULL)
prevIsParenHeader = true;
else if (prevWord == "return") // don't unpad
prevIsParenHeader = true;
else if (isCStyle() && prevWord == "throw" && shouldPadHeader) // don't unpad
prevIsParenHeader = true;
else if (prevWord == "and" || prevWord == "or") // don't unpad
prevIsParenHeader = true;
// 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;
// trace
//cout << traceLineNumber << " " << prevWord << endl;
}
}
}
// do not unpad operators, but leave them if already padded
if (shouldPadParensOutside || prevIsParenHeader)
spacesOutsideToDelete--;
else if (lastChar == '|' // check for ||
|| lastChar == '&' // check for &&
|| lastChar == ','
|| (lastChar == '(' && shouldPadParensInside)
|| (lastChar == '>' && !foundCastOperator)
|| lastChar == '<'
|| lastChar == '?'
|| lastChar == ':'
|| lastChar == ';'
|| lastChar == '='
|| lastChar == '+'
|| lastChar == '-'
|| lastChar == '*'
|| lastChar == '/'
|| lastChar == '%'
|| lastChar == '^'
)
spacesOutsideToDelete--;
if (spacesOutsideToDelete > 0)
{
formattedLine.erase(i + 1, spacesOutsideToDelete);
spacePadNum -= spacesOutsideToDelete;
}
}
// pad open paren outside
if (shouldPadFirstParen && previousChar != '(')
appendSpacePad();
else if (shouldPadParensOutside)
{
char peekedCharOutside = peekNextChar();
if (!(currentChar == '(' && peekedCharOutside == ')'))
appendSpacePad();
}
appendCurrentChar();
// unpad open paren inside
if (shouldUnPadParens)
{
size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
if (j != string::npos)
spacesInsideToDelete = j - charNum - 1;
if (shouldPadParensInside)
spacesInsideToDelete--;
if (spacesInsideToDelete > 0)
{
currentLine.erase(charNum + 1, spacesInsideToDelete);
spacePadNum -= spacesInsideToDelete;
}
// convert tab to space if requested
if (shouldConvertTabs
&& (int)currentLine.length() > charNum + 1
&& currentLine[charNum+1] == '\t')
currentLine[charNum+1] = ' ';
}
// pad open paren inside
char peekedCharInside = peekNextChar();
if (shouldPadParensInside)
if (!(currentChar == '(' && peekedCharInside == ')'))
appendSpaceAfter();
// trace
//if(spacesOutsideToDelete > 0 || spacesInsideToDelete > 0)
// cout << traceLineNumber << " " << spacesOutsideToDelete << '(' << spacesInsideToDelete << endl;
}
else if (currentChar == ')')
{
spacesOutsideToDelete = 0;
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
// close parens outside are left unchanged
if (shouldUnPadParens)
{
//size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
//if (j != string::npos)
// spacesOutsideToDelete = j - charNum - 1;
//if (shouldPadParensOutside)
// spacesOutsideToDelete--;
//if (spacesOutsideToDelete > 0)
//{
// currentLine.erase(charNum + 1, spacesOutsideToDelete);
// spacePadNum -= spacesOutsideToDelete;
//}
}
// pad close paren outside
char peekedCharOutside = peekNextChar();
if (shouldPadParensOutside)
if (peekedCharOutside != ';'
&& peekedCharOutside != ','
&& peekedCharOutside != '.'
&& peekedCharOutside != '-' // check for ->
&& peekedCharOutside != ']')
appendSpaceAfter();
// trace
//if(spacesInsideToDelete > 0)
// cout << traceLineNumber << " " << spacesInsideToDelete << ')' << 0 << endl;
}
return;
}
/**
* format opening bracket 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::formatOpeningBracket(BracketType bracketType)
{
assert(!isBracketType(bracketType, ARRAY_TYPE));
assert(currentChar == '{');
parenStack->push_back(0);
bool breakBracket = isCurrentBracketBroken();
if (breakBracket)
{
if (isBeforeAnyComment() && isOkToBreakBlock(bracketType))
{
// if comment is at line end leave the comment on this line
if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBracket) // lineBeginsWith('{')
{
currentChar = ' '; // remove bracket from current line
if (parenStack->size() > 1)
parenStack->pop_back();
currentLine[charNum] = currentChar;
appendOpeningBracket = true; // append bracket to following line
}
// else put comment after the bracket
else if (!isBeforeMultipleLineEndComments(charNum))
breakLine();
}
else if (!isBracketType(bracketType, SINGLE_LINE_TYPE))
breakLine();
else if (shouldBreakOneLineBlocks && peekNextChar() != '}')
breakLine();
else if (!isInLineBreak)
appendSpacePad();
appendCurrentChar();
// should a following comment break from the bracket?
// must break the line AFTER the bracket
if (isBeforeComment()
&& formattedLine.length() > 0
&& formattedLine[0] == '{'
&& isOkToBreakBlock(bracketType)
&& (bracketFormatMode == BREAK_MODE
|| bracketFormatMode == LINUX_MODE
|| bracketFormatMode == STROUSTRUP_MODE))
{
shouldBreakLineAtNextChar = true;
}
}
else // attach bracket
{
// are there comments before the bracket?
if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment)
{
if (isOkToBreakBlock(bracketType)
&& !(isCharImmediatelyPostComment && isCharImmediatelyPostLineComment) // don't attach if two comments on the line
&& !isImmediatelyPostPreprocessor
&& peekNextChar() != '}' // don't attach { }
&& previousCommandChar != '{' // don't attach { {
&& previousCommandChar != '}' // don't attach } {
&& previousCommandChar != ';') // don't attach ; {
{
appendCharInsideComments();
}
else
{
appendCurrentChar(); // don't attach
}
}
else if (previousCommandChar == '{'
|| previousCommandChar == '}'
|| previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';'
{
appendCurrentChar(); // don't attach
}
else
{
// if a blank line preceeds this don't attach
if (isEmptyLine(formattedLine))
appendCurrentChar(); // don't attach
else if (isOkToBreakBlock(bracketType)
&& !(isImmediatelyPostPreprocessor
&& currentLineBeginsWithBracket)) // lineBeginsWith('{')
{
if (peekNextChar() != '}')
{
appendSpacePad();
appendCurrentChar(false); // OK to attach
testForTimeToSplitFormattedLine(); // line length will have changed
// should a following comment attach with the bracket?
// insert spaces to reposition the comment
if (isBeforeComment()
&& !isBeforeMultipleLineEndComments(charNum)
&& (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBracket)) // lineBeginsWith('{')
{
shouldBreakLineAtNextChar = true;
currentLine.insert(charNum+1, charNum+1, ' ');
}
}
else
{
appendSpacePad();
appendCurrentChar();
}
}
else
{
if (!isInLineBreak)
appendSpacePad();
appendCurrentChar(); // don't attach
}
}
}
}
/**
* format closing bracket
* currentChar contains the bracket
* the calling function should have a continue statement after calling this method
*
* @param bracketType the type of the opening bracket for this closing bracket.
*/
void ASFormatter::formatClosingBracket(BracketType bracketType)
{
assert(!isBracketType(bracketType, ARRAY_TYPE));
assert(currentChar == '}');
// parenStack must contain one entry
if (parenStack->size() > 1)
parenStack->pop_back();
// mark state of immediately after empty block
// this state will be used for locating brackets that appear immedately AFTER an empty block (e.g. '{} \n}').
if (previousCommandChar == '{')
isImmediatelyPostEmptyBlock = true;
if (shouldAttachClosingBracket)
{
// for now, namespaces and classes will be attached. Uncomment the lines below to break.
if ((isEmptyLine(formattedLine) // if a blank line preceeds this
|| isCharImmediatelyPostLineComment
|| isCharImmediatelyPostComment
|| (isImmediatelyPostPreprocessor && (int) currentLine.find_first_not_of(" \t") == charNum)
// || (isBracketType(bracketType, CLASS_TYPE) && isOkToBreakBlock(bracketType) && previousNonWSChar != '{')
// || (isBracketType(bracketType, NAMESPACE_TYPE) && isOkToBreakBlock(bracketType) && previousNonWSChar != '{')
)
&& (!isBracketType(bracketType, SINGLE_LINE_TYPE) || isOkToBreakBlock(bracketType)))
{
breakLine();
appendCurrentChar(); // don't attach
}
else
{
if (previousNonWSChar != '{'
&& (!isBracketType(bracketType, SINGLE_LINE_TYPE) || isOkToBreakBlock(bracketType)))
appendSpacePad();
appendCurrentChar(false); // attach
}
}
else if ((!(previousCommandChar == '{' && isPreviousBracketBlockRelated)) // this '{' does not close an empty block
&& isOkToBreakBlock(bracketType)) // astyle is allowed to break one line blocks
// && !isImmediatelyPostEmptyBlock) /* removed 9/5/10 */ // this '}' does not immediately follow an empty block
{
breakLine();
appendCurrentChar();
}
else
{
appendCurrentChar();
}
// if a declaration follows a definition, space pad
if (isLegalNameChar(peekNextChar()))
appendSpaceAfter();
if (shouldBreakBlocks && currentHeader != NULL && parenStack->back() == 0)
{
if (currentHeader == &AS_CASE || currentHeader == &AS_DEFAULT)
{
// do not yet insert a line if "break" statement is outside the brackets
string nextText = peekNextText(currentLine.substr(charNum+1));
if (nextText.substr(0, 5) != "break")
isAppendPostBlockEmptyLineRequested = true;
}
else
isAppendPostBlockEmptyLineRequested = true;
}
}
/**
* 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(isBracketType(bracketType, ARRAY_TYPE));
assert(currentChar == '{' || currentChar == '}');
if (currentChar == '{')
{
// is this the first opening bracket in the array?
if (isOpeningArrayBracket)
{
if (bracketFormatMode == ATTACH_MODE
|| bracketFormatMode == LINUX_MODE
|| bracketFormatMode == STROUSTRUP_MODE)
{
// don't attach to a preprocessor directive
if (isImmediatelyPostPreprocessor && currentLineBeginsWithBracket) // lineBeginsWith('{')
{
isInLineBreak = true;
appendCurrentChar(); // don't attach
}
else if (isCharImmediatelyPostComment)
{
// TODO: attach bracket to line-end comment
appendCurrentChar(); // don't attach
}
else if (isCharImmediatelyPostLineComment && !isBracketType(bracketType, SINGLE_LINE_TYPE))
{
appendCharInsideComments();
}
else
{
// if a blank line preceeds this don't attach
if (isEmptyLine(formattedLine))
appendCurrentChar(); // don't attach
else
{
// if bracket is broken or not an assignment
if (currentLineBeginsWithBracket // lineBeginsWith('{')
&& !isBracketType(bracketType, SINGLE_LINE_TYPE))
{
appendSpacePad();
appendCurrentChar(false); // OK to attach
// TODO: debug the following line
testForTimeToSplitFormattedLine(); // line length will have changed
if (currentLineBeginsWithBracket
&& (int)currentLineFirstBracketNum == charNum)
shouldBreakLineAtNextChar = true;
}
else
{
appendSpacePad();
appendCurrentChar();
}
}
}
}
else if (bracketFormatMode == BREAK_MODE)
{
if (isWhiteSpace(peekNextChar()))
breakLine();
else if (isBeforeAnyComment())
{
// do not break unless comment is at line end
if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBracket)
{
currentChar = ' '; // remove bracket from current line
appendOpeningBracket = true; // append bracket to following line
}
}
if (!isInLineBreak)
appendSpacePad();
appendCurrentChar();
if (currentLineBeginsWithBracket
&& (int)currentLineFirstBracketNum == charNum
&& !isBracketType(bracketType, SINGLE_LINE_TYPE))
shouldBreakLineAtNextChar = true;
}
else if (bracketFormatMode == RUN_IN_MODE)
{
if (isWhiteSpace(peekNextChar()))
breakLine();
else if (isBeforeAnyComment())
{
// do not break unless comment is at line end
if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBracket) // lineBeginsWith('{')
{
currentChar = ' '; // remove bracket from current line
appendOpeningBracket = true; // append bracket to following line
}
}
if (!isInLineBreak)
appendSpacePad();
appendCurrentChar();
}
else if (bracketFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBracket) // lineBeginsWith('{')
{
appendCurrentChar(); // don't attach
}
else
{
appendSpacePad();
appendCurrentChar(false); // OK to attach
}
}
}
else // not the first opening bracket
{
if (bracketFormatMode == RUN_IN_MODE)
{
if (previousNonWSChar == '{'
&& bracketTypeStack->size() > 2
&& !isBracketType((*bracketTypeStack)[bracketTypeStack->size()-2], SINGLE_LINE_TYPE))
formatArrayRunIn();
}
else if (!isInLineBreak
&& !isWhiteSpace(peekNextChar())
&& previousNonWSChar == '{'
&& bracketTypeStack->size() > 2
&& !isBracketType((*bracketTypeStack)[bracketTypeStack->size()-2], SINGLE_LINE_TYPE))
formatArrayRunIn();
appendCurrentChar();
}
}
else if (currentChar == '}')
{
if (shouldAttachClosingBracket)
{
if (isEmptyLine(formattedLine) // if a blank line preceeds this
|| isImmediatelyPostPreprocessor
|| isCharImmediatelyPostLineComment
|| isCharImmediatelyPostComment)
appendCurrentChar(); // don't attach
else
{
appendSpacePad();
appendCurrentChar(false); // attach
}
}
else
{
// does this close the first opening bracket in the array?
// must check if the block is still a single line because of anonymous statements
if (!isBracketType(bracketType, SINGLE_LINE_TYPE)
|| formattedLine.find('{') == string::npos)
breakLine();
appendCurrentChar();
}
// if a declaration follows an enum definition, space pad
char peekedChar = peekNextChar();
if (isLegalNameChar(peekedChar)
|| peekedChar == '[')
appendSpaceAfter();
}
}
/**
* determine if a run-in can be attached.
* if it can insert the indents in formattedLine and reset the current line break.
*/
void ASFormatter::formatRunIn()
{
assert(bracketFormatMode == RUN_IN_MODE || bracketFormatMode == NONE_MODE);
// keep one line blocks returns true without indenting the run-in
if (!isOkToBreakBlock(bracketTypeStack->back()))
return; // true;
// make sure the line begins with a bracket
size_t lastText = formattedLine.find_last_not_of(" \t");
if (lastText == string::npos || formattedLine[lastText] != '{')
return; // false;
// make sure the bracket is broken
if (formattedLine.find_first_not_of(" \t{") != string::npos)
return; // false;
if (isBracketType(bracketTypeStack->back(), NAMESPACE_TYPE))
return; // false;
bool extraIndent = false;
isInLineBreak = true;
// cannot attach a class modifier without indent-classes
if (isCStyle()
&& isCharPotentialHeader(currentLine, charNum)
&& (isBracketType(bracketTypeStack->back(), CLASS_TYPE)
|| (isBracketType(bracketTypeStack->back(), STRUCT_TYPE)
&& isInIndentableStruct)))
{
if (findKeyword(currentLine, charNum, AS_PUBLIC)
|| findKeyword(currentLine, charNum, AS_PRIVATE)
|| findKeyword(currentLine, charNum, AS_PROTECTED))
{
if (!getClassIndent())
return; // false;
}
else if (getClassIndent())
extraIndent = true;
}
// cannot attach a 'case' statement without indent-switches
if (!getSwitchIndent()
&& isCharPotentialHeader(currentLine, charNum)
&& (findKeyword(currentLine, charNum, AS_CASE)
|| findKeyword(currentLine, charNum, AS_DEFAULT)))
return; // false;
// extra indent for switch statements
if (getSwitchIndent()
&& !preBracketHeaderStack->empty()
&& preBracketHeaderStack->back() == &AS_SWITCH
&& ((isLegalNameChar(currentChar)
&& !findKeyword(currentLine, charNum, AS_CASE))
|| isSequenceReached("//")
|| isSequenceReached("/*")))
extraIndent = true;
isInLineBreak = false;
// remove for extra whitespace
if (formattedLine.length() > lastText+1
&& formattedLine.find_first_not_of(" \t", lastText+1) == string::npos)
formattedLine.erase(lastText+1);
if (getForceTabIndentation() && getIndentLength() != getTabLength())
{
// insert the space indents
string indent;
int indentLength_ = getIndentLength();
int tabLength_ = getTabLength();
indent.append(indentLength_, ' ');
if (extraIndent)
indent.append(indentLength_, ' ');
// replace spaces indents with tab indents
size_t tabCount = indent.length() / tabLength_; // truncate extra spaces
indent.erase(0U, tabCount * tabLength_);
indent.insert(0U, tabCount, '\t');
horstmannIndentChars = indentLength_;
if (indent[0] == ' ') // allow for bracket
indent.erase(0, 1);
formattedLine.append(indent);
}
else if (getIndentString() == "\t")
{
appendChar('\t', false);
horstmannIndentChars = 2; // one for { and one for tab
if (extraIndent)
{
appendChar('\t', false);
horstmannIndentChars++;
}
}
else // spaces
{
int indentLength_ = getIndentLength();
formattedLine.append(indentLength_ - 1, ' ');
horstmannIndentChars = indentLength_;
if (extraIndent)
{
formattedLine.append(indentLength_, ' ');
horstmannIndentChars += indentLength_;
}
}
isInHorstmannRunIn = true;
}
/**
* remove whitepace and add indentation for an array run-in.
*/
void ASFormatter::formatArrayRunIn()
{
assert(isBracketType(bracketTypeStack->back(), ARRAY_TYPE));
// make sure the bracket is broken
if (formattedLine.find_first_not_of(" \t{") != string::npos)
return;
size_t lastText = formattedLine.find_last_not_of(" \t");
if (lastText == string::npos || formattedLine[lastText] != '{')
return;
// check for extra whitespace
if (formattedLine.length() > lastText+1
&& formattedLine.find_first_not_of(" \t", lastText+1) == string::npos)
formattedLine.erase(lastText+1);
if (getIndentString() == "\t")
{
appendChar('\t', false);
horstmannIndentChars = 2; // one for { and one for tab
}
else
{
int indent = getIndentLength();
formattedLine.append(indent-1, ' ');
horstmannIndentChars = indent;
}
isInHorstmannRunIn = true;
isInLineBreak = false;
}
/**
* delete a bracketTypeStack vector object
* BracketTypeStack did not work with the DeleteContainer template
*/
void ASFormatter::deleteContainer(vector<BracketType>* &container)
{
if (container != NULL)
{
container->clear();
delete (container);
container = NULL;
}
}
/**
* delete a vector object
* T is the type of vector
* used for all vectors except bracketTypeStack
*/
template<typename T>
void ASFormatter::deleteContainer(T &container)
{
if (container != NULL)
{
container->clear();
delete (container);
container = NULL;
}
}
/**
* initialize a BracketType vector object
* BracketType did not work with the DeleteContainer template
*/
void ASFormatter::initContainer(vector<BracketType>* &container, vector<BracketType>* value)
{
if (container != NULL)
deleteContainer(container);
container = value;
}
/**
* initialize a vector object
* T is the type of vector
* used for all vectors except bracketTypeStack
*/
template<typename T>
void ASFormatter::initContainer(T &container, T value)
{
// since the ASFormatter object is never deleted,
// the existing vectors must be deleted before creating new ones
if (container != NULL)
deleteContainer(container);
container = value;
}
/**
* convert a tab to spaces.
* charNum points to the current character to convert to spaces.
* tabIncrementIn is the increment that must be added for tab indent characters
* to get the correct column for the current tab.
* replaces the tab in currentLine with the required number of spaces.
* replaces the value of currentChar.
*/
void ASFormatter::convertTabToSpaces()
{
assert(currentLine[charNum] == '\t');
assert(getTabLength() > 0);
// do NOT replace if in quotes
if (isInQuote || isInQuoteContinuation)
return;
size_t tabSize = getTabLength();
size_t numSpaces = tabSize - ((tabIncrementIn + charNum) % tabSize);
currentLine.replace(charNum, 1, numSpaces, ' ');
currentChar = currentLine[charNum];
}
/**
* is it ok to break this block?
*/
bool ASFormatter::isOkToBreakBlock(BracketType bracketType) const
{
// Actually, there should not be an ARRAY_TYPE bracket here.
// But this will avoid breaking a one line block when there is.
// Otherwise they will be formatted differently on consecutive runs.
if (isBracketType(bracketType, ARRAY_TYPE)
&& isBracketType(bracketType, SINGLE_LINE_TYPE))
return false;
if (!isBracketType(bracketType, SINGLE_LINE_TYPE)
|| shouldBreakOneLineBlocks
|| breakCurrentOneLineBlock)
return true;
return false;
}
/**
* check if a sharp header is a paren or nonparen header
*/
bool ASFormatter::isSharpStyleWithParen(const string* header) const
{
if (isSharpStyle() && peekNextChar() == '('
&& (header == &AS_CATCH
|| header == &AS_DELEGATE))
return true;
return false;
}
/**
* check for a following header when a comment is reached.
* if a header follows, the comments are kept as part of the header block.
* firstLine must contain the start of the comment.
*/
void ASFormatter::checkForHeaderFollowingComment(const string &firstLine)
{
assert(isInComment || isInLineComment);
// this is called ONLY IF shouldBreakBlocks is TRUE.
assert(shouldBreakBlocks);
// look ahead to find the next non-comment text
bool endOnEmptyLine = (currentHeader == NULL);
string nextText = peekNextText(firstLine, endOnEmptyLine);
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
return;
const string* newHeader = ASBeautifier::findHeader(nextText, 0, headers);
if (newHeader == NULL)
return;
// if a closing header, reset break unless break is requested
if (isClosingHeader(newHeader))
{
if (!shouldBreakClosingHeaderBlocks)
isPrependPostBlockEmptyLineRequested = false;
}
// if an opening header, break before the comment
else
{
isPrependPostBlockEmptyLineRequested = true;
}
}
/**
* process preprocessor statements.
* charNum should be the index of the #.
*
* delete bracketTypeStack entries added by #if if a #else is found.
* prevents double entries in the bracketTypeStack.
*/
void ASFormatter::processPreprocessor()
{
assert(currentChar == '#');
const size_t preproc = currentLine.find_first_not_of(" \t", charNum + 1);
if (preproc == string::npos)
return;
if (currentLine.compare(preproc, 2, "if") == 0)
{
preprocBracketTypeStackSize = bracketTypeStack->size();
}
else if (currentLine.compare(preproc, 4, "else") == 0)
{
// delete stack entries added in #if
// should be replaced by #else
if (preprocBracketTypeStackSize > 0)
{
int addedPreproc = bracketTypeStack->size() - preprocBracketTypeStackSize;
for (int i=0; i < addedPreproc; i++)
bracketTypeStack->pop_back();
}
}
}
/**
* determine if the next line starts a comment
* and a header follows the comment or comments.
*/
bool ASFormatter::commentAndHeaderFollows()
{
// called ONLY IF shouldDeleteEmptyLines and shouldBreakBlocks are TRUE.
assert(shouldDeleteEmptyLines && shouldBreakBlocks);
// is the next line a comment
if (!sourceIterator->hasMoreLines())
return false;
string nextLine_ = sourceIterator->peekNextLine();
size_t firstChar = nextLine_.find_first_not_of(" \t");
if (firstChar == string::npos
|| !(nextLine_.compare(firstChar, 2, "//") == 0
|| nextLine_.compare(firstChar, 2, "/*") == 0))
{
sourceIterator->peekReset();
return false;
}
// find the next non-comment text, and reset
string nextText = peekNextText(nextLine_, false, true);
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
return false;
const string* newHeader = ASBeautifier::findHeader(nextText, 0, headers);
if (newHeader == NULL)
return false;
// if a closing header, reset break unless break is requested
if (isClosingHeader(newHeader) && !shouldBreakClosingHeaderBlocks)
{
isAppendPostBlockEmptyLineRequested = false;
return false;
}
return true;
}
/**
* determine if a bracket should be attached or broken
* uses brackets in the bracketTypeStack
* the last bracket in the bracketTypeStack is the one being formatted
* returns true if the bracket should be broken
*/
bool ASFormatter::isCurrentBracketBroken() const
{
assert(bracketTypeStack->size() > 1);
bool breakBracket = false;
size_t bracketTypeStackEnd = bracketTypeStack->size()-1;
if (isBracketType((*bracketTypeStack)[bracketTypeStackEnd], EXTERN_TYPE))
{
if (currentLineBeginsWithBracket
|| bracketFormatMode == RUN_IN_MODE)
breakBracket = true;
}
else if (bracketFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBracket
&& (int)currentLineFirstBracketNum == charNum) // lineBeginsWith('{')
breakBracket = true;
}
else if (bracketFormatMode == BREAK_MODE || bracketFormatMode == RUN_IN_MODE)
{
breakBracket = true;
}
else if (bracketFormatMode == LINUX_MODE || bracketFormatMode == STROUSTRUP_MODE)
{
// break a class if Linux
if (isBracketType((*bracketTypeStack)[bracketTypeStackEnd], CLASS_TYPE))
{
if (bracketFormatMode == LINUX_MODE)
breakBracket = true;
}
// break a namespace or interface if Linux
else if (isBracketType((*bracketTypeStack)[bracketTypeStackEnd], NAMESPACE_TYPE)
|| isBracketType((*bracketTypeStack)[bracketTypeStackEnd], INTERFACE_TYPE))
{
if (bracketFormatMode == LINUX_MODE)
breakBracket = true;
}
// break the first bracket if a function
else if (bracketTypeStackEnd == 1
&& isBracketType((*bracketTypeStack)[bracketTypeStackEnd], COMMAND_TYPE))
{
breakBracket = true;
}
else if (bracketTypeStackEnd > 1)
{
// break the first bracket after a namespace or extern if a function
if (isBracketType((*bracketTypeStack)[bracketTypeStackEnd-1], NAMESPACE_TYPE)
|| isBracketType((*bracketTypeStack)[bracketTypeStackEnd-1], EXTERN_TYPE))
{
if (isBracketType((*bracketTypeStack)[bracketTypeStackEnd], COMMAND_TYPE))
breakBracket = true;
}
// if not C style then break the first bracket after a class if a function
else if (!isCStyle())
{
if ((isBracketType((*bracketTypeStack)[bracketTypeStackEnd-1], CLASS_TYPE)
|| isBracketType((*bracketTypeStack)[bracketTypeStackEnd-1], ARRAY_TYPE)
|| isBracketType((*bracketTypeStack)[bracketTypeStackEnd-1], STRUCT_TYPE))
&& isBracketType((*bracketTypeStack)[bracketTypeStackEnd], COMMAND_TYPE))
breakBracket = true;
}
}
}
return breakBracket;
}
/**
* format comment body
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatCommentBody()
{
assert(isInComment);
if (isSequenceReached("*/"))
{
isInComment = false;
noTrimCommentContinuation = false;
isImmediatelyPostComment = true;
appendSequence(AS_CLOSE_COMMENT);
goForward(1);
if (doesLineStartComment
&& (currentLine.find_first_not_of(" \t", charNum+1) == string::npos))
lineEndsInCommentOnly = true;
if (peekNextChar() == '}'
&& previousCommandChar != ';'
&& !isBracketType(bracketTypeStack->back(), ARRAY_TYPE)
&& !isInPreprocessor
&& isOkToBreakBlock(bracketTypeStack->back()))
{
isInLineBreak = true;
shouldBreakLineAtNextChar = true;
}
}
else
{
appendCurrentChar();
// append the comment up to the next tab or comment end
// tabs must be checked for convert-tabs before appending
while (charNum + 1 < (int) currentLine.length()
&& !isLineReady
&& currentLine[charNum+1] != '\t'
&& currentLine.compare(charNum+1, 2, "*/") != 0)
{
currentChar = currentLine[++charNum];
appendCurrentChar();
}
}
}
/**
* format a comment opener
* the comment opener will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatCommentOpener()
{
assert(isSequenceReached("/*"));
isInComment = isInCommentStartLine = true;
isImmediatelyPostLineComment = false;
if (spacePadNum != 0 && !isInLineBreak)
adjustComments();
formattedLineCommentNum = formattedLine.length();
// must be done BEFORE appendSequence
if (previousCommandChar == '{'
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment)
{
if (bracketFormatMode == NONE_MODE)
{
// should a run-in statement be attached?
if (currentLineBeginsWithBracket)
formatRunIn();
}
else if (bracketFormatMode == ATTACH_MODE)
{
// if the bracket was not attached?
if (formattedLine.length() > 0 && formattedLine[0] == '{'
&& !isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE))
isInLineBreak = true;
}
else if (bracketFormatMode == RUN_IN_MODE)
{
// should a run-in statement be attached?
if (formattedLine.length() > 0 && formattedLine[0] == '{')
formatRunIn();
}
}
else if (!doesLineStartComment)
noTrimCommentContinuation = true;
// appendSequence will write the previous line
appendSequence(AS_OPEN_COMMENT);
goForward(1);
// must be done AFTER appendSequence
if (shouldBreakBlocks)
{
// break before the comment if a header follows the comment
// for speed, do not check if previous line is empty,
// if previous line is a line comment or if previous line is '{'
if (doesLineStartComment
&& !isImmediatelyPostEmptyLine
&& !isImmediatelyPostCommentOnly
&& previousCommandChar != '{')
{
checkForHeaderFollowingComment(currentLine.substr(charNum-1));
}
}
if (previousCommandChar == '}')
currentHeader = NULL;
}
/**
* format a line comment body
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatLineCommentBody()
{
assert(isInLineComment);
appendCurrentChar();
// append the comment up to the next tab
// tabs must be checked for convert-tabs before appending
while (charNum + 1 < (int) currentLine.length()
&& !isLineReady
&& currentLine[charNum+1] != '\t')
{
currentChar = currentLine[++charNum];
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.
}
}
/**
* format a line comment opener
* the line comment opener will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatLineCommentOpener()
{
assert(isSequenceReached("//"));
if ((int)currentLine.length() > charNum + 2
&& currentLine[charNum+2] == '\xf2') // check for windows line marker
isAppendPostBlockEmptyLineRequested = false;
isInLineComment = true;
isCharImmediatelyPostComment = false;
// do not indent if in column 1 or 2
if (!shouldIndentCol1Comments && !lineCommentNoIndent)
{
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 && !isInLineBreak)
adjustComments();
formattedLineCommentNum = formattedLine.length();
// must be done BEFORE appendSequence
// check for run-in statement
if (previousCommandChar == '{'
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment)
{
if (bracketFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBracket)
formatRunIn();
}
else if (bracketFormatMode == RUN_IN_MODE)
{
if (!lineCommentNoIndent)
formatRunIn();
else
isInLineBreak = true;
}
else if (bracketFormatMode == BREAK_MODE)
{
if (formattedLine.length() > 0 && formattedLine[0] == '{')
isInLineBreak = true;
}
else
{
if (currentLineBeginsWithBracket)
isInLineBreak = true;
}
}
// appendSequence will write the previous line
appendSequence(AS_OPEN_LINE_COMMENT);
goForward(1);
if (formattedLine.compare(0, 2, "//") == 0)
lineIsLineCommentOnly = true;
// must be done AFTER appendSequence
if (shouldBreakBlocks)
{
// break before the comment if a header follows the line comment
// for speed, do not check if previous line is empty,
// if previous line is a comment or if previous line is '{'
if (lineIsLineCommentOnly
&& previousCommandChar != '{'
&& !isImmediatelyPostEmptyLine
&& !isImmediatelyPostCommentOnly)
{
checkForHeaderFollowingComment(currentLine.substr(charNum-1));
}
}
if (previousCommandChar == '}')
currentHeader = NULL;
// if tabbed input don't convert the immediately following tabs to spaces
if (getIndentString() == "\t" && lineCommentNoIndent)
{
while (charNum + 1 < (int) currentLine.length()
&& currentLine[charNum+1] == '\t')
{
currentChar = currentLine[++charNum];
appendCurrentChar();
}
}
// 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.
}
}
/**
* format quote body
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatQuoteBody()
{
assert(isInQuote);
if (isSpecialChar)
{
isSpecialChar = false;
}
else if (currentChar == '\\' && !isInVerbatimQuote)
{
if (peekNextChar() == ' ') // is this '\' at end of line
haveLineContinuationChar = true;
else
isSpecialChar = true;
}
else if (isInVerbatimQuote && currentChar == '"')
{
if (peekNextChar() == '"') // check consecutive quotes
{
appendSequence("\"\"");
goForward(1);
return;
}
else
{
isInQuote = false;
isInVerbatimQuote = false;
}
}
else if (quoteChar == currentChar)
{
isInQuote = false;
}
appendCurrentChar();
// append the text to the ending quoteChar or an escape sequence
// tabs in quotes are NOT changed by convert-tabs
if (isInQuote && currentChar != '\\')
{
while (charNum + 1 < (int) currentLine.length()
&& currentLine[charNum+1] != quoteChar
&& currentLine[charNum+1] != '\\')
{
currentChar = currentLine[++charNum];
appendCurrentChar();
}
}
}
/**
* format a quote opener
* the quote opener will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatQuoteOpener()
{
assert(currentChar == '"' || currentChar == '\'');
isInQuote = true;
quoteChar = currentChar;
if (isSharpStyle() && previousChar == '@')
isInVerbatimQuote = true;
// a quote following a bracket is an array
if (previousCommandChar == '{'
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment
&& isNonInStatementArray
&& !isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE)
&& !isWhiteSpace(peekNextChar()))
{
if (bracketFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBracket)
formatRunIn();
}
else if (bracketFormatMode == RUN_IN_MODE)
{
formatRunIn();
}
else if (bracketFormatMode == BREAK_MODE)
{
if (formattedLine.length() > 0 && formattedLine[0] == '{')
isInLineBreak = true;
}
else
{
if (currentLineBeginsWithBracket)
isInLineBreak = true;
}
}
previousCommandChar = ' ';
appendCurrentChar();
}
/**
* get the next line comment adjustment that results from breaking a closing bracket.
* the bracket must be on the same line as the closing header.
* i.e "} else" changed to "} \n else".
*/
int ASFormatter::getNextLineCommentAdjustment()
{
assert(foundClosingHeader && previousNonWSChar == '}');
if (charNum < 1) // "else" is in column 1
return 0;
size_t lastBracket = currentLine.rfind('}', charNum - 1);
if (lastBracket != string::npos)
return (lastBracket - charNum); // return a negative number
return 0;
}
// for console build only
LineEndFormat ASFormatter::getLineEndFormat() const
{
return lineEnd;
}
/**
* get the current line comment adjustment that results from attaching
* a closing header to a closing bracket.
* the bracket must be on the line previous to the closing header.
* the adjustment is 2 chars, one for the bracket and one for the space.
* i.e "} \n else" changed to "} else".
*/
int ASFormatter::getCurrentLineCommentAdjustment()
{
assert(foundClosingHeader && previousNonWSChar == '}');
if (charNum < 1)
return 2;
size_t lastBracket = currentLine.rfind('}', charNum - 1);
if (lastBracket == string::npos)
return 2;
return 0;
}
/**
* get the previous word on a line
* the argument 'currPos' must point to the current position.
*
* @return is the previous word or an empty string if none found.
*/
string ASFormatter::getPreviousWord(const string &line, int currPos) const
{
// get the last legal word (may be a number)
if (currPos == 0)
return string();
size_t end = line.find_last_not_of(" \t", currPos-1);
if (end == string::npos || !isLegalNameChar(line[end]))
return string();
int start; // start of the previous word
for (start = end; start > -1; start--)
{
if (!isLegalNameChar(line[start]) || line[start] == '.')
break;
}
start++;
return (line.substr(start, end-start+1));
}
/**
* check if a line break is needed when a closing bracket
* is followed by a closing header.
* the break depends on the bracketFormatMode and other factors.
*/
void ASFormatter::isLineBreakBeforeClosingHeader()
{
assert(foundClosingHeader && previousNonWSChar == '}');
if (bracketFormatMode == BREAK_MODE
|| bracketFormatMode == RUN_IN_MODE
|| shouldAttachClosingBracket)
{
isInLineBreak = true;
}
else if (bracketFormatMode == NONE_MODE)
{
if (shouldBreakClosingHeaderBrackets
|| getBracketIndent() || getBlockIndent())
{
isInLineBreak = true;
}
else
{
appendSpacePad();
// is closing bracket broken?
size_t i = currentLine.find_first_not_of(" \t");
if (i != string::npos && currentLine[i] == '}')
isInLineBreak = false;
if (shouldBreakBlocks)
isAppendPostBlockEmptyLineRequested = false;
}
}
// bracketFormatMode == ATTACH_MODE, LINUX_MODE, STROUSTRUP_MODE
else
{
if (shouldBreakClosingHeaderBrackets
|| getBracketIndent() || getBlockIndent())
{
isInLineBreak = true;
}
else
{
// if a blank line does not preceed this
// or last line is not a one line block, attach header
bool previousLineIsEmpty = isEmptyLine(formattedLine);
int previousLineIsOneLineBlock = 0;
size_t firstBracket = findNextChar(formattedLine, '{');
if (firstBracket != string::npos)
previousLineIsOneLineBlock = isOneLineBlockReached(formattedLine, firstBracket);
if (!previousLineIsEmpty
&& previousLineIsOneLineBlock == 0)
{
isInLineBreak = false;
appendSpacePad();
spacePadNum = 0; // don't count as comment padding
}
if (shouldBreakBlocks)
isAppendPostBlockEmptyLineRequested = false;
}
}
}
/**
* Add brackets to a single line statement following a header.
* Brackets are not added if the proper conditions are not met.
* Brackets are added to the currentLine.
*/
bool ASFormatter::addBracketsToStatement()
{
assert(isImmediatelyPostHeader);
if (currentHeader != &AS_IF
&& currentHeader != &AS_ELSE
&& currentHeader != &AS_FOR
&& currentHeader != &AS_WHILE
&& currentHeader != &AS_DO
&& currentHeader != &AS_FOREACH)
return false;
if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while
return false;
// do not bracket an empty statement
if (currentChar == ';')
return false;
// do not add if a header follows (i.e. else if)
if (isCharPotentialHeader(currentLine, charNum))
if (findHeader(headers) != NULL)
return false;
// find the next semi-colon
size_t nextSemiColon = charNum;
if (currentChar != ';')
nextSemiColon = findNextChar(currentLine, ';', charNum+1);
if (nextSemiColon == string::npos)
return false;
// add closing bracket before changing the line length
if (nextSemiColon == currentLine.length() - 1)
currentLine.append(" }");
else
currentLine.insert(nextSemiColon + 1, " }");
// add opening bracket
currentLine.insert(charNum, "{ ");
assert(computeChecksumIn("{}"));
currentChar = '{';
// remove extra spaces
if (!shouldAddOneLineBrackets)
{
size_t lastText = formattedLine.find_last_not_of(" \t");
if ((formattedLine.length() - 1) - lastText > 1)
formattedLine.erase(lastText + 1);
}
return true;
}
/**
* Find the next character that is not in quotes or a comment.
*
* @param line the line to be searched.
* @param searchChar the char to find.
* @param searchStart the start position on the line (default is 0).
* @return the position on the line or string::npos if not found.
*/
size_t ASFormatter::findNextChar(string &line, char searchChar, int searchStart /*0*/)
{
// find the next searchChar
size_t i;
for (i = searchStart; i < line.length(); i++)
{
if (line.compare(i, 2, "//") == 0)
return string::npos;
if (line.compare(i, 2, "/*") == 0)
{
size_t endComment = line.find("*/", i+2);
if (endComment == string::npos)
return string::npos;
i = endComment + 2;
if (i >= line.length())
return string::npos;
}
if (line[i] == '\'' || line[i] == '\"')
{
char quote = line[i];
while (i < line.length())
{
size_t endQuote = line.find(quote, i+1);
if (endQuote == string::npos)
return string::npos;
i = endQuote;
if (line[endQuote-1] != '\\') // check for '\"'
break;
if (line[endQuote-2] == '\\') // check for '\\'
break;
}
}
if (line[i] == searchChar)
break;
// for now don't process C# 'delegate' brackets
// do this last in case the search char is a '{'
if (line[i] == '{')
return string::npos;
}
if (i >= line.length()) // didn't find searchChar
return string::npos;
return i;
}
/**
* Look ahead in the file to see if a struct has access modifiers.
*
* @param line a reference to the line to indent.
* @param index the current line index.
* @return true if the struct has access modifiers.
*/
bool ASFormatter::isStructAccessModified(string &firstLine, size_t index) const
{
assert(firstLine[index] == '{');
assert(isCStyle());
bool isFirstLine = true;
bool needReset = false;
size_t bracketCount = 1;
string nextLine_ = firstLine.substr(index + 1);
// find the first non-blank text, bypassing all comments and quotes.
bool isInComment_ = false;
bool isInQuote_ = false;
char quoteChar_ = ' ';
while (sourceIterator->hasMoreLines())
{
if (isFirstLine)
isFirstLine = false;
else
{
nextLine_ = sourceIterator->peekNextLine();
needReset = true;
}
// parse the line
for (size_t i = 0; i < nextLine_.length(); i++)
{
if (isWhiteSpace(nextLine_[i]))
continue;
if (nextLine_.compare(i, 2, "/*") == 0)
isInComment_ = true;
if (isInComment_)
{
if (nextLine_.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (nextLine_[i] == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (nextLine_[i] == quoteChar_)
isInQuote_ = false;
continue;
}
if (nextLine_[i] == '"' || nextLine_[i] == '\'')
{
isInQuote_ = true;
quoteChar_ = nextLine_[i];
continue;
}
if (nextLine_.compare(i, 2, "//") == 0)
{
i = nextLine_.length();
continue;
}
// handle brackets
if (nextLine_[i] == '{')
++bracketCount;
if (nextLine_[i] == '}')
--bracketCount;
if (bracketCount == 0)
{
if (needReset)
sourceIterator->peekReset();
return false;
}
// check for access modifiers
if (isCharPotentialHeader(nextLine_, i))
{
if (findKeyword(nextLine_, i, AS_PUBLIC)
|| findKeyword(nextLine_, i, AS_PRIVATE)
|| findKeyword(nextLine_, i, AS_PROTECTED))
{
if (needReset)
sourceIterator->peekReset();
return true;
}
string name = getCurrentWord(nextLine_, i);
i += name.length() - 1;
}
} // end of for loop
} // end of while loop
if (needReset)
sourceIterator->peekReset();
return false;
}
/**
* Check to see if this is an EXEC SQL statement.
*
* @param line a reference to the line to indent.
* @param index the current line index.
* @return true if the statement is EXEC SQL.
*/
bool ASFormatter::isExecSQL(string &line, size_t index) const
{
if (line[index] != 'e' && line[index] != 'E') // quick check to reject most
return false;
string word;
if (isCharPotentialHeader(line, index))
word = getCurrentWord(line, index);
for (size_t i = 0; i < word.length(); i++)
word[i] = (char) toupper(word[i]);
if (word != "EXEC")
return false;
size_t index2 = index + word.length();
index2 = line.find_first_not_of(" \t", index2);
if (index2 == string::npos)
return false;
word.erase();
if (isCharPotentialHeader(line, index2))
word = getCurrentWord(line, index2);
for (size_t i = 0; i < word.length(); i++)
word[i] = (char) toupper(word[i]);
if (word != "SQL")
return false;
return true;
}
/**
* The continuation lines must be adjusted so the leading spaces
* is equivalent to the text on the opening line.
*
* Updates currentLine and charNum.
*/
void ASFormatter::trimContinuationLine()
{
assert(getTabLength() > 0);
size_t len = currentLine.length();
size_t tabSize = getTabLength();
charNum = 0;
if (leadingSpaces > 0 && len > 0)
{
size_t i;
size_t continuationIncrementIn = 0;
for (i = 0; (i < len) && (i + continuationIncrementIn < leadingSpaces); i++)
{
if (!isWhiteSpace(currentLine[i])) // don't delete any text
{
if (i < continuationIncrementIn)
leadingSpaces = i + tabIncrementIn;
continuationIncrementIn = tabIncrementIn;
break;
}
if (currentLine[i] == '\t')
continuationIncrementIn += tabSize - 1 - ((continuationIncrementIn + i) % tabSize);
}
if ((int) continuationIncrementIn == tabIncrementIn)
charNum = i;
else
{
// build a new line with the equivalent leading chars
string newLine;
int leadingChars = 0;
if ((int) leadingSpaces > tabIncrementIn)
leadingChars = leadingSpaces - tabIncrementIn;
newLine.append(leadingChars, ' ');
newLine.append(currentLine, i, len-i);
currentLine = newLine;
charNum = leadingChars;
if (currentLine.length() == 0)
currentLine = string(" "); // a null is inserted if this is not done
}
if (i >= len)
charNum = 0;
}
return;
}
/**
* Determine if a header is a closing header
*
* @return true if the header is a closing header.
*/
bool ASFormatter::isClosingHeader(const string* header) const
{
return (header == &AS_ELSE
|| header == &AS_CATCH
|| header == &AS_FINALLY);
}
/**
* Determine if a * following a closing paren is immediately.
* after a cast. If so it is a dereference and not a multiply.
* e.g. "(int*) *ptr" is a dereference.
*/
bool ASFormatter::isImmediatelyPostCast() const
{
assert(previousNonWSChar == ')' && currentChar == '*');
// find preceeding closing paren
size_t paren = currentLine.rfind(")", charNum);
if (paren == string::npos || paren == 0)
return false;
// find character preceeding the closing paren
size_t lastChar = currentLine.find_last_not_of(" \t", paren-1);
if (lastChar == string::npos)
return false;
// check for pointer cast
if (currentLine[lastChar] == '*')
return true;
return false;
}
/**
* Determine if a < is a template definition or instantiation.
* Sets the class variables isInTemplate and templateDepth.
*/
void ASFormatter::checkIfTemplateOpener()
{
assert(!isInTemplate && currentChar == '<');
int parenDepth_ = 0;
int maxTemplateDepth = 0;
templateDepth = 0;
for (size_t i = charNum; i < currentLine.length(); i++)
{
char currentChar_ = currentLine[i];
if (isWhiteSpace(currentChar_))
continue;
if (currentChar_ == '<')
{
templateDepth++;
maxTemplateDepth++;
}
else if (currentChar_ == '>')
{
templateDepth--;
if (templateDepth == 0)
{
if (parenDepth_ == 0)
{
// this is a template!
isInTemplate = true;
templateDepth = maxTemplateDepth;
}
return;
}
}
else if (currentChar_ == '(' || currentChar_ == ')')
{
if (currentChar_ == '(')
parenDepth_++;
else
parenDepth_--;
continue;
}
else if (currentLine.compare(i, 2, "&&") == 0
|| currentLine.compare(i, 2, "||") == 0)
{
// this is not a template -> leave...
isInTemplate = false;
return;
}
else if (currentChar_ == ',' // comma, e.g. A<int, char>
|| currentChar_ == '&' // reference, e.g. A<int&>
|| currentChar_ == '*' // pointer, e.g. A<int*>
|| currentChar_ == '^' // C++/CLI managed pointer, e.g. A<int^>
|| currentChar_ == ':' // ::, e.g. std::string
|| currentChar_ == '=' // assign e.g. default parameter
|| currentChar_ == '[' // [] e.g. string[]
|| currentChar_ == ']' // [] e.g. string[]
|| currentChar_ == '(' // (...) e.g. function definition
|| currentChar_ == ')') // (...) e.g. function definition
{
continue;
}
else if (!isLegalNameChar(currentChar_) && currentChar_ != '?')
{
// this is not a template -> leave...
isInTemplate = false;
return;
}
}
}
void ASFormatter::updateFormattedLineSplitPoints(char appendedChar)
{
assert(formattedLine.length() > 0);
if (!isOkToSplitFormattedLine())
return;
char nextChar = peekNextChar();
// don't split before or after a bracket
if (appendedChar == '{' || appendedChar == '}'
|| previousNonWSChar == '{' || previousNonWSChar == '}'
|| nextChar == '{' || nextChar == '}'
|| currentChar == '{' || currentChar == '}') // currentChar tests for an appended bracket
return;
// don't split before or after a block paren
if (appendedChar == '[' || appendedChar == ']'
|| previousNonWSChar == '['
|| nextChar == '[' || nextChar == ']')
return;
// don't split before an end of line comment
if (nextChar == '/')
return;
if (isWhiteSpace(appendedChar))
{
if (nextChar != ')' // empty parens
&& currentChar != ')' // appended space preceeding a paren
&& nextChar != '/' // following comment
&& currentChar != '(' // appended space after a paren
&& previousNonWSChar != '(' // decided at the '('
&& !(nextChar == '*'
&& !isCharPotentialOperator(previousNonWSChar)
&& pointerAlignment == PTR_ALIGN_TYPE)
&& !(nextChar == '&'
&& !isCharPotentialOperator(previousNonWSChar)
&& (referenceAlignment == REF_ALIGN_TYPE
|| (referenceAlignment == REF_SAME_AS_PTR && pointerAlignment == PTR_ALIGN_TYPE)))
&& !(nextChar == '('
&& !isCharPotentialOperator(previousNonWSChar)) // not an operator followed by a paren
&& !(currentChar == '('
&& !isCharPotentialOperator(previousNonWSChar)) // appended space between a non operator followed by a paren
// NO && !(previousNonWSChar == '(' && nextChar == '(') // space between opening parens
// NO && !(currentChar == '(' && nextChar == '(') // appended space between opening parens
// NO && !(previousNonWSChar == '(' && nextChar == '"') // space after a paren followed by a quote
// NO && !(currentChar == '(' && nextChar == '"') // appended space after a paren followed by a quote
)
{
if (maxWhiteSpace == 0 || formattedLine.length() < maxCodeLength)
maxWhiteSpace = formattedLine.length() - 1;
else
maxWhiteSpacePending = formattedLine.length() -1;
}
}
// unpadded operators may split before the operator (counts as whitespace)
else if (isSplittableOperator(appendedChar))
{
if (charNum > 0
&& (isLegalNameChar(currentLine[charNum - 1]) || currentLine[charNum - 1] == ')'))
{
if (formattedLine.length() + 1 < maxCodeLength)
maxWhiteSpace = formattedLine.length();
else if (maxWhiteSpace == 0 || formattedLine.length() < maxCodeLength)
maxWhiteSpace = formattedLine.length() - 1;
else
maxWhiteSpacePending = formattedLine.length() - 1;
}
}
// unpadded closing parens may split after the paren (counts as whitespace)
else if (appendedChar == ')')
{
char adjacentChar = ' ';
if (charNum + 1 < (int) currentLine.length())
adjacentChar = currentLine[charNum + 1];
if (previousNonWSChar != '(' // empty parens
&& adjacentChar != ' '
&& adjacentChar != ';'
&& adjacentChar != ','
&& adjacentChar != '.')
{
if (maxWhiteSpace == 0 || formattedLine.length() < maxCodeLength)
maxWhiteSpace = formattedLine.length();
else
maxWhiteSpacePending = formattedLine.length();
}
}
else if (appendedChar == ',')
{
if (maxComma == 0 || formattedLine.length() < maxCodeLength)
maxComma = formattedLine.length();
else
maxCommaPending = formattedLine.length();
}
else if (appendedChar == '(')
{
// a following quote is something like wxT("..."), do not break
if (nextChar != ')' && nextChar != '(' && nextChar != '"' && nextChar != '\'')
{
// if follows an operator break before
size_t parenNum;
if (isCharPotentialOperator(previousNonWSChar))
parenNum = formattedLine.length() - 1 ;
else
parenNum = formattedLine.length();
if (maxParen == 0 || formattedLine.length() < maxCodeLength)
maxParen = parenNum;
else
maxParenPending = parenNum;
}
}
else if (appendedChar == ';')
{
if (nextChar != ' ' && nextChar != '}' && nextChar != '/') // check for following comment
{
if (maxSemi == 0 || formattedLine.length() < maxCodeLength)
maxSemi = formattedLine.length();
else
maxSemiPending = formattedLine.length();
}
}
}
void ASFormatter::updateFormattedLineSplitPointSequence(const string &sequence)
{
assert(formattedLine.length() > 0);
if (!isOkToSplitFormattedLine())
return;
// check for logical conditional
if (sequence == "||" || sequence == "&&"|| sequence == "or"|| sequence == "and")
{
if (shouldBreakLineAfterLogical)
maxAndOr = formattedLine.length();
else
maxAndOr = formattedLine.length() - sequence.length();
}
// unpadded comparison operators will split after the operator (counts as whitespace)
else if (sequence == "==" || sequence == "!="|| sequence == ">="|| sequence == "<=")
{
if (maxWhiteSpace == 0 || formattedLine.length() < maxCodeLength)
maxWhiteSpace = formattedLine.length();
else
maxWhiteSpacePending = formattedLine.length();
}
}
bool ASFormatter::isSplittableOperator(char appendedChar) const
{
return (appendedChar == '+' || appendedChar == '-' || appendedChar == '='
|| appendedChar == ':' || appendedChar == '?');
}
bool ASFormatter::isOkToSplitFormattedLine()
{
// Is it OK to split the line?
if (shouldKeepLineUnbroken
|| isInLineComment
|| isInComment
|| isInQuote
|| isInBlParen
|| isInPreprocessor
|| isInExecSQL
|| isInAsm || isInAsmOneLine || isInAsmBlock
|| isInTemplate)
return false;
if (!isOkToBreakBlock(bracketTypeStack->back())
|| isBracketType(bracketTypeStack->back(), ARRAY_TYPE))
{
shouldKeepLineUnbroken = true;
clearFormattedLineSplitPoints();
return false;
}
return true;
}
/* This is called if the option maxCodeLength is set.
This can be called either before the characters are attached to the formattedLine
or after. If attached before, the sequenceLength of the attached characters must
be sent as a parameter. If attached after, the sequenceLength is NOT sent.
*/
void ASFormatter::testForTimeToSplitFormattedLine(int sequenceLength /* 0 */)
{
// should the line be split
if (formattedLine.length() > maxCodeLength && !isLineReady)
{
//if (charNum + sequenceLength == (int) currentLine.length())
//{
// isInLineBreak = true; // break when this sequence is attached
// return;
//}
size_t splitPoint = findFormattedLineSplitPoint(sequenceLength);
if (splitPoint > 0)
{
string splitLine = formattedLine.substr(splitPoint);
formattedLine = formattedLine.substr(0, splitPoint);
breakLine(true);
formattedLine = splitLine;
// adjust max split points
maxAndOr = (maxAndOr > splitPoint) ? (maxAndOr - splitPoint) : 0;
maxSemi = (maxSemi > splitPoint) ? (maxSemi - splitPoint) : 0;
maxComma = (maxComma > splitPoint) ? (maxComma - splitPoint) : 0;
maxParen = (maxParen > splitPoint) ? (maxParen - splitPoint) : 0;
maxWhiteSpace = (maxWhiteSpace > splitPoint) ? (maxWhiteSpace - splitPoint) : 0;
if (maxSemiPending > 0)
{
maxSemi = (maxSemiPending > splitPoint) ? (maxSemiPending - splitPoint) : 0;
maxSemiPending = 0;
}
if (maxCommaPending > 0)
{
maxComma = (maxCommaPending > splitPoint) ? (maxCommaPending - splitPoint) : 0;
maxCommaPending = 0;
}
if (maxParenPending > 0)
{
maxParen = (maxParenPending > splitPoint) ? (maxParenPending - splitPoint) : 0;
maxParenPending = 0;
}
if (maxWhiteSpacePending > 0)
{
maxWhiteSpace = (maxWhiteSpacePending > splitPoint) ? (maxWhiteSpacePending - splitPoint) : 0;
maxWhiteSpacePending = 0;
}
// don't allow an empty formatted line
size_t firstText = formattedLine.find_first_not_of(" \t");
if (firstText == string::npos && formattedLine.length() > 0)
{
formattedLine.erase();
clearFormattedLineSplitPoints();
if (isWhiteSpace(currentChar))
for (size_t i = charNum+1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
goForward(1);
}
else if (firstText > 0)
{
formattedLine.erase(0, firstText);
maxSemi = (maxSemi > firstText) ? (maxSemi - firstText) : 0;
maxAndOr = (maxAndOr > firstText) ? (maxAndOr - firstText) : 0;
maxComma = (maxComma > firstText) ? (maxComma - firstText) : 0;
maxParen = (maxParen > firstText) ? (maxParen - firstText) : 0;
maxWhiteSpace = (maxWhiteSpace > firstText) ? (maxWhiteSpace - firstText) : 0;
}
// reset formattedLineCommentNum
if (formattedLineCommentNum != string::npos)
{
formattedLineCommentNum = formattedLine.find("//");
if (formattedLineCommentNum == string::npos)
formattedLineCommentNum = formattedLine.find("/*");
}
}
}
}
size_t ASFormatter::findFormattedLineSplitPoint(int sequenceLength) const
{
// don't split if the last char is a semi-colon or space
if (formattedLine.length() == maxCodeLength + 1
&& (currentChar == ';' || currentChar == ' '))
return 0;
// determine where to split
size_t indentLength_ = static_cast<size_t>(getIndentLength());
size_t splitPoint = 0;
if (maxSemi > 0)
splitPoint = maxSemi;
else if (maxAndOr > 0)
splitPoint = maxAndOr;
else if (maxComma > 0)
splitPoint = maxComma;
size_t minCodeLength = (indentLength_ * 2) + 2;
if (splitPoint < minCodeLength)
splitPoint = 0;
// use maxParen instead of whitespace if it is long enough
if (splitPoint == 0
&& maxParen > minCodeLength
&& (maxParen > maxWhiteSpace
|| maxParen > (maxCodeLength * .7)
|| maxWhiteSpace > maxCodeLength))
splitPoint = maxParen;
// use whitespace or paren if available
if (splitPoint == 0)
splitPoint = maxWhiteSpace;
if (splitPoint == 0
&& maxParen > 0)
splitPoint = maxParen;
// replace split point with first available break point
if (splitPoint < minCodeLength)
{
splitPoint = string::npos;
if (maxSemiPending > 0 && maxSemiPending < splitPoint)
splitPoint = maxSemiPending;
// if (maxAndOrPending > 0 && maxAndOrPending < splitPoint) // TODO: Fix This
// splitPoint = maxAndOrPending;
if (maxCommaPending > 0 && maxCommaPending < splitPoint)
splitPoint = maxCommaPending;
if (maxParenPending > 0 && maxParenPending < splitPoint)
splitPoint = maxParenPending;
if (maxWhiteSpacePending > 0 && maxWhiteSpacePending < splitPoint)
splitPoint = maxWhiteSpacePending;
if (splitPoint == string::npos)
splitPoint = 0;
}
// Don't split if near the end.
int remainingCurrent = currentLine.length() - (charNum + sequenceLength);
if (remainingCurrent == 0)
{
if (formattedLine.length() <= maxCodeLength
|| formattedLine.length() <= splitPoint
|| (splitPoint >= maxCodeLength
&& formattedLine.length() <= maxCodeLength + 2))
splitPoint = 0;
}
return splitPoint;
}
//int ASFormatter::findRemainingPadding() const
//{
// // find remaining padding on the current line
// // err on the side of accumulating too much so the line will be broken
// int remainingPadding = 0;
// if ((int) currentLine.length() <= charNum + 1)
// return remainingPadding;
// string searchLine = currentLine.substr(charNum);
// char prevChar = searchLine[0];
// char currChar;
// char nextChar;
// for (size_t i = 1; i < searchLine.length(); i++)
// {
// currChar = searchLine[i];
// nextChar = (i+1 >= searchLine.length()) ? ' ' : searchLine[i+1];
// if (currChar == ',' && nextChar != ' ')
// ++remainingPadding;
// if (currChar == '(')
// {
// if (shouldPadParensOutside && prevChar != ' ')
// ++remainingPadding;
// if (shouldPadParensInside && nextChar != ' '
// && (prevChar != '(' && shouldPadParensInside)) // to avoid double counting '(('
// ++remainingPadding;
// }
// if (currChar == ')')
// {
// if (shouldPadParensInside && prevChar != ' '
// && (prevChar != ')' && shouldPadParensOutside)) // to avoid double counting '))'
// ++remainingPadding;
// if (shouldPadParensOutside && nextChar != ' ' && nextChar != ';')
// ++remainingPadding;
// }
// prevChar = currChar;
// }
// return remainingPadding;
//}
void ASFormatter::clearFormattedLineSplitPoints()
{
maxSemi = 0;
maxAndOr = 0;
maxComma = 0;
maxParen = 0;
maxWhiteSpace = 0;
maxSemiPending = 0;
maxCommaPending = 0;
maxParenPending = 0;
maxWhiteSpacePending = 0;
}
/**
* Compute the input checksum.
* This is called as an assert so it for is debug config only
*/
bool ASFormatter::computeChecksumIn(const string &currentLine_)
{
for (size_t i = 0; i < currentLine_.length(); i++)
if (!isWhiteSpace(currentLine_[i]))
checksumIn += currentLine_[i];
return true;
}
/**
* get the value of checksumIn for unit testing
*
* @return checksumIn.
*/
size_t ASFormatter::getChecksumIn() const
{
return checksumIn;
}
/**
* Compute the output checksum.
* This is called as an assert so it is for debug config only
*/
bool ASFormatter::computeChecksumOut(const string &beautifiedLine)
{
for (size_t i = 0; i < beautifiedLine.length(); i++)
if (!isWhiteSpace(beautifiedLine[i]))
checksumOut += beautifiedLine[i];
return true;
}
/**
* Return isLineReady for the final check at end of file.
*/
bool ASFormatter::getIsLineReady() const
{
return isLineReady;
}
/**
* get the value of checksumOut for unit testing
*
* @return checksumOut.
*/
size_t ASFormatter::getChecksumOut() const
{
return checksumOut;
}
/**
* Return the difference in checksums.
* If zero all is okay.
*/
int ASFormatter::getChecksumDiff() const
{
return checksumOut - checksumIn;
}
// for unit testing
int ASFormatter::getFormatterFileType() const
{ return formatterFileType; }
// Check if an operator follows the next word.
// The next word must be a legal name.
const string* ASFormatter::getFollowingOperator() const
{
// find next word
size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextNum == string::npos)
return NULL;
if (!isLegalNameChar(currentLine[nextNum]))
return NULL;
// bypass next word and following spaces
while (nextNum < currentLine.length())
{
if (!isLegalNameChar(currentLine[nextNum])
&& !isWhiteSpace(currentLine[nextNum]))
break;
nextNum++;
}
if (nextNum >= currentLine.length()
|| !isCharPotentialOperator(currentLine[nextNum])
|| currentLine[nextNum] == '/') // comment
return NULL;
const string* newOperator = ASBeautifier::findOperator(currentLine, nextNum, operators);
return newOperator;
}
// Check following data to determine if the current character is an array operator.
bool ASFormatter::isArrayOperator() const
{
assert(currentChar == '*' || currentChar == '&');
assert(isBracketType(bracketTypeStack->back(), ARRAY_TYPE));
// find next word
size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextNum == string::npos)
return false;
if (!isLegalNameChar(currentLine[nextNum]))
return false;
// bypass next word and following spaces
while (nextNum < currentLine.length())
{
if (!isLegalNameChar(currentLine[nextNum])
&& !isWhiteSpace(currentLine[nextNum]))
break;
nextNum++;
}
// check for characters that indicate an operator
if (currentLine[nextNum] == ','
|| currentLine[nextNum] == '}'
|| currentLine[nextNum] == ')'
|| currentLine[nextNum] == '(')
return true;
return false;
}
} // end namespace astyle