openjpeg/thirdparty/astyle/ASFormatter.cpp

7715 lines
216 KiB
C++
Executable File

// ASFormatter.cpp
// Copyright (c) 2017 by Jim Pattee <jimp03@email.com>.
// This code is licensed under the MIT License.
// License.md describes the conditions under which this software may be distributed.
//-----------------------------------------------------------------------------
// headers
//-----------------------------------------------------------------------------
#include "astyle.h"
#include <algorithm>
#include <fstream>
//-----------------------------------------------------------------------------
// astyle namespace
//-----------------------------------------------------------------------------
namespace astyle {
//
//-----------------------------------------------------------------------------
// ASFormatter class
//-----------------------------------------------------------------------------
/**
* Constructor of ASFormatter
*/
ASFormatter::ASFormatter()
{
sourceIterator = nullptr;
enhancer = new ASEnhancer;
preBraceHeaderStack = nullptr;
braceTypeStack = nullptr;
parenStack = nullptr;
structStack = nullptr;
questionMarkStack = nullptr;
lineCommentNoIndent = false;
formattingStyle = STYLE_NONE;
braceFormatMode = NONE_MODE;
pointerAlignment = PTR_ALIGN_NONE;
referenceAlignment = REF_SAME_AS_PTR;
objCColonPadMode = COLON_PAD_NO_CHANGE;
lineEnd = LINEEND_DEFAULT;
maxCodeLength = string::npos;
shouldPadCommas = false;
shouldPadOperators = false;
shouldPadParensOutside = false;
shouldPadFirstParen = false;
shouldPadParensInside = false;
shouldPadHeader = false;
shouldStripCommentPrefix = false;
shouldUnPadParens = false;
attachClosingBraceMode = false;
shouldBreakOneLineBlocks = true;
shouldBreakOneLineHeaders = false;
shouldBreakOneLineStatements = true;
shouldConvertTabs = false;
shouldIndentCol1Comments = false;
shouldIndentPreprocBlock = false;
shouldCloseTemplates = false;
shouldAttachExternC = false;
shouldAttachNamespace = false;
shouldAttachClass = false;
shouldAttachClosingWhile = false;
shouldAttachInline = false;
shouldBreakBlocks = false;
shouldBreakClosingHeaderBlocks = false;
shouldBreakClosingHeaderBraces = false;
shouldDeleteEmptyLines = false;
shouldBreakElseIfs = false;
shouldBreakLineAfterLogical = false;
shouldAddBraces = false;
shouldAddOneLineBraces = false;
shouldRemoveBraces = false;
shouldPadMethodColon = false;
shouldPadMethodPrefix = false;
shouldUnPadMethodPrefix = false;
shouldPadReturnType = false;
shouldUnPadReturnType = false;
shouldPadParamType = false;
shouldUnPadParamType = false;
// initialize ASFormatter member vectors
formatterFileType = 9; // reset to an invalid type
headers = new vector<const string*>;
nonParenHeaders = new vector<const string*>;
preDefinitionHeaders = new vector<const string*>;
preCommandHeaders = new vector<const string*>;
operators = new vector<const string*>;
assignmentOperators = new vector<const string*>;
castOperators = new vector<const string*>;
// initialize ASEnhancer member vectors
indentableMacros = new vector<const pair<const string, const string>* >;
}
/**
* Destructor of ASFormatter
*/
ASFormatter::~ASFormatter()
{
// delete ASFormatter stack vectors
deleteContainer(preBraceHeaderStack);
deleteContainer(braceTypeStack);
deleteContainer(parenStack);
deleteContainer(structStack);
deleteContainer(questionMarkStack);
// delete ASFormatter member vectors
formatterFileType = 9; // reset to an invalid type
delete headers;
delete nonParenHeaders;
delete preDefinitionHeaders;
delete preCommandHeaders;
delete operators;
delete assignmentOperators;
delete castOperators;
// delete ASEnhancer member vectors
delete indentableMacros;
// must be done when the ASFormatter object is deleted (not ASBeautifier)
// delete ASBeautifier member vectors
ASBeautifier::deleteBeautifierVectors();
delete enhancer;
}
/**
* initialize the ASFormatter.
*
* init() should be called every time a ASFormatter object is to start
* formatting a NEW source file.
* init() receives a pointer to a ASSourceIterator object that will be
* used to iterate through the source code.
*
* @param si a pointer to the ASSourceIterator or ASStreamIterator object.
*/
void ASFormatter::init(ASSourceIterator* si)
{
buildLanguageVectors();
fixOptionVariableConflicts();
ASBeautifier::init(si);
sourceIterator = si;
enhancer->init(getFileType(),
getIndentLength(),
getTabLength(),
getIndentString() == "\t",
getForceTabIndentation(),
getNamespaceIndent(),
getCaseIndent(),
shouldIndentPreprocBlock,
getPreprocDefineIndent(),
getEmptyLineFill(),
indentableMacros);
initContainer(preBraceHeaderStack, new vector<const string*>);
initContainer(parenStack, new vector<int>);
initContainer(structStack, new vector<bool>);
initContainer(questionMarkStack, new vector<bool>);
parenStack->emplace_back(0); // parenStack must contain this default entry
initContainer(braceTypeStack, new vector<BraceType>);
braceTypeStack->emplace_back(NULL_TYPE); // braceTypeStack must contain this default entry
clearFormattedLineSplitPoints();
currentHeader = nullptr;
currentLine = "";
readyFormattedLine = "";
formattedLine = "";
verbatimDelimiter = "";
currentChar = ' ';
previousChar = ' ';
previousCommandChar = ' ';
previousNonWSChar = ' ';
quoteChar = '"';
preprocBlockEnd = 0;
charNum = 0;
checksumIn = 0;
checksumOut = 0;
currentLineFirstBraceNum = string::npos;
formattedLineCommentNum = 0;
leadingSpaces = 0;
previousReadyFormattedLineLength = string::npos;
preprocBraceTypeStackSize = 0;
spacePadNum = 0;
nextLineSpacePadNum = 0;
objCColonAlign = 0;
templateDepth = 0;
squareBracketCount = 0;
runInIndentChars = 0;
tabIncrementIn = 0;
previousBraceType = NULL_TYPE;
isVirgin = true;
isInVirginLine = true;
isInLineComment = false;
isInComment = false;
isInCommentStartLine = false;
noTrimCommentContinuation = false;
isInPreprocessor = false;
isInPreprocessorBeautify = false;
doesLineStartComment = false;
lineEndsInCommentOnly = false;
lineIsCommentOnly = false;
lineIsLineCommentOnly = false;
lineIsEmpty = false;
isImmediatelyPostCommentOnly = false;
isImmediatelyPostEmptyLine = false;
isInClassInitializer = false;
isInQuote = false;
isInVerbatimQuote = false;
haveLineContinuationChar = false;
isInQuoteContinuation = false;
isHeaderInMultiStatementLine = false;
isSpecialChar = false;
isNonParenHeader = false;
foundNamespaceHeader = false;
foundClassHeader = false;
foundStructHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundPreCommandMacro = false;
foundTrailingReturnType = false;
foundCastOperator = false;
foundQuestionMark = false;
isInLineBreak = false;
endOfAsmReached = false;
endOfCodeReached = false;
isFormattingModeOff = false;
isInEnum = false;
isInExecSQL = false;
isInAsm = false;
isInAsmOneLine = false;
isInAsmBlock = false;
isLineReady = false;
elseHeaderFollowsComments = false;
caseHeaderFollowsComments = false;
isPreviousBraceBlockRelated = false;
isInPotentialCalculation = false;
needHeaderOpeningBrace = false;
shouldBreakLineAtNextChar = false;
shouldKeepLineUnbroken = false;
shouldReparseCurrentChar = false;
passedSemicolon = false;
passedColon = false;
isImmediatelyPostNonInStmt = false;
isCharImmediatelyPostNonInStmt = false;
isInTemplate = false;
isImmediatelyPostComment = false;
isImmediatelyPostLineComment = false;
isImmediatelyPostEmptyBlock = false;
isImmediatelyPostObjCMethodPrefix = false;
isImmediatelyPostPreprocessor = false;
isImmediatelyPostReturn = false;
isImmediatelyPostThrow = false;
isImmediatelyPostNewDelete = false;
isImmediatelyPostOperator = false;
isImmediatelyPostTemplate = false;
isImmediatelyPostPointerOrReference = false;
isCharImmediatelyPostReturn = false;
isCharImmediatelyPostThrow = false;
isCharImmediatelyPostNewDelete = false;
isCharImmediatelyPostOperator = false;
isCharImmediatelyPostComment = false;
isPreviousCharPostComment = false;
isCharImmediatelyPostLineComment = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
isCharImmediatelyPostTemplate = false;
isCharImmediatelyPostPointerOrReference = false;
isInObjCInterface = false;
isInObjCMethodDefinition = false;
isInObjCReturnType = false;
isInObjCSelector = false;
breakCurrentOneLineBlock = false;
shouldRemoveNextClosingBrace = false;
isInBraceRunIn = false;
currentLineBeginsWithBrace = false;
isPrependPostBlockEmptyLineRequested = false;
isAppendPostBlockEmptyLineRequested = false;
isIndentableProprocessor = false;
isIndentableProprocessorBlock = false;
prependEmptyLine = false;
appendOpeningBrace = false;
foundClosingHeader = false;
isImmediatelyPostHeader = false;
isInHeader = false;
isInCase = false;
isFirstPreprocConditional = false;
processedFirstConditional = false;
isJavaStaticConstructor = false;
}
/**
* build vectors for each programing language
* depending on the file extension.
*/
void ASFormatter::buildLanguageVectors()
{
if (getFileType() == formatterFileType) // don't build unless necessary
return;
formatterFileType = getFileType();
headers->clear();
nonParenHeaders->clear();
preDefinitionHeaders->clear();
preCommandHeaders->clear();
operators->clear();
assignmentOperators->clear();
castOperators->clear();
indentableMacros->clear(); // ASEnhancer
ASResource::buildHeaders(headers, getFileType());
ASResource::buildNonParenHeaders(nonParenHeaders, getFileType());
ASResource::buildPreDefinitionHeaders(preDefinitionHeaders, getFileType());
ASResource::buildPreCommandHeaders(preCommandHeaders, getFileType());
ASResource::buildOperators(operators, getFileType());
ASResource::buildAssignmentOperators(assignmentOperators);
ASResource::buildCastOperators(castOperators);
ASResource::buildIndentableMacros(indentableMacros); //ASEnhancer
}
/**
* set the variables for each predefined style.
* this will override any previous settings.
*/
void ASFormatter::fixOptionVariableConflicts()
{
if (formattingStyle == STYLE_ALLMAN)
{
setBraceFormatMode(BREAK_MODE);
}
else if (formattingStyle == STYLE_JAVA)
{
setBraceFormatMode(ATTACH_MODE);
}
else if (formattingStyle == STYLE_KR)
{
setBraceFormatMode(LINUX_MODE);
}
else if (formattingStyle == STYLE_STROUSTRUP)
{
setBraceFormatMode(LINUX_MODE);
setBreakClosingHeaderBracesMode(true);
}
else if (formattingStyle == STYLE_WHITESMITH)
{
setBraceFormatMode(BREAK_MODE);
setBraceIndent(true);
setClassIndent(true); // avoid hanging indent with access modifiers
setSwitchIndent(true); // avoid hanging indent with case statements
}
else if (formattingStyle == STYLE_VTK)
{
// the unindented class brace does NOT cause a hanging indent like Whitesmith
setBraceFormatMode(BREAK_MODE);
setBraceIndentVtk(true); // sets both braceIndent and braceIndentVtk
setSwitchIndent(true); // avoid hanging indent with case statements
}
else if (formattingStyle == STYLE_BANNER)
{
// attached braces can have hanging indents with the closing brace
setBraceFormatMode(ATTACH_MODE);
setBraceIndent(true);
setClassIndent(true); // avoid hanging indent with access modifiers
setSwitchIndent(true); // avoid hanging indent with case statements
}
else if (formattingStyle == STYLE_GNU)
{
setBraceFormatMode(BREAK_MODE);
setBlockIndent(true);
}
else if (formattingStyle == STYLE_LINUX)
{
setBraceFormatMode(LINUX_MODE);
// always for Linux style
setMinConditionalIndentOption(MINCOND_ONEHALF);
}
else if (formattingStyle == STYLE_HORSTMANN)
{
setBraceFormatMode(RUN_IN_MODE);
setSwitchIndent(true);
}
else if (formattingStyle == STYLE_1TBS)
{
setBraceFormatMode(LINUX_MODE);
setAddBracesMode(true);
setRemoveBracesMode(false);
}
else if (formattingStyle == STYLE_GOOGLE)
{
setBraceFormatMode(ATTACH_MODE);
setModifierIndent(true);
setClassIndent(false);
}
else if (formattingStyle == STYLE_MOZILLA)
{
setBraceFormatMode(LINUX_MODE);
}
else if (formattingStyle == STYLE_PICO)
{
setBraceFormatMode(RUN_IN_MODE);
setAttachClosingBraceMode(true);
setSwitchIndent(true);
setBreakOneLineBlocksMode(false);
setBreakOneLineStatementsMode(false);
// add-braces won't work for pico, but it could be fixed if necessary
// both options should be set to true
if (shouldAddBraces)
shouldAddOneLineBraces = true;
}
else if (formattingStyle == STYLE_LISP)
{
setBraceFormatMode(ATTACH_MODE);
setAttachClosingBraceMode(true);
setBreakOneLineStatementsMode(false);
// add-one-line-braces won't work for lisp
// only shouldAddBraces should be set to true
if (shouldAddOneLineBraces)
{
shouldAddBraces = true;
shouldAddOneLineBraces = false;
}
}
setMinConditionalIndentLength();
// if not set by indent=force-tab-x set equal to indentLength
if (getTabLength() == 0)
setDefaultTabLength();
// add-one-line-braces implies keep-one-line-blocks
if (shouldAddOneLineBraces)
setBreakOneLineBlocksMode(false);
// don't allow add-braces and remove-braces
if (shouldAddBraces || shouldAddOneLineBraces)
setRemoveBracesMode(false);
// don't allow indent-classes and indent-modifiers
if (getClassIndent())
setModifierIndent(false);
}
/**
* get the next formatted line.
*
* @return formatted line.
*/
string ASFormatter::nextLine()
{
const string* newHeader = nullptr;
isInVirginLine = isVirgin;
isCharImmediatelyPostComment = false;
isPreviousCharPostComment = false;
isCharImmediatelyPostLineComment = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
isCharImmediatelyPostTemplate = false;
while (!isLineReady)
{
if (shouldReparseCurrentChar)
shouldReparseCurrentChar = false;
else if (!getNextChar())
{
breakLine();
continue;
}
else // stuff to do when reading a new character...
{
// make sure that a virgin '{' at the beginning of the file will be treated as a block...
if (isInVirginLine && currentChar == '{'
&& currentLineBeginsWithBrace
&& previousCommandChar == ' ')
previousCommandChar = '{';
if (isInClassInitializer
&& isBraceType(braceTypeStack->back(), COMMAND_TYPE))
isInClassInitializer = false;
if (isInBraceRunIn)
isInLineBreak = false;
if (!isWhiteSpace(currentChar))
isInBraceRunIn = false;
isPreviousCharPostComment = isCharImmediatelyPostComment;
isCharImmediatelyPostComment = false;
isCharImmediatelyPostTemplate = false;
isCharImmediatelyPostReturn = false;
isCharImmediatelyPostThrow = false;
isCharImmediatelyPostNewDelete = false;
isCharImmediatelyPostOperator = false;
isCharImmediatelyPostPointerOrReference = false;
isCharImmediatelyPostOpenBlock = false;
isCharImmediatelyPostCloseBlock = false;
}
if ((lineIsLineCommentOnly || lineIsCommentOnly)
&& currentLine.find("*INDENT-ON*", charNum) != string::npos
&& isFormattingModeOff)
{
isFormattingModeOff = false;
breakLine();
formattedLine = currentLine;
charNum = (int) currentLine.length() - 1;
continue;
}
if (isFormattingModeOff)
{
breakLine();
formattedLine = currentLine;
charNum = (int) currentLine.length() - 1;
continue;
}
if ((lineIsLineCommentOnly || lineIsCommentOnly)
&& currentLine.find("*INDENT-OFF*", charNum) != string::npos)
{
isFormattingModeOff = true;
if (isInLineBreak) // is true if not the first line
breakLine();
formattedLine = currentLine;
charNum = (int)currentLine.length() - 1;
continue;
}
if (shouldBreakLineAtNextChar)
{
if (isWhiteSpace(currentChar) && !lineIsEmpty)
continue;
isInLineBreak = true;
shouldBreakLineAtNextChar = false;
}
if (isInExecSQL && !passedSemicolon)
{
if (currentChar == ';')
passedSemicolon = true;
appendCurrentChar();
continue;
}
if (isInLineComment)
{
formatLineCommentBody();
continue;
}
else if (isInComment)
{
formatCommentBody();
continue;
}
else if (isInQuote)
{
formatQuoteBody();
continue;
}
// not in quote or comment or line comment
if (isSequenceReached("//"))
{
formatLineCommentOpener();
testForTimeToSplitFormattedLine();
continue;
}
else if (isSequenceReached("/*"))
{
formatCommentOpener();
testForTimeToSplitFormattedLine();
continue;
}
else if (currentChar == '"'
|| (currentChar == '\'' && !isDigitSeparator(currentLine, charNum)))
{
formatQuoteOpener();
testForTimeToSplitFormattedLine();
continue;
}
// treat these preprocessor statements as a line comment
else if (currentChar == '#'
&& currentLine.find_first_not_of(" \t") == (size_t) charNum)
{
string preproc = trim(currentLine.c_str() + charNum + 1);
if (preproc.length() > 0
&& isCharPotentialHeader(preproc, 0)
&& (findKeyword(preproc, 0, "region")
|| findKeyword(preproc, 0, "endregion")
|| findKeyword(preproc, 0, "error")
|| findKeyword(preproc, 0, "warning")
|| findKeyword(preproc, 0, "line")))
{
currentLine = rtrim(currentLine); // trim the end only
// check for run-in
if (formattedLine.length() > 0 && formattedLine[0] == '{')
{
isInLineBreak = true;
isInBraceRunIn = false;
}
if (previousCommandChar == '}')
currentHeader = nullptr;
isInLineComment = true;
appendCurrentChar();
continue;
}
}
if (isInPreprocessor)
{
appendCurrentChar();
continue;
}
if (isInTemplate && shouldCloseTemplates)
{
if (previousNonWSChar == '>' && isWhiteSpace(currentChar) && peekNextChar() == '>')
continue;
}
if (shouldRemoveNextClosingBrace && currentChar == '}')
{
currentLine[charNum] = currentChar = ' ';
shouldRemoveNextClosingBrace = false;
assert(adjustChecksumIn(-'}'));
if (isEmptyLine(currentLine))
continue;
}
// handle white space - needed to simplify the rest.
if (isWhiteSpace(currentChar))
{
appendCurrentChar();
continue;
}
/* not in MIDDLE of quote or comment or SQL or white-space of any type ... */
// check if in preprocessor
// ** isInPreprocessor will be automatically reset at the beginning
// of a new line in getnextChar()
if (currentChar == '#')
{
isInPreprocessor = true;
// check for run-in
if (formattedLine.length() > 0 && formattedLine[0] == '{')
{
isInLineBreak = true;
isInBraceRunIn = false;
}
processPreprocessor();
// if top level it is potentially indentable
if (shouldIndentPreprocBlock
&& (isBraceType(braceTypeStack->back(), NULL_TYPE)
|| isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
&& !foundClassHeader
&& !isInClassInitializer
&& sourceIterator->tellg() > preprocBlockEnd)
{
// indent the #if preprocessor blocks
string preproc = ASBeautifier::extractPreprocessorStatement(currentLine);
if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef
{
if (isImmediatelyPostPreprocessor)
breakLine();
isIndentableProprocessorBlock = isIndentablePreprocessorBlock(currentLine, charNum);
isIndentableProprocessor = isIndentableProprocessorBlock;
}
}
if (isIndentableProprocessorBlock
&& charNum < (int) currentLine.length() - 1
&& isWhiteSpace(currentLine[charNum + 1]))
{
size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextText != string::npos)
currentLine.erase(charNum + 1, nextText - charNum - 1);
}
if (isIndentableProprocessorBlock
&& sourceIterator->tellg() >= preprocBlockEnd)
isIndentableProprocessorBlock = false;
// need to fall thru here to reset the variables
}
/* not in preprocessor ... */
if (isImmediatelyPostComment)
{
caseHeaderFollowsComments = false;
isImmediatelyPostComment = false;
isCharImmediatelyPostComment = true;
}
if (isImmediatelyPostLineComment)
{
caseHeaderFollowsComments = false;
isImmediatelyPostLineComment = false;
isCharImmediatelyPostLineComment = true;
}
if (isImmediatelyPostReturn)
{
isImmediatelyPostReturn = false;
isCharImmediatelyPostReturn = true;
}
if (isImmediatelyPostThrow)
{
isImmediatelyPostThrow = false;
isCharImmediatelyPostThrow = true;
}
if (isImmediatelyPostNewDelete)
{
isImmediatelyPostNewDelete = false;
isCharImmediatelyPostNewDelete = true;
}
if (isImmediatelyPostOperator)
{
isImmediatelyPostOperator = false;
isCharImmediatelyPostOperator = true;
}
if (isImmediatelyPostTemplate)
{
isImmediatelyPostTemplate = false;
isCharImmediatelyPostTemplate = true;
}
if (isImmediatelyPostPointerOrReference)
{
isImmediatelyPostPointerOrReference = false;
isCharImmediatelyPostPointerOrReference = true;
}
// reset isImmediatelyPostHeader information
if (isImmediatelyPostHeader)
{
// should braces be added
if (currentChar != '{'
&& shouldAddBraces
&& (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine)
&& isOkToBreakBlock(braceTypeStack->back()))
{
bool bracesAdded = addBracesToStatement();
if (bracesAdded && !shouldAddOneLineBraces)
{
size_t firstText = currentLine.find_first_not_of(" \t");
assert(firstText != string::npos);
if ((int) firstText == charNum || shouldBreakOneLineHeaders)
breakCurrentOneLineBlock = true;
}
}
// should braces be removed
else if (currentChar == '{' && shouldRemoveBraces)
{
bool bracesRemoved = removeBracesFromStatement();
if (bracesRemoved)
{
shouldRemoveNextClosingBrace = true;
if (isBeforeAnyLineEndComment(charNum))
spacePadNum--;
else if (shouldBreakOneLineBlocks
|| (currentLineBeginsWithBrace
&& currentLine.find_first_not_of(" \t") != string::npos))
shouldBreakLineAtNextChar = true;
continue;
}
}
// break 'else-if' if shouldBreakElseIfs is requested
if (shouldBreakElseIfs
&& currentHeader == &AS_ELSE
&& isOkToBreakBlock(braceTypeStack->back())
&& !isBeforeAnyComment()
&& (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine))
{
string nextText = peekNextText(currentLine.substr(charNum));
if (nextText.length() > 0
&& isCharPotentialHeader(nextText, 0)
&& ASBase::findHeader(nextText, 0, headers) == &AS_IF)
{
isInLineBreak = true;
}
}
// break a header (e.g. if, while, else) from the following statement
if (shouldBreakOneLineHeaders
&& peekNextChar() != ' '
&& (shouldBreakOneLineStatements
|| (!isHeaderInMultiStatementLine
&& !isMultiStatementLine()))
&& isOkToBreakBlock(braceTypeStack->back())
&& !isBeforeAnyComment())
{
if (currentChar == '{')
{
if (!currentLineBeginsWithBrace)
{
if (isOneLineBlockReached(currentLine, charNum) == 3)
isInLineBreak = false;
else
breakCurrentOneLineBlock = true;
}
}
else if (currentHeader == &AS_ELSE)
{
string nextText = peekNextText(currentLine.substr(charNum), true);
if (nextText.length() > 0
&& ((isCharPotentialHeader(nextText, 0)
&& ASBase::findHeader(nextText, 0, headers) != &AS_IF)
|| nextText[0] == '{'))
isInLineBreak = true;
}
else
{
isInLineBreak = true;
}
}
isImmediatelyPostHeader = false;
}
if (passedSemicolon) // need to break the formattedLine
{
passedSemicolon = false;
if (parenStack->back() == 0 && !isCharImmediatelyPostComment && currentChar != ';') // allow ;;
{
// does a one-line block have ending comments?
if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
{
size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACE);
assert(blockEnd != string::npos);
// move ending comments to this formattedLine
if (isBeforeAnyLineEndComment(blockEnd))
{
size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1);
assert(commentStart != string::npos);
assert((currentLine.compare(commentStart, 2, "//") == 0)
|| (currentLine.compare(commentStart, 2, "/*") == 0));
formattedLine.append(getIndentLength() - 1, ' ');
// append comment
int charNumSave = charNum;
charNum = commentStart;
while (charNum < (int) currentLine.length())
{
currentChar = currentLine[charNum];
if (currentChar == '\t' && shouldConvertTabs)
convertTabToSpaces();
formattedLine.append(1, currentChar);
++charNum;
}
size_t commentLength = currentLine.length() - commentStart;
currentLine.erase(commentStart, commentLength);
charNum = charNumSave;
currentChar = currentLine[charNum];
testForTimeToSplitFormattedLine();
}
}
isInExecSQL = false;
shouldReparseCurrentChar = true;
if (formattedLine.find_first_not_of(" \t") != string::npos)
isInLineBreak = true;
if (needHeaderOpeningBrace)
{
isCharImmediatelyPostCloseBlock = true;
needHeaderOpeningBrace = false;
}
continue;
}
}
if (passedColon)
{
passedColon = false;
if (parenStack->back() == 0
&& !isBeforeAnyComment()
&& (formattedLine.find_first_not_of(" \t") != string::npos))
{
shouldReparseCurrentChar = true;
isInLineBreak = true;
continue;
}
}
// Check if in template declaration, e.g. foo<bar> or foo<bar,fig>
if (!isInTemplate && currentChar == '<')
{
checkIfTemplateOpener();
}
// handle parens
if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<'))
{
questionMarkStack->push_back(foundQuestionMark);
foundQuestionMark = false;
parenStack->back()++;
if (currentChar == '[')
{
++squareBracketCount;
if (getAlignMethodColon() && squareBracketCount == 1 && isCStyle())
objCColonAlign = findObjCColonAlignment();
}
}
else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>'))
{
foundPreCommandHeader = false;
parenStack->back()--;
// this can happen in preprocessor directives
if (parenStack->back() < 0)
parenStack->back() = 0;
if (!questionMarkStack->empty())
{
foundQuestionMark = questionMarkStack->back();
questionMarkStack->pop_back();
}
if (isInTemplate && currentChar == '>')
{
templateDepth--;
if (templateDepth == 0)
{
isInTemplate = false;
isImmediatelyPostTemplate = true;
}
}
// check if this parenthesis closes a header, e.g. if (...), while (...)
if (isInHeader && parenStack->back() == 0)
{
isInHeader = false;
isImmediatelyPostHeader = true;
foundQuestionMark = false;
}
if (currentChar == ']')
{
--squareBracketCount;
if (squareBracketCount <= 0)
{
squareBracketCount = 0;
objCColonAlign = 0;
}
}
if (currentChar == ')')
{
foundCastOperator = false;
if (parenStack->back() == 0)
endOfAsmReached = true;
}
}
// handle braces
if (currentChar == '{' || currentChar == '}')
{
// if appendOpeningBrace this was already done for the original brace
if (currentChar == '{' && !appendOpeningBrace)
{
BraceType newBraceType = getBraceType();
breakCurrentOneLineBlock = false;
foundNamespaceHeader = false;
foundClassHeader = false;
foundStructHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundPreCommandMacro = false;
foundTrailingReturnType = false;
isInPotentialCalculation = false;
isInObjCMethodDefinition = false;
isInObjCInterface = false;
isInEnum = false;
isJavaStaticConstructor = false;
isCharImmediatelyPostNonInStmt = false;
needHeaderOpeningBrace = false;
shouldKeepLineUnbroken = false;
objCColonAlign = 0;
isPreviousBraceBlockRelated = !isBraceType(newBraceType, ARRAY_TYPE);
braceTypeStack->emplace_back(newBraceType);
preBraceHeaderStack->emplace_back(currentHeader);
currentHeader = nullptr;
structStack->push_back(isInIndentableStruct);
if (isBraceType(newBraceType, STRUCT_TYPE) && isCStyle())
isInIndentableStruct = isStructAccessModified(currentLine, charNum);
else
isInIndentableStruct = false;
}
// this must be done before the braceTypeStack is popped
BraceType braceType = braceTypeStack->back();
bool isOpeningArrayBrace = (isBraceType(braceType, ARRAY_TYPE)
&& braceTypeStack->size() >= 2
&& !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2], ARRAY_TYPE)
);
if (currentChar == '}')
{
// if a request has been made to append a post block empty line,
// but the block exists immediately before a closing brace,
// then there is no need for the post block empty line.
isAppendPostBlockEmptyLineRequested = false;
if (isInAsm)
endOfAsmReached = true;
isInAsmOneLine = isInQuote = false;
shouldKeepLineUnbroken = false;
squareBracketCount = 0;
if (braceTypeStack->size() > 1)
{
previousBraceType = braceTypeStack->back();
braceTypeStack->pop_back();
isPreviousBraceBlockRelated = !isBraceType(braceType, ARRAY_TYPE);
}
else
{
previousBraceType = NULL_TYPE;
isPreviousBraceBlockRelated = false;
}
if (!preBraceHeaderStack->empty())
{
currentHeader = preBraceHeaderStack->back();
preBraceHeaderStack->pop_back();
}
else
currentHeader = nullptr;
if (!structStack->empty())
{
isInIndentableStruct = structStack->back();
structStack->pop_back();
}
else
isInIndentableStruct = false;
if (isNonInStatementArray
&& (!isBraceType(braceTypeStack->back(), ARRAY_TYPE) // check previous brace
|| peekNextChar() == ';')) // check for "};" added V2.01
isImmediatelyPostNonInStmt = true;
if (!shouldBreakOneLineStatements
&& ASBeautifier::getNextWord(currentLine, charNum) == AS_ELSE)
{
// handle special case of "else" at the end of line
size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ')
shouldBreakLineAtNextChar = true;
}
}
// format braces
appendOpeningBrace = false;
if (isBraceType(braceType, ARRAY_TYPE))
{
formatArrayBraces(braceType, isOpeningArrayBrace);
}
else
{
if (currentChar == '{')
formatOpeningBrace(braceType);
else
formatClosingBrace(braceType);
}
continue;
}
if ((((previousCommandChar == '{' && isPreviousBraceBlockRelated)
|| ((previousCommandChar == '}'
&& !isImmediatelyPostEmptyBlock
&& isPreviousBraceBlockRelated
&& !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments
&& peekNextChar() != ' '
&& !isBraceType(previousBraceType, DEFINITION_TYPE))
&& !isBraceType(braceTypeStack->back(), DEFINITION_TYPE)))
&& isOkToBreakBlock(braceTypeStack->back()))
// check for array
|| (previousCommandChar == '{' // added 9/30/2010
&& isBraceType(braceTypeStack->back(), ARRAY_TYPE)
&& !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
&& isNonInStatementArray)
// check for pico one line braces
|| (formattingStyle == STYLE_PICO
&& (previousCommandChar == '{' && isPreviousBraceBlockRelated)
&& isBraceType(braceTypeStack->back(), COMMAND_TYPE)
&& isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
&& braceFormatMode == RUN_IN_MODE)
)
{
isCharImmediatelyPostOpenBlock = (previousCommandChar == '{');
isCharImmediatelyPostCloseBlock = (previousCommandChar == '}');
if (isCharImmediatelyPostOpenBlock
&& !isCharImmediatelyPostComment
&& !isCharImmediatelyPostLineComment)
{
previousCommandChar = ' ';
if (braceFormatMode == NONE_MODE)
{
if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
&& (isBraceType(braceTypeStack->back(), BREAK_BLOCK_TYPE)
|| shouldBreakOneLineBlocks))
isInLineBreak = true;
else if (currentLineBeginsWithBrace)
formatRunIn();
else
breakLine();
}
else if (braceFormatMode == RUN_IN_MODE
&& currentChar != '#')
formatRunIn();
else
isInLineBreak = true;
}
else if (isCharImmediatelyPostCloseBlock
&& shouldBreakOneLineStatements
&& !isCharImmediatelyPostComment
&& ((isLegalNameChar(currentChar) && currentChar != '.')
|| currentChar == '+'
|| currentChar == '-'
|| currentChar == '*'
|| currentChar == '&'
|| currentChar == '('))
{
previousCommandChar = ' ';
isInLineBreak = true;
}
}
// reset block handling flags
isImmediatelyPostEmptyBlock = false;
// look for headers
bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum);
if (isPotentialHeader && !isInTemplate && squareBracketCount == 0)
{
isNonParenHeader = false;
foundClosingHeader = false;
newHeader = findHeader(headers);
// Qt headers may be variables in C++
if (isCStyle()
&& (newHeader == &AS_FOREVER || newHeader == &AS_FOREACH))
{
if (currentLine.find_first_of("=;", charNum) != string::npos)
newHeader = nullptr;
}
if (isJavaStyle()
&& (newHeader == &AS_SYNCHRONIZED))
{
// want synchronized statements not synchronized methods
if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE))
newHeader = nullptr;
}
else if (newHeader == &AS_USING
&& ASBeautifier::peekNextChar(
currentLine, charNum + (*newHeader).length() - 1) != '(')
newHeader = nullptr;
if (newHeader != nullptr)
{
foundClosingHeader = isClosingHeader(newHeader);
if (!foundClosingHeader)
{
// these are closing headers
if ((newHeader == &AS_WHILE && currentHeader == &AS_DO)
|| (newHeader == &_AS_FINALLY && currentHeader == &_AS_TRY)
|| (newHeader == &_AS_EXCEPT && currentHeader == &_AS_TRY))
foundClosingHeader = true;
// don't append empty block for these related headers
else if (isSharpStyle()
&& previousNonWSChar == '}'
&& ((newHeader == &AS_SET && currentHeader == &AS_GET)
|| (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
&& isOkToBreakBlock(braceTypeStack->back()))
isAppendPostBlockEmptyLineRequested = false;
}
// TODO: this can be removed in a future release
// version 3.0 - break erroneous attached header from previous versions
if (isSharpStyle()
&& ((newHeader == &AS_SET && currentHeader == &AS_GET)
|| (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
&& !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
&& currentLine[currentLine.find_first_not_of(" \t")] == '}')
isInLineBreak = true;
// END TODO
const string* previousHeader = currentHeader;
currentHeader = newHeader;
needHeaderOpeningBrace = true;
// is the previous statement on the same line?
if ((previousNonWSChar == ';' || previousNonWSChar == ':')
&& !isInLineBreak
&& isOkToBreakBlock(braceTypeStack->back()))
{
// if breaking lines, break the line at the header
// except for multiple 'case' statements on a line
if (maxCodeLength != string::npos
&& previousHeader != &AS_CASE)
isInLineBreak = true;
else
isHeaderInMultiStatementLine = true;
}
if (foundClosingHeader && previousNonWSChar == '}')
{
if (isOkToBreakBlock(braceTypeStack->back()))
isLineBreakBeforeClosingHeader();
// get the adjustment for a comment following the closing header
if (isInLineBreak)
nextLineSpacePadNum = getNextLineCommentAdjustment();
else
spacePadNum = getCurrentLineCommentAdjustment();
}
// check if the found header is non-paren header
isNonParenHeader = findHeader(nonParenHeaders) != nullptr;
if (isNonParenHeader
&& (currentHeader == &AS_CATCH
|| currentHeader == &AS_CASE))
{
int startChar = charNum + currentHeader->length() - 1;
if (ASBeautifier::peekNextChar(currentLine, startChar) == '(')
isNonParenHeader = false;
}
// join 'else if' statements
if (currentHeader == &AS_IF
&& previousHeader == &AS_ELSE
&& isInLineBreak
&& !shouldBreakElseIfs
&& !isCharImmediatelyPostLineComment
&& !isImmediatelyPostPreprocessor)
{
// 'else' must be last thing on the line
size_t start = formattedLine.length() >= 6 ? formattedLine.length() - 6 : 0;
if (formattedLine.find(AS_ELSE, start) != string::npos)
{
appendSpacePad();
isInLineBreak = false;
}
}
appendSequence(*currentHeader);
goForward(currentHeader->length() - 1);
// if a paren-header is found add a space after it, if needed
// this checks currentLine, appendSpacePad() checks formattedLine
// in 'case' and C# 'catch' can be either a paren or non-paren header
if (shouldPadHeader
&& !isNonParenHeader
&& charNum < (int) currentLine.length() - 1 && !isWhiteSpace(currentLine[charNum + 1]))
appendSpacePad();
// Signal that a header has been reached
// *** But treat a closing while() (as in do...while)
// as if it were NOT a header since a closing while()
// should never have a block after it!
if (currentHeader != &AS_CASE && currentHeader != &AS_DEFAULT
&& !(foundClosingHeader && currentHeader == &AS_WHILE))
{
isInHeader = true;
// in C# 'catch' and 'delegate' can be a paren or non-paren header
if (isNonParenHeader && !isSharpStyleWithParen(currentHeader))
{
isImmediatelyPostHeader = true;
isInHeader = false;
}
}
if (shouldBreakBlocks
&& isOkToBreakBlock(braceTypeStack->back())
&& !isHeaderInMultiStatementLine)
{
if (previousHeader == nullptr
&& !foundClosingHeader
&& !isCharImmediatelyPostOpenBlock
&& !isImmediatelyPostCommentOnly)
{
isPrependPostBlockEmptyLineRequested = true;
}
if (isClosingHeader(currentHeader)
|| foundClosingHeader)
{
isPrependPostBlockEmptyLineRequested = false;
}
if (shouldBreakClosingHeaderBlocks
&& isCharImmediatelyPostCloseBlock
&& !isImmediatelyPostCommentOnly
&& !(currentHeader == &AS_WHILE // do-while
&& foundClosingHeader))
{
isPrependPostBlockEmptyLineRequested = true;
}
}
if (currentHeader == &AS_CASE
|| currentHeader == &AS_DEFAULT)
isInCase = true;
continue;
}
else if ((newHeader = findHeader(preDefinitionHeaders)) != nullptr
&& parenStack->back() == 0
&& !isInEnum) // not C++11 enum class
{
if (newHeader == &AS_NAMESPACE || newHeader == &AS_MODULE)
foundNamespaceHeader = true;
if (newHeader == &AS_CLASS)
foundClassHeader = true;
if (newHeader == &AS_STRUCT)
foundStructHeader = true;
if (newHeader == &AS_INTERFACE)
foundInterfaceHeader = true;
foundPreDefinitionHeader = true;
appendSequence(*newHeader);
goForward(newHeader->length() - 1);
continue;
}
else if ((newHeader = findHeader(preCommandHeaders)) != nullptr)
{
// a 'const' variable is not a preCommandHeader
if (previousNonWSChar == ')')
foundPreCommandHeader = true;
}
else if ((newHeader = findHeader(castOperators)) != nullptr)
{
foundCastOperator = true;
appendSequence(*newHeader);
goForward(newHeader->length() - 1);
continue;
}
} // (isPotentialHeader && !isInTemplate)
if (isInLineBreak) // OK to break line here
{
breakLine();
if (isInVirginLine) // adjust for the first line
{
lineCommentNoBeautify = lineCommentNoIndent;
lineCommentNoIndent = false;
if (isImmediatelyPostPreprocessor)
{
isInIndentablePreproc = isIndentableProprocessor;
isIndentableProprocessor = false;
}
}
}
if (previousNonWSChar == '}' || currentChar == ';')
{
if (currentChar == ';')
{
squareBracketCount = 0;
if (((shouldBreakOneLineStatements
|| isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
&& isOkToBreakBlock(braceTypeStack->back()))
&& !(attachClosingBraceMode && peekNextChar() == '}'))
{
passedSemicolon = true;
}
else if (!shouldBreakOneLineStatements
&& ASBeautifier::getNextWord(currentLine, charNum) == AS_ELSE)
{
// handle special case of "else" at the end of line
size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ')
passedSemicolon = true;
}
if (shouldBreakBlocks
&& currentHeader != nullptr
&& currentHeader != &AS_CASE
&& currentHeader != &AS_DEFAULT
&& !isHeaderInMultiStatementLine
&& parenStack->back() == 0)
{
isAppendPostBlockEmptyLineRequested = true;
}
}
if (currentChar != ';'
|| (needHeaderOpeningBrace && parenStack->back() == 0))
currentHeader = nullptr;
resetEndOfStatement();
}
if (currentChar == ':'
&& previousChar != ':' // not part of '::'
&& peekNextChar() != ':') // not part of '::'
{
if (isInCase)
{
isInCase = false;
if (shouldBreakOneLineStatements)
passedColon = true;
}
else if (isCStyle() // for C/C++ only
&& isOkToBreakBlock(braceTypeStack->back())
&& shouldBreakOneLineStatements
&& !foundQuestionMark // not in a ?: sequence
&& !foundPreDefinitionHeader // not in a definition block
&& previousCommandChar != ')' // not after closing paren of a method header
&& !foundPreCommandHeader // not after a 'noexcept'
&& squareBracketCount == 0 // not in objC method call
&& !isInObjCMethodDefinition // not objC '-' or '+' method
&& !isInObjCInterface // not objC @interface
&& !isInObjCSelector // not objC @selector
&& !isDigit(peekNextChar()) // not a bit field
&& !isInEnum // not an enum with a base type
&& !isInAsm // not in extended assembler
&& !isInAsmOneLine // not in extended assembler
&& !isInAsmBlock) // not in extended assembler
{
passedColon = true;
}
if (isCStyle()
&& shouldPadMethodColon
&& (squareBracketCount > 0 || isInObjCMethodDefinition || isInObjCSelector)
&& !foundQuestionMark) // not in a ?: sequence
padObjCMethodColon();
if (isInObjCInterface)
{
appendSpacePad();
if ((int) currentLine.length() > charNum + 1
&& !isWhiteSpace(currentLine[charNum + 1]))
currentLine.insert(charNum + 1, " ");
}
if (isClassInitializer())
isInClassInitializer = true;
}
if (currentChar == '?')
foundQuestionMark = true;
if (isPotentialHeader && !isInTemplate)
{
if (findKeyword(currentLine, charNum, AS_NEW)
|| findKeyword(currentLine, charNum, AS_DELETE))
{
isInPotentialCalculation = false;
isImmediatelyPostNewDelete = true;
}
if (findKeyword(currentLine, charNum, AS_RETURN))
{
isInPotentialCalculation = true; // return is the same as an = sign
isImmediatelyPostReturn = true;
}
if (findKeyword(currentLine, charNum, AS_OPERATOR))
isImmediatelyPostOperator = true;
if (findKeyword(currentLine, charNum, AS_ENUM))
{
size_t firstNum = currentLine.find_first_of("(){},/");
if (firstNum == string::npos
|| currentLine[firstNum] == '{'
|| currentLine[firstNum] == '/')
isInEnum = true;
}
if (isCStyle()
&& findKeyword(currentLine, charNum, AS_THROW)
&& previousCommandChar != ')'
&& !foundPreCommandHeader) // 'const' throw()
isImmediatelyPostThrow = true;
if (isCStyle() && findKeyword(currentLine, charNum, AS_EXTERN) && isExternC())
isInExternC = true;
if (isCStyle() && findKeyword(currentLine, charNum, AS_AUTO)
&& (isBraceType(braceTypeStack->back(), NULL_TYPE)
|| isBraceType(braceTypeStack->back(), NAMESPACE_TYPE)
|| isBraceType(braceTypeStack->back(), CLASS_TYPE)))
foundTrailingReturnType = true;
// Objective-C NSException macros are preCommandHeaders
if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_DURING))
foundPreCommandMacro = true;
if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_HANDLER))
foundPreCommandMacro = true;
if (isCStyle() && isExecSQL(currentLine, charNum))
isInExecSQL = true;
if (isCStyle())
{
if (findKeyword(currentLine, charNum, AS_ASM)
|| findKeyword(currentLine, charNum, AS__ASM__))
{
isInAsm = true;
}
else if (findKeyword(currentLine, charNum, AS_MS_ASM) // microsoft specific
|| findKeyword(currentLine, charNum, AS_MS__ASM))
{
int index = 4;
if (peekNextChar() == '_') // check for __asm
index = 5;
char peekedChar = ASBase::peekNextChar(currentLine, charNum + index);
if (peekedChar == '{' || peekedChar == ' ')
isInAsmBlock = true;
else
isInAsmOneLine = true;
}
}
if (isJavaStyle()
&& (findKeyword(currentLine, charNum, AS_STATIC)
&& isNextCharOpeningBrace(charNum + 6)))
isJavaStaticConstructor = true;
if (isSharpStyle()
&& (findKeyword(currentLine, charNum, AS_DELEGATE)
|| findKeyword(currentLine, charNum, AS_UNCHECKED)))
isSharpDelegate = true;
// append the entire name
string name = getCurrentWord(currentLine, charNum);
// must pad the 'and' and 'or' operators if required
if (name == "and" || name == "or")
{
if (shouldPadOperators && previousNonWSChar != ':')
{
appendSpacePad();
appendOperator(name);
goForward(name.length() - 1);
if (!isBeforeAnyComment()
&& !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0)
&& !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0))
appendSpaceAfter();
}
else
{
appendOperator(name);
goForward(name.length() - 1);
}
}
else
{
appendSequence(name);
goForward(name.length() - 1);
}
continue;
} // (isPotentialHeader && !isInTemplate)
// determine if this is an Objective-C statement
if (currentChar == '@'
&& isCharPotentialHeader(currentLine, charNum + 1)
&& findKeyword(currentLine, charNum + 1, AS_INTERFACE)
&& isBraceType(braceTypeStack->back(), NULL_TYPE))
{
isInObjCInterface = true;
string name = '@' + AS_INTERFACE;
appendSequence(name);
goForward(name.length() - 1);
continue;
}
else if (currentChar == '@'
&& isCharPotentialHeader(currentLine, charNum + 1)
&& findKeyword(currentLine, charNum + 1, AS_SELECTOR))
{
isInObjCSelector = true;
string name = '@' + AS_SELECTOR;
appendSequence(name);
goForward(name.length() - 1);
continue;
}
else if ((currentChar == '-' || currentChar == '+')
&& (int) currentLine.find_first_not_of(" \t") == charNum
&& peekNextChar() == '('
&& isBraceType(braceTypeStack->back(), NULL_TYPE)
&& !isInPotentialCalculation)
{
isInObjCMethodDefinition = true;
isImmediatelyPostObjCMethodPrefix = true;
isInObjCInterface = false;
if (getAlignMethodColon())
objCColonAlign = findObjCColonAlignment();
appendCurrentChar();
continue;
}
// determine if this is a potential calculation
bool isPotentialOperator = isCharPotentialOperator(currentChar);
newHeader = nullptr;
if (isPotentialOperator)
{
newHeader = findOperator(operators);
// check for Java ? wildcard
if (newHeader != nullptr
&& newHeader == &AS_GCC_MIN_ASSIGN
&& isJavaStyle()
&& isInTemplate)
newHeader = nullptr;
if (newHeader != nullptr)
{
if (newHeader == &AS_LAMBDA)
foundPreCommandHeader = true;
// correct mistake of two >> closing a template
if (isInTemplate && (newHeader == &AS_GR_GR || newHeader == &AS_GR_GR_GR))
newHeader = &AS_GR;
if (!isInPotentialCalculation)
{
// must determine if newHeader is an assignment operator
// do NOT use findOperator - the length must be exact!!!
if (find(begin(*assignmentOperators), end(*assignmentOperators), newHeader)
!= end(*assignmentOperators))
{
foundPreCommandHeader = false;
char peekedChar = peekNextChar();
isInPotentialCalculation = !(newHeader == &AS_EQUAL && peekedChar == '*')
&& !(newHeader == &AS_EQUAL && peekedChar == '&')
&& !isCharImmediatelyPostOperator;
}
}
}
}
// process pointers and references
// check newHeader to eliminate things like '&&' sequence
if (newHeader != nullptr && !isJavaStyle()
&& (newHeader == &AS_MULT
|| newHeader == &AS_BIT_AND
|| newHeader == &AS_BIT_XOR
|| newHeader == &AS_AND)
&& isPointerOrReference())
{
if (!isDereferenceOrAddressOf() && !isOperatorPaddingDisabled())
formatPointerOrReference();
else
{
appendOperator(*newHeader);
goForward(newHeader->length() - 1);
}
isImmediatelyPostPointerOrReference = true;
continue;
}
if (shouldPadOperators && newHeader != nullptr && !isOperatorPaddingDisabled())
{
padOperators(newHeader);
continue;
}
// remove spaces before commas
if (currentChar == ',')
{
const size_t len = formattedLine.length();
size_t lastText = formattedLine.find_last_not_of(' ');
if (lastText != string::npos && lastText < len - 1)
{
formattedLine.resize(lastText + 1);
int size_diff = len - (lastText + 1);
spacePadNum -= size_diff;
}
}
// pad commas and semi-colons
if (currentChar == ';'
|| (currentChar == ',' && (shouldPadOperators || shouldPadCommas)))
{
char nextChar = ' ';
if (charNum + 1 < (int) currentLine.length())
nextChar = currentLine[charNum + 1];
if (!isWhiteSpace(nextChar)
&& nextChar != '}'
&& nextChar != ')'
&& nextChar != ']'
&& nextChar != '>'
&& nextChar != ';'
&& !isBeforeAnyComment()
/* && !(isBraceType(braceTypeStack->back(), ARRAY_TYPE)) */
)
{
appendCurrentChar();
appendSpaceAfter();
continue;
}
}
// pad parens
if (currentChar == '(' || currentChar == ')')
{
if (currentChar == '(')
{
if (shouldPadHeader
&& (isCharImmediatelyPostReturn
|| isCharImmediatelyPostThrow
|| isCharImmediatelyPostNewDelete))
appendSpacePad();
}
if (shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen)
padParens();
else
appendCurrentChar();
if (isInObjCMethodDefinition)
{
if (currentChar == '(' && isImmediatelyPostObjCMethodPrefix)
{
if (shouldPadMethodPrefix || shouldUnPadMethodPrefix)
padObjCMethodPrefix();
isImmediatelyPostObjCMethodPrefix = false;
isInObjCReturnType = true;
}
else if (currentChar == ')' && isInObjCReturnType)
{
if (shouldPadReturnType || shouldUnPadReturnType)
padObjCReturnType();
isInObjCReturnType = false;
}
else if (shouldPadParamType || shouldUnPadParamType)
padObjCParamType();
}
continue;
}
// bypass the entire operator
if (newHeader != nullptr)
{
appendOperator(*newHeader);
goForward(newHeader->length() - 1);
continue;
}
appendCurrentChar();
} // end of while loop * end of while loop * end of while loop * end of while loop
// return a beautified (i.e. correctly indented) line.
string beautifiedLine;
size_t readyFormattedLineLength = trim(readyFormattedLine).length();
bool isInNamespace = isBraceType(braceTypeStack->back(), NAMESPACE_TYPE);
if (prependEmptyLine // prepend a blank line before this formatted line
&& readyFormattedLineLength > 0
&& previousReadyFormattedLineLength > 0)
{
isLineReady = true; // signal a waiting readyFormattedLine
beautifiedLine = beautify("");
previousReadyFormattedLineLength = 0;
// call the enhancer for new empty lines
enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL);
}
else // format the current formatted line
{
isLineReady = false;
runInIndentContinuation = runInIndentChars;
beautifiedLine = beautify(readyFormattedLine);
previousReadyFormattedLineLength = readyFormattedLineLength;
// the enhancer is not called for no-indent line comments
if (!lineCommentNoBeautify && !isFormattingModeOff)
enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL);
runInIndentChars = 0;
lineCommentNoBeautify = lineCommentNoIndent;
lineCommentNoIndent = false;
isInIndentablePreproc = isIndentableProprocessor;
isIndentableProprocessor = false;
isElseHeaderIndent = elseHeaderFollowsComments;
isCaseHeaderCommentIndent = caseHeaderFollowsComments;
objCColonAlignSubsequent = objCColonAlign;
if (isCharImmediatelyPostNonInStmt)
{
isNonInStatementArray = false;
isCharImmediatelyPostNonInStmt = false;
}
isInPreprocessorBeautify = isInPreprocessor; // used by ASEnhancer
isInBeautifySQL = isInExecSQL; // used by ASEnhancer
}
prependEmptyLine = false;
assert(computeChecksumOut(beautifiedLine));
return beautifiedLine;
}
/**
* check if there are any indented lines ready to be read by nextLine()
*
* @return are there any indented lines ready?
*/
bool ASFormatter::hasMoreLines() const
{
return !endOfCodeReached;
}
/**
* comparison function for BraceType enum
*/
bool ASFormatter::isBraceType(BraceType a, BraceType b) const
{
if (a == NULL_TYPE || b == NULL_TYPE)
return (a == b);
return ((a & b) == b);
}
/**
* set the formatting style.
*
* @param style the formatting style.
*/
void ASFormatter::setFormattingStyle(FormatStyle style)
{
formattingStyle = style;
}
/**
* set the add braces mode.
* options:
* true braces added to headers for single line statements.
* false braces NOT added to headers for single line statements.
*
* @param state the add braces state.
*/
void ASFormatter::setAddBracesMode(bool state)
{
shouldAddBraces = state;
}
/**
* set the add one line braces mode.
* options:
* true one line braces added to headers for single line statements.
* false one line braces NOT added to headers for single line statements.
*
* @param state the add one line braces state.
*/
void ASFormatter::setAddOneLineBracesMode(bool state)
{
shouldAddBraces = state;
shouldAddOneLineBraces = state;
}
/**
* set the remove braces mode.
* options:
* true braces removed from headers for single line statements.
* false braces NOT removed from headers for single line statements.
*
* @param state the remove braces state.
*/
void ASFormatter::setRemoveBracesMode(bool state)
{
shouldRemoveBraces = state;
}
// retained for compatability with release 2.06
// "Brackets" have been changed to "Braces" in 3.0
// it is referenced only by the old "bracket" options
void ASFormatter::setAddBracketsMode(bool state)
{
setAddBracesMode(state);
}
// retained for compatability with release 2.06
// "Brackets" have been changed to "Braces" in 3.0
// it is referenced only by the old "bracket" options
void ASFormatter::setAddOneLineBracketsMode(bool state)
{
setAddOneLineBracesMode(state);
}
// retained for compatability with release 2.06
// "Brackets" have been changed to "Braces" in 3.0
// it is referenced only by the old "bracket" options
void ASFormatter::setRemoveBracketsMode(bool state)
{
setRemoveBracesMode(state);
}
// retained for compatability with release 2.06
// "Brackets" have been changed to "Braces" in 3.0
// it is referenced only by the old "bracket" options
void ASFormatter::setBreakClosingHeaderBracketsMode(bool state)
{
setBreakClosingHeaderBracesMode(state);
}
/**
* set the brace formatting mode.
* options:
*
* @param mode the brace formatting mode.
*/
void ASFormatter::setBraceFormatMode(BraceMode mode)
{
braceFormatMode = mode;
}
/**
* set 'break after' mode for maximum code length
*
* @param state the 'break after' mode.
*/
void ASFormatter::setBreakAfterMode(bool state)
{
shouldBreakLineAfterLogical = state;
}
/**
* set closing header brace breaking mode
* options:
* true braces just before closing headers (e.g. 'else', 'catch')
* will be broken, even if standard braces are attached.
* false closing header braces will be treated as standard braces.
*
* @param state the closing header brace breaking mode.
*/
void ASFormatter::setBreakClosingHeaderBracesMode(bool state)
{
shouldBreakClosingHeaderBraces = state;
}
/**
* set 'else if()' breaking mode
* options:
* true 'else' headers will be broken from their succeeding 'if' headers.
* false 'else' headers will be attached to their succeeding 'if' headers.
*
* @param state the 'else if()' breaking mode.
*/
void ASFormatter::setBreakElseIfsMode(bool state)
{
shouldBreakElseIfs = state;
}
/**
* set comma padding mode.
* options:
* true statement commas and semicolons will be padded with spaces around them.
* false statement commas and semicolons will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setCommaPaddingMode(bool state)
{
shouldPadCommas = state;
}
/**
* set maximum code length
*
* @param max the maximum code length.
*/
void ASFormatter::setMaxCodeLength(int max)
{
maxCodeLength = max;
}
/**
* set operator padding mode.
* options:
* true statement operators will be padded with spaces around them.
* false statement operators will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setOperatorPaddingMode(bool state)
{
shouldPadOperators = state;
}
/**
* set parenthesis outside padding mode.
* options:
* true statement parentheses will be padded with spaces around them.
* false statement parentheses will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensOutsidePaddingMode(bool state)
{
shouldPadParensOutside = state;
}
/**
* set parenthesis inside padding mode.
* options:
* true statement parenthesis will be padded with spaces around them.
* false statement parenthesis will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensInsidePaddingMode(bool state)
{
shouldPadParensInside = state;
}
/**
* set padding mode before one or more open parentheses.
* options:
* true first open parenthesis will be padded with a space before.
* false first open parenthesis will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensFirstPaddingMode(bool state)
{
shouldPadFirstParen = state;
}
/**
* set header padding mode.
* options:
* true headers will be padded with spaces around them.
* false headers will not be padded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensHeaderPaddingMode(bool state)
{
shouldPadHeader = state;
}
/**
* set parenthesis unpadding mode.
* options:
* true statement parenthesis will be unpadded with spaces removed around them.
* false statement parenthesis will not be unpadded.
*
* @param state the padding mode.
*/
void ASFormatter::setParensUnPaddingMode(bool state)
{
shouldUnPadParens = state;
}
/**
* set the state of the preprocessor indentation option.
* If true, #ifdef blocks at level 0 will be indented.
*
* @param state state of option.
*/
void ASFormatter::setPreprocBlockIndent(bool state)
{
shouldIndentPreprocBlock = state;
}
/**
* Set strip comment prefix mode.
* options:
* true strip leading '*' in a comment.
* false leading '*' in a comment will be left unchanged.
*
* @param state the strip comment prefix mode.
*/
void ASFormatter::setStripCommentPrefix(bool state)
{
shouldStripCommentPrefix = state;
}
/**
* set objective-c '-' or '+' class prefix padding mode.
* options:
* true class prefix will be padded a spaces after them.
* false class prefix will be left unchanged.
*
* @param state the padding mode.
*/
void ASFormatter::setMethodPrefixPaddingMode(bool state)
{
shouldPadMethodPrefix = state;
}
/**
* set objective-c '-' or '+' class prefix unpadding mode.
* options:
* true class prefix will be unpadded with spaces after them removed.
* false class prefix will left unchanged.
*
* @param state the unpadding mode.
*/
void ASFormatter::setMethodPrefixUnPaddingMode(bool state)
{
shouldUnPadMethodPrefix = state;
}
// set objective-c '-' or '+' return type padding mode.
void ASFormatter::setReturnTypePaddingMode(bool state)
{
shouldPadReturnType = state;
}
// set objective-c '-' or '+' return type unpadding mode.
void ASFormatter::setReturnTypeUnPaddingMode(bool state)
{
shouldUnPadReturnType = state;
}
// set objective-c method parameter type padding mode.
void ASFormatter::setParamTypePaddingMode(bool state)
{
shouldPadParamType = state;
}
// set objective-c method parameter type unpadding mode.
void ASFormatter::setParamTypeUnPaddingMode(bool state)
{
shouldUnPadParamType = state;
}
/**
* set objective-c method colon padding mode.
*
* @param mode objective-c colon padding mode.
*/
void ASFormatter::setObjCColonPaddingMode(ObjCColonPad mode)
{
shouldPadMethodColon = true;
objCColonPadMode = mode;
}
/**
* set option to attach closing braces
*
* @param state true = attach, false = don't attach.
*/
void ASFormatter::setAttachClosingBraceMode(bool state)
{
attachClosingBraceMode = state;
}
/**
* set option to attach class braces
*
* @param state true = attach, false = use style default.
*/
void ASFormatter::setAttachClass(bool state)
{
shouldAttachClass = state;
}
/**
* set option to attach extern "C" braces
*
* @param state true = attach, false = use style default.
*/
void ASFormatter::setAttachExternC(bool state)
{
shouldAttachExternC = state;
}
/**
* set option to attach namespace braces
*
* @param state true = attach, false = use style default.
*/
void ASFormatter::setAttachNamespace(bool state)
{
shouldAttachNamespace = state;
}
/**
* set option to attach inline braces
*
* @param state true = attach, false = use style default.
*/
void ASFormatter::setAttachInline(bool state)
{
shouldAttachInline = state;
}
void ASFormatter::setAttachClosingWhile(bool state)
{
shouldAttachClosingWhile = state;
}
/**
* set option to break/not break one-line blocks
*
* @param state true = break, false = don't break.
*/
void ASFormatter::setBreakOneLineBlocksMode(bool state)
{
shouldBreakOneLineBlocks = state;
}
/**
* set one line headers breaking mode
*/
void ASFormatter::setBreakOneLineHeadersMode(bool state)
{
shouldBreakOneLineHeaders = state;
}
/**
* set option to break/not break lines consisting of multiple statements.
*
* @param state true = break, false = don't break.
*/
void ASFormatter::setBreakOneLineStatementsMode(bool state)
{
shouldBreakOneLineStatements = state;
}
void ASFormatter::setCloseTemplatesMode(bool state)
{
shouldCloseTemplates = state;
}
/**
* set option to convert tabs to spaces.
*
* @param state true = convert, false = don't convert.
*/
void ASFormatter::setTabSpaceConversionMode(bool state)
{
shouldConvertTabs = state;
}
/**
* set option to indent comments in column 1.
*
* @param state true = indent, false = don't indent.
*/
void ASFormatter::setIndentCol1CommentsMode(bool state)
{
shouldIndentCol1Comments = state;
}
/**
* set option to force all line ends to a particular style.
*
* @param fmt format enum value
*/
void ASFormatter::setLineEndFormat(LineEndFormat fmt)
{
lineEnd = fmt;
}
/**
* set option to break unrelated blocks of code with empty lines.
*
* @param state true = convert, false = don't convert.
*/
void ASFormatter::setBreakBlocksMode(bool state)
{
shouldBreakBlocks = state;
}
/**
* set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines.
*
* @param state true = convert, false = don't convert.
*/
void ASFormatter::setBreakClosingHeaderBlocksMode(bool state)
{
shouldBreakClosingHeaderBlocks = state;
}
/**
* set option to delete empty lines.
*
* @param state true = delete, false = don't delete.
*/
void ASFormatter::setDeleteEmptyLinesMode(bool state)
{
shouldDeleteEmptyLines = state;
}
/**
* set the pointer alignment.
*
* @param alignment the pointer alignment.
*/
void ASFormatter::setPointerAlignment(PointerAlign alignment)
{
pointerAlignment = alignment;
}
void ASFormatter::setReferenceAlignment(ReferenceAlign alignment)
{
referenceAlignment = alignment;
}
/**
* jump over several characters.
*
* @param i the number of characters to jump over.
*/
void ASFormatter::goForward(int i)
{
while (--i >= 0)
getNextChar();
}
/**
* peek at the next unread character.
*
* @return the next unread character.
*/
char ASFormatter::peekNextChar() const
{
char ch = ' ';
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos)
return ch;
ch = currentLine[peekNum];
return ch;
}
/**
* check if current placement is before a comment
*
* @return is before a comment.
*/
bool ASFormatter::isBeforeComment() const
{
bool foundComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos)
return foundComment;
foundComment = (currentLine.compare(peekNum, 2, "/*") == 0);
return foundComment;
}
/**
* check if current placement is before a comment or line-comment
*
* @return is before a comment or line-comment.
*/
bool ASFormatter::isBeforeAnyComment() const
{
bool foundComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos)
return foundComment;
foundComment = (currentLine.compare(peekNum, 2, "/*") == 0
|| currentLine.compare(peekNum, 2, "//") == 0);
return foundComment;
}
/**
* check if current placement is before a comment or line-comment
* if a block comment it must be at the end of the line
*
* @return is before a comment or line-comment.
*/
bool ASFormatter::isBeforeAnyLineEndComment(int startPos) const
{
bool foundLineEndComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
if (peekNum != string::npos)
{
if (currentLine.compare(peekNum, 2, "//") == 0)
foundLineEndComment = true;
else if (currentLine.compare(peekNum, 2, "/*") == 0)
{
// comment must be closed on this line with nothing after it
size_t endNum = currentLine.find("*/", peekNum + 2);
if (endNum != string::npos)
{
size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
if (nextChar == string::npos)
foundLineEndComment = true;
}
}
}
return foundLineEndComment;
}
/**
* check if current placement is before a comment followed by a line-comment
*
* @return is before a multiple line-end comment.
*/
bool ASFormatter::isBeforeMultipleLineEndComments(int startPos) const
{
bool foundMultipleLineEndComment = false;
size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
if (peekNum != string::npos)
{
if (currentLine.compare(peekNum, 2, "/*") == 0)
{
// comment must be closed on this line with nothing after it
size_t endNum = currentLine.find("*/", peekNum + 2);
if (endNum != string::npos)
{
size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
if (nextChar != string::npos
&& currentLine.compare(nextChar, 2, "//") == 0)
foundMultipleLineEndComment = true;
}
}
}
return foundMultipleLineEndComment;
}
/**
* get the next character, increasing the current placement in the process.
* the new character is inserted into the variable currentChar.
*
* @return whether succeeded to receive the new character.
*/
bool ASFormatter::getNextChar()
{
isInLineBreak = false;
previousChar = currentChar;
if (!isWhiteSpace(currentChar))
{
previousNonWSChar = currentChar;
if (!isInComment && !isInLineComment && !isInQuote
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment
&& !isInPreprocessor
&& !isSequenceReached("/*")
&& !isSequenceReached("//"))
previousCommandChar = currentChar;
}
if (charNum + 1 < (int) currentLine.length()
&& (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment))
{
currentChar = currentLine[++charNum];
if (currentChar == '\t' && shouldConvertTabs)
convertTabToSpaces();
return true;
}
// end of line has been reached
return getNextLine();
}
/**
* get the next line of input, increasing the current placement in the process.
*
* @param emptyLineWasDeleted an empty line was deleted.
* @return whether succeeded in reading the next line.
*/
bool ASFormatter::getNextLine(bool emptyLineWasDeleted /*false*/)
{
if (!sourceIterator->hasMoreLines())
{
endOfCodeReached = true;
return false;
}
if (appendOpeningBrace)
currentLine = "{"; // append brace that was removed from the previous line
else
{
currentLine = sourceIterator->nextLine(emptyLineWasDeleted);
assert(computeChecksumIn(currentLine));
}
// reset variables for new line
inLineNumber++;
if (endOfAsmReached)
endOfAsmReached = isInAsmBlock = isInAsm = false;
shouldKeepLineUnbroken = false;
isInCommentStartLine = false;
isInCase = false;
isInAsmOneLine = false;
isHeaderInMultiStatementLine = false;
isInQuoteContinuation = isInVerbatimQuote || haveLineContinuationChar;
haveLineContinuationChar = false;
isImmediatelyPostEmptyLine = lineIsEmpty;
previousChar = ' ';
if (currentLine.length() == 0)
currentLine = string(" "); // a null is inserted if this is not done
// unless reading in the first line of the file, break a new line.
if (!isVirgin)
isInLineBreak = true;
else
isVirgin = false;
if (isImmediatelyPostNonInStmt)
{
isCharImmediatelyPostNonInStmt = true;
isImmediatelyPostNonInStmt = false;
}
// check if is in preprocessor before line trimming
// a blank line after a \ will remove the flag
isImmediatelyPostPreprocessor = isInPreprocessor;
if (!isInComment
&& (previousNonWSChar != '\\'
|| isEmptyLine(currentLine)))
isInPreprocessor = false;
if (passedSemicolon)
isInExecSQL = false;
initNewLine();
currentChar = currentLine[charNum];
if (isInBraceRunIn && previousNonWSChar == '{' && !isInComment)
isInLineBreak = false;
isInBraceRunIn = false;
if (currentChar == '\t' && shouldConvertTabs)
convertTabToSpaces();
// check for an empty line inside a command brace.
// if yes then read the next line (calls getNextLine recursively).
// must be after initNewLine.
if (shouldDeleteEmptyLines
&& lineIsEmpty
&& isBraceType((*braceTypeStack)[braceTypeStack->size() - 1], COMMAND_TYPE))
{
if (!shouldBreakBlocks || previousNonWSChar == '{' || !commentAndHeaderFollows())
{
isInPreprocessor = isImmediatelyPostPreprocessor; // restore
lineIsEmpty = false;
return getNextLine(true);
}
}
return true;
}
/**
* jump over the leading white space in the current line,
* IF the line does not begin a comment or is in a preprocessor definition.
*/
void ASFormatter::initNewLine()
{
size_t len = currentLine.length();
size_t tabSize = getTabLength();
charNum = 0;
// don't trim these
if (isInQuoteContinuation
|| (isInPreprocessor && !getPreprocDefineIndent()))
return;
// SQL continuation lines must be adjusted so the leading spaces
// is equivalent to the opening EXEC SQL
if (isInExecSQL)
{
// replace leading tabs with spaces
// so that continuation indent will be spaces
size_t tabCount_ = 0;
size_t i;
for (i = 0; i < currentLine.length(); i++)
{
if (!isWhiteSpace(currentLine[i])) // stop at first text
break;
if (currentLine[i] == '\t')
{
size_t numSpaces = tabSize - ((tabCount_ + i) % tabSize);
currentLine.replace(i, 1, numSpaces, ' ');
tabCount_++;
i += tabSize - 1;
}
}
// this will correct the format if EXEC SQL is not a hanging indent
trimContinuationLine();
return;
}
// comment continuation lines must be adjusted so the leading spaces
// is equivalent to the opening comment
if (isInComment)
{
if (noTrimCommentContinuation)
leadingSpaces = tabIncrementIn = 0;
trimContinuationLine();
return;
}
// compute leading spaces
isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly;
lineIsCommentOnly = false;
lineIsLineCommentOnly = false;
lineEndsInCommentOnly = false;
doesLineStartComment = false;
currentLineBeginsWithBrace = false;
lineIsEmpty = false;
currentLineFirstBraceNum = string::npos;
tabIncrementIn = 0;
// bypass whitespace at the start of a line
// preprocessor tabs are replaced later in the program
for (charNum = 0; isWhiteSpace(currentLine[charNum]) && charNum + 1 < (int) len; charNum++)
{
if (currentLine[charNum] == '\t' && !isInPreprocessor)
tabIncrementIn += tabSize - 1 - ((tabIncrementIn + charNum) % tabSize);
}
leadingSpaces = charNum + tabIncrementIn;
if (isSequenceReached("/*"))
{
doesLineStartComment = true;
if ((int) currentLine.length() > charNum + 2
&& currentLine.find("*/", charNum + 2) != string::npos)
lineIsCommentOnly = true;
}
else if (isSequenceReached("//"))
{
lineIsLineCommentOnly = true;
}
else if (isSequenceReached("{"))
{
currentLineBeginsWithBrace = true;
currentLineFirstBraceNum = charNum;
size_t firstText = currentLine.find_first_not_of(" \t", charNum + 1);
if (firstText != string::npos)
{
if (currentLine.compare(firstText, 2, "//") == 0)
lineIsLineCommentOnly = true;
else if (currentLine.compare(firstText, 2, "/*") == 0
|| isExecSQL(currentLine, firstText))
{
// get the extra adjustment
size_t j;
for (j = charNum + 1; j < firstText && isWhiteSpace(currentLine[j]); j++)
{
if (currentLine[j] == '\t')
tabIncrementIn += tabSize - 1 - ((tabIncrementIn + j) % tabSize);
}
leadingSpaces = j + tabIncrementIn;
if (currentLine.compare(firstText, 2, "/*") == 0)
doesLineStartComment = true;
}
}
}
else if (isWhiteSpace(currentLine[charNum]) && !(charNum + 1 < (int) currentLine.length()))
{
lineIsEmpty = true;
}
// do not trim indented preprocessor define (except for comment continuation lines)
if (isInPreprocessor)
{
if (!doesLineStartComment)
leadingSpaces = 0;
charNum = 0;
}
}
/**
* Append a character to the current formatted line.
* The formattedLine split points are updated.
*
* @param ch the character to append.
* @param canBreakLine if true, a registered line-break
*/
void ASFormatter::appendChar(char ch, bool canBreakLine)
{
if (canBreakLine && isInLineBreak)
breakLine();
formattedLine.append(1, ch);
isImmediatelyPostCommentOnly = false;
if (maxCodeLength != string::npos)
{
// These compares reduce the frequency of function calls.
if (isOkToSplitFormattedLine())
updateFormattedLineSplitPoints(ch);
if (formattedLine.length() > maxCodeLength)
testForTimeToSplitFormattedLine();
}
}
/**
* Append a string sequence to the current formatted line.
* The formattedLine split points are NOT updated.
* But the formattedLine is checked for time to split.
*
* @param sequence the sequence to append.
* @param canBreakLine if true, a registered line-break
*/
void ASFormatter::appendSequence(const string& sequence, bool canBreakLine)
{
if (canBreakLine && isInLineBreak)
breakLine();
formattedLine.append(sequence);
if (formattedLine.length() > maxCodeLength)
testForTimeToSplitFormattedLine();
}
/**
* Append an operator sequence to the current formatted line.
* The formattedLine split points are updated.
*
* @param sequence the sequence to append.
* @param canBreakLine if true, a registered line-break
*/
void ASFormatter::appendOperator(const string& sequence, bool canBreakLine)
{
if (canBreakLine && isInLineBreak)
breakLine();
formattedLine.append(sequence);
if (maxCodeLength != string::npos)
{
// These compares reduce the frequency of function calls.
if (isOkToSplitFormattedLine())
updateFormattedLineSplitPointsOperator(sequence);
if (formattedLine.length() > maxCodeLength)
testForTimeToSplitFormattedLine();
}
}
/**
* append a space to the current formattedline, UNLESS the
* last character is already a white-space character.
*/
void ASFormatter::appendSpacePad()
{
int len = formattedLine.length();
if (len > 0 && !isWhiteSpace(formattedLine[len - 1]))
{
formattedLine.append(1, ' ');
spacePadNum++;
if (maxCodeLength != string::npos)
{
// These compares reduce the frequency of function calls.
if (isOkToSplitFormattedLine())
updateFormattedLineSplitPoints(' ');
if (formattedLine.length() > maxCodeLength)
testForTimeToSplitFormattedLine();
}
}
}
/**
* append a space to the current formattedline, UNLESS the
* next character is already a white-space character.
*/
void ASFormatter::appendSpaceAfter()
{
int len = currentLine.length();
if (charNum + 1 < len && !isWhiteSpace(currentLine[charNum + 1]))
{
formattedLine.append(1, ' ');
spacePadNum++;
if (maxCodeLength != string::npos)
{
// These compares reduce the frequency of function calls.
if (isOkToSplitFormattedLine())
updateFormattedLineSplitPoints(' ');
if (formattedLine.length() > maxCodeLength)
testForTimeToSplitFormattedLine();
}
}
}
/**
* register a line break for the formatted line.
*/
void ASFormatter::breakLine(bool isSplitLine /*false*/)
{
isLineReady = true;
isInLineBreak = false;
spacePadNum = nextLineSpacePadNum;
nextLineSpacePadNum = 0;
readyFormattedLine = formattedLine;
formattedLine.erase();
// queue an empty line prepend request if one exists
prependEmptyLine = isPrependPostBlockEmptyLineRequested;
if (!isSplitLine)
{
formattedLineCommentNum = string::npos;
clearFormattedLineSplitPoints();
if (isAppendPostBlockEmptyLineRequested)
{
isAppendPostBlockEmptyLineRequested = false;
isPrependPostBlockEmptyLineRequested = true;
}
else
isPrependPostBlockEmptyLineRequested = false;
}
}
/**
* check if the currently reached open-brace (i.e. '{')
* opens a:
* - a definition type block (such as a class or namespace),
* - a command block (such as a method block)
* - a static array
* this method takes for granted that the current character
* is an opening brace.
*
* @return the type of the opened block.
*/
BraceType ASFormatter::getBraceType()
{
assert(currentChar == '{');
BraceType returnVal = NULL_TYPE;
if ((previousNonWSChar == '='
|| isBraceType(braceTypeStack->back(), ARRAY_TYPE))
&& previousCommandChar != ')'
&& !isNonParenHeader)
returnVal = ARRAY_TYPE;
else if (foundPreDefinitionHeader && previousCommandChar != ')')
{
returnVal = DEFINITION_TYPE;
if (foundNamespaceHeader)
returnVal = (BraceType)(returnVal | NAMESPACE_TYPE);
else if (foundClassHeader)
returnVal = (BraceType)(returnVal | CLASS_TYPE);
else if (foundStructHeader)
returnVal = (BraceType)(returnVal | STRUCT_TYPE);
else if (foundInterfaceHeader)
returnVal = (BraceType)(returnVal | INTERFACE_TYPE);
}
else if (isInEnum)
{
returnVal = (BraceType)(ARRAY_TYPE | ENUM_TYPE);
}
else
{
bool isCommandType = (foundPreCommandHeader
|| foundPreCommandMacro
|| (currentHeader != nullptr && isNonParenHeader)
|| (previousCommandChar == ')')
|| (previousCommandChar == ':' && !foundQuestionMark)
|| (previousCommandChar == ';')
|| ((previousCommandChar == '{' || previousCommandChar == '}')
&& isPreviousBraceBlockRelated)
|| (isInClassInitializer
&& (!isLegalNameChar(previousNonWSChar) || foundPreCommandHeader))
|| foundTrailingReturnType
|| isInObjCMethodDefinition
|| isInObjCInterface
|| isJavaStaticConstructor
|| isSharpDelegate);
// C# methods containing 'get', 'set', 'add', and 'remove' do NOT end with parens
if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1))
{
isCommandType = true;
isSharpAccessor = true;
}
if (isInExternC)
returnVal = (isCommandType ? COMMAND_TYPE : EXTERN_TYPE);
else
returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE);
}
int foundOneLineBlock = isOneLineBlockReached(currentLine, charNum);
if (foundOneLineBlock == 2 && returnVal == COMMAND_TYPE)
returnVal = ARRAY_TYPE;
if (foundOneLineBlock > 0)
{
returnVal = (BraceType) (returnVal | SINGLE_LINE_TYPE);
if (breakCurrentOneLineBlock)
returnVal = (BraceType) (returnVal | BREAK_BLOCK_TYPE);
if (foundOneLineBlock == 3)
returnVal = (BraceType)(returnVal | EMPTY_BLOCK_TYPE);
}
if (isBraceType(returnVal, ARRAY_TYPE))
{
if (isNonInStatementArrayBrace())
{
returnVal = (BraceType)(returnVal | ARRAY_NIS_TYPE);
isNonInStatementArray = true;
isImmediatelyPostNonInStmt = false; // in case of "},{"
nonInStatementBrace = formattedLine.length() - 1;
}
if (isUniformInitializerBrace())
returnVal = (BraceType)(returnVal | INIT_TYPE);
}
return returnVal;
}
/**
* check if a colon is a class initializer separator
*
* @return whether it is a class initializer separator
*/
bool ASFormatter::isClassInitializer() const
{
assert(currentChar == ':');
assert(previousChar != ':' && peekNextChar() != ':'); // not part of '::'
// this should be similar to ASBeautifier::parseCurrentLine()
bool foundClassInitializer = false;
if (foundQuestionMark)
{
// do nothing special
}
else if (parenStack->back() > 0)
{
// found a 'for' loop or an objective-C statement
// so do nothing special
}
else if (isInEnum)
{
// found an enum with a base-type
}
else if (isCStyle()
&& !isInCase
&& (previousCommandChar == ')' || foundPreCommandHeader))
{
// found a 'class' c'tor initializer
foundClassInitializer = true;
}
return foundClassInitializer;
}
/**
* check if a line is empty
*
* @return whether line is empty
*/
bool ASFormatter::isEmptyLine(const string& line) const
{
return line.find_first_not_of(" \t") == string::npos;
}
/**
* Check if the following text is "C" as in extern "C".
*
* @return whether the statement is extern "C"
*/
bool ASFormatter::isExternC() const
{
// charNum should be at 'extern'
assert(!isWhiteSpace(currentLine[charNum]));
size_t startQuote = currentLine.find_first_of(" \t\"", charNum);
if (startQuote == string::npos)
return false;
startQuote = currentLine.find_first_not_of(" \t", startQuote);
if (startQuote == string::npos)
return false;
if (currentLine.compare(startQuote, 3, "\"C\"") != 0)
return false;
return true;
}
/**
* Check if the currently reached '*', '&' or '^' character is
* a pointer-or-reference symbol, or another operator.
* A pointer dereference (*) or an "address of" character (&)
* counts as a pointer or reference because it is not an
* arithmetic operator.
*
* @return whether current character is a reference-or-pointer
*/
bool ASFormatter::isPointerOrReference() const
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
if (isJavaStyle())
return false;
if (isCharImmediatelyPostOperator)
return false;
// get the last legal word (may be a number)
string lastWord = getPreviousWord(currentLine, charNum);
if (lastWord.empty())
lastWord = " ";
// check for preceding or following numeric values
string nextText = peekNextText(currentLine.substr(charNum + 1));
if (nextText.length() == 0)
nextText = " ";
char nextChar = nextText[0];
if (isDigit(lastWord[0])
|| isDigit(nextChar)
|| nextChar == '!'
|| nextChar == '~')
return false;
// check for multiply then a dereference (a * *b)
if (currentChar == '*'
&& charNum < (int) currentLine.length() - 1
&& isWhiteSpace(currentLine[charNum + 1])
&& nextChar == '*')
return false;
if ((foundCastOperator && nextChar == '>')
|| isPointerOrReferenceVariable(lastWord))
return true;
if (isInClassInitializer
&& previousNonWSChar != '('
&& previousNonWSChar != '{'
&& previousCommandChar != ','
&& nextChar != ')'
&& nextChar != '}')
return false;
//check for rvalue reference
if (currentChar == '&' && nextChar == '&')
{
if (previousNonWSChar == '>')
return true;
string followingText;
if ((int) currentLine.length() > charNum + 2)
followingText = peekNextText(currentLine.substr(charNum + 2));
if (followingText.length() > 0 && followingText[0] == ')')
return true;
if (currentHeader != nullptr || isInPotentialCalculation)
return false;
if (parenStack->back() > 0 && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
return false;
return true;
}
if (nextChar == '*'
|| previousNonWSChar == '='
|| previousNonWSChar == '('
|| previousNonWSChar == '['
|| isCharImmediatelyPostReturn
|| isInTemplate
|| isCharImmediatelyPostTemplate
|| currentHeader == &AS_CATCH
|| currentHeader == &AS_FOREACH
|| currentHeader == &AS_QFOREACH)
return true;
if (isBraceType(braceTypeStack->back(), ARRAY_TYPE)
&& isLegalNameChar(lastWord[0])
&& isLegalNameChar(nextChar)
&& previousNonWSChar != ')')
{
if (isArrayOperator())
return false;
}
// checks on operators in parens
if (parenStack->back() > 0
&& isLegalNameChar(lastWord[0])
&& isLegalNameChar(nextChar))
{
// if followed by an assignment it is a pointer or reference
// if followed by semicolon it is a pointer or reference in range-based for
const string* followingOperator = getFollowingOperator();
if (followingOperator != nullptr
&& followingOperator != &AS_MULT
&& followingOperator != &AS_BIT_AND)
{
if (followingOperator == &AS_ASSIGN || followingOperator == &AS_COLON)
return true;
return false;
}
if (isBraceType(braceTypeStack->back(), COMMAND_TYPE)
|| squareBracketCount > 0)
return false;
return true;
}
// checks on operators in parens with following '('
if (parenStack->back() > 0
&& nextChar == '('
&& previousNonWSChar != ','
&& previousNonWSChar != '('
&& previousNonWSChar != '!'
&& previousNonWSChar != '&'
&& previousNonWSChar != '*'
&& previousNonWSChar != '|')
return false;
if (nextChar == '-'
|| nextChar == '+')
{
size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextNum != string::npos)
{
if (currentLine.compare(nextNum, 2, "++") != 0
&& currentLine.compare(nextNum, 2, "--") != 0)
return false;
}
}
bool isPR = (!isInPotentialCalculation
|| (!isLegalNameChar(previousNonWSChar)
&& !(previousNonWSChar == ')' && nextChar == '(')
&& !(previousNonWSChar == ')' && currentChar == '*' && !isImmediatelyPostCast())
&& previousNonWSChar != ']')
|| (!isWhiteSpace(nextChar)
&& nextChar != '-'
&& nextChar != '('
&& nextChar != '['
&& !isLegalNameChar(nextChar))
);
return isPR;
}
/**
* Check if the currently reached '*' or '&' character is
* a dereferenced pointer or "address of" symbol.
* NOTE: this MUST be a pointer or reference as determined by
* the function isPointerOrReference().
*
* @return whether current character is a dereference or address of
*/
bool ASFormatter::isDereferenceOrAddressOf() const
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
if (isCharImmediatelyPostTemplate)
return false;
if (previousNonWSChar == '='
|| previousNonWSChar == ','
|| previousNonWSChar == '.'
|| previousNonWSChar == '{'
|| previousNonWSChar == '>'
|| previousNonWSChar == '<'
|| previousNonWSChar == '?'
|| isCharImmediatelyPostLineComment
|| isCharImmediatelyPostComment
|| isCharImmediatelyPostReturn)
return true;
char nextChar = peekNextChar();
if (currentChar == '*' && nextChar == '*')
{
if (previousNonWSChar == '(')
return true;
if ((int) currentLine.length() < charNum + 2)
return true;
return false;
}
if (currentChar == '&' && nextChar == '&')
{
if (previousNonWSChar == '(' || isInTemplate)
return true;
if ((int) currentLine.length() < charNum + 2)
return true;
return false;
}
// check first char on the line
if (charNum == (int) currentLine.find_first_not_of(" \t")
&& (isBraceType(braceTypeStack->back(), COMMAND_TYPE)
|| parenStack->back() != 0))
return true;
string nextText = peekNextText(currentLine.substr(charNum + 1));
if (nextText.length() > 0)
{
if (nextText[0] == ')' || nextText[0] == '>'
|| nextText[0] == ',' || nextText[0] == '=')
return false;
if (nextText[0] == ';')
return true;
}
// check for reference to a pointer *& (cannot have &*)
if ((currentChar == '*' && nextChar == '&')
|| (previousNonWSChar == '*' && currentChar == '&'))
return false;
if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE)
&& parenStack->back() == 0)
return false;
string lastWord = getPreviousWord(currentLine, charNum);
if (lastWord == "else" || lastWord == "delete")
return true;
if (isPointerOrReferenceVariable(lastWord))
return false;
bool isDA = (!(isLegalNameChar(previousNonWSChar) || previousNonWSChar == '>')
|| (nextText.length() > 0 && !isLegalNameChar(nextText[0]) && nextText[0] != '/')
|| (ispunct((unsigned char)previousNonWSChar) && previousNonWSChar != '.')
|| isCharImmediatelyPostReturn);
return isDA;
}
/**
* Check if the currently reached '*' or '&' character is
* centered with one space on each side.
* Only spaces are checked, not tabs.
* If true then a space will be deleted on the output.
*
* @return whether current character is centered.
*/
bool ASFormatter::isPointerOrReferenceCentered() const
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
int prNum = charNum;
int lineLength = (int) currentLine.length();
// check for end of line
if (peekNextChar() == ' ')
return false;
// check space before
if (prNum < 1
|| currentLine[prNum - 1] != ' ')
return false;
// check no space before that
if (prNum < 2
|| currentLine[prNum - 2] == ' ')
return false;
// check for ** or &&
if (prNum + 1 < lineLength
&& (currentLine[prNum + 1] == '*' || currentLine[prNum + 1] == '&'))
prNum++;
// check space after
if (prNum + 1 <= lineLength
&& currentLine[prNum + 1] != ' ')
return false;
// check no space after that
if (prNum + 2 < lineLength
&& currentLine[prNum + 2] == ' ')
return false;
return true;
}
/**
* Check if a word is a pointer or reference variable type.
*
* @return whether word is a pointer or reference variable.
*/
bool ASFormatter::isPointerOrReferenceVariable(const string& word) const
{
return (word == "char"
|| word == "int"
|| word == "void"
|| (word.length() >= 6 // check end of word for _t
&& word.compare(word.length() - 2, 2, "_t") == 0)
|| word == "INT"
|| word == "VOID");
}
/**
* check if the currently reached '+' or '-' character is a unary operator
* this method takes for granted that the current character
* is a '+' or '-'.
*
* @return whether the current '+' or '-' is a unary operator.
*/
bool ASFormatter::isUnaryOperator() const
{
assert(currentChar == '+' || currentChar == '-');
return ((isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar))
&& previousCommandChar != '.'
&& previousCommandChar != '\"'
&& previousCommandChar != '\''
&& previousCommandChar != ')'
&& previousCommandChar != ']');
}
/**
* check if the currently reached comment is in a 'switch' statement
*
* @return whether the current '+' or '-' is in an exponent.
*/
bool ASFormatter::isInSwitchStatement() const
{
assert(isInLineComment || isInComment);
if (!preBraceHeaderStack->empty())
for (size_t i = 1; i < preBraceHeaderStack->size(); i++)
if (preBraceHeaderStack->at(i) == &AS_SWITCH)
return true;
return false;
}
/**
* check if the currently reached '+' or '-' character is
* part of an exponent, i.e. 0.2E-5.
*
* @return whether the current '+' or '-' is in an exponent.
*/
bool ASFormatter::isInExponent() const
{
assert(currentChar == '+' || currentChar == '-');
if (charNum >= 2)
{
char prevPrevFormattedChar = currentLine[charNum - 2];
char prevFormattedChar = currentLine[charNum - 1];
return ((prevFormattedChar == 'e' || prevFormattedChar == 'E')
&& (prevPrevFormattedChar == '.' || isDigit(prevPrevFormattedChar)));
}
return false;
}
/**
* check if an array brace should NOT have an in-statement indent
*
* @return the array is non in-statement
*/
bool ASFormatter::isNonInStatementArrayBrace() const
{
bool returnVal = false;
char nextChar = peekNextChar();
// if this opening brace begins the line there will be no inStatement indent
if (currentLineBeginsWithBrace
&& charNum == (int) currentLineFirstBraceNum
&& nextChar != '}')
returnVal = true;
// if an opening brace ends the line there will be no inStatement indent
if (isWhiteSpace(nextChar)
|| isBeforeAnyLineEndComment(charNum)
|| nextChar == '{')
returnVal = true;
// Java "new Type [] {...}" IS an inStatement indent
if (isJavaStyle() && previousNonWSChar == ']')
returnVal = false;
return returnVal;
}
/**
* check if a one-line block has been reached,
* i.e. if the currently reached '{' character is closed
* with a complimentary '}' elsewhere on the current line,
*.
* @return 0 = one-line block has not been reached.
* 1 = one-line block has been reached.
* 2 = one-line block has been reached and is followed by a comma.
* 3 = one-line block has been reached and is an empty block.
*/
int ASFormatter::isOneLineBlockReached(const string& line, int startChar) const
{
assert(line[startChar] == '{');
bool isInComment_ = false;
bool isInQuote_ = false;
bool hasText = false;
int braceCount = 0;
int lineLength = line.length();
char quoteChar_ = ' ';
char ch = ' ';
char prevCh = ' ';
for (int i = startChar; i < lineLength; ++i)
{
ch = line[i];
if (isInComment_)
{
if (line.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (ch == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (ch == quoteChar_)
isInQuote_ = false;
continue;
}
if (ch == '"'
|| (ch == '\'' && !isDigitSeparator(line, i)))
{
isInQuote_ = true;
quoteChar_ = ch;
continue;
}
if (line.compare(i, 2, "//") == 0)
break;
if (line.compare(i, 2, "/*") == 0)
{
isInComment_ = true;
++i;
continue;
}
if (ch == '{')
{
++braceCount;
continue;
}
if (ch == '}')
{
--braceCount;
if (braceCount == 0)
{
// is this an array?
if (parenStack->back() == 0 && prevCh != '}')
{
size_t peekNum = line.find_first_not_of(" \t", i + 1);
if (peekNum != string::npos && line[peekNum] == ',')
return 2;
}
if (!hasText)
return 3; // is an empty block
return 1;
}
}
if (ch == ';')
continue;
if (!isWhiteSpace(ch))
{
hasText = true;
prevCh = ch;
}
}
return 0;
}
/**
* peek at the next word to determine if it is a C# non-paren header.
* will look ahead in the input file if necessary.
*
* @param startChar position on currentLine to start the search
* @return true if the next word is get or set.
*/
bool ASFormatter::isNextWordSharpNonParenHeader(int startChar) const
{
// look ahead to find the next non-comment text
string nextText = peekNextText(currentLine.substr(startChar));
if (nextText.length() == 0)
return false;
if (nextText[0] == '[')
return true;
if (!isCharPotentialHeader(nextText, 0))
return false;
if (findKeyword(nextText, 0, AS_GET) || findKeyword(nextText, 0, AS_SET)
|| findKeyword(nextText, 0, AS_ADD) || findKeyword(nextText, 0, AS_REMOVE))
return true;
return false;
}
/**
* peek at the next char to determine if it is an opening brace.
* will look ahead in the input file if necessary.
* this determines a java static constructor.
*
* @param startChar position on currentLine to start the search
* @return true if the next word is an opening brace.
*/
bool ASFormatter::isNextCharOpeningBrace(int startChar) const
{
bool retVal = false;
string nextText = peekNextText(currentLine.substr(startChar));
if (nextText.length() > 0
&& nextText.compare(0, 1, "{") == 0)
retVal = true;
return retVal;
}
/**
* Check if operator and, pointer, and reference padding is disabled.
* Disabling is done thru a NOPAD tag in an ending comment.
*
* @return true if the formatting on this line is disabled.
*/
bool ASFormatter::isOperatorPaddingDisabled() const
{
size_t commentStart = currentLine.find("//", charNum);
if (commentStart == string::npos)
{
commentStart = currentLine.find("/*", charNum);
// comment must end on this line
if (commentStart != string::npos)
{
size_t commentEnd = currentLine.find("*/", commentStart + 2);
if (commentEnd == string::npos)
commentStart = string::npos;
}
}
if (commentStart == string::npos)
return false;
size_t noPadStart = currentLine.find("*NOPAD*", commentStart);
if (noPadStart == string::npos)
return false;
return true;
}
/**
* Determine if an opening array-type brace should have a leading space pad.
* This is to identify C++11 uniform initializers.
*/
bool ASFormatter::isUniformInitializerBrace() const
{
if (isCStyle() && !isInEnum && !isImmediatelyPostPreprocessor)
{
if (isInClassInitializer
|| isLegalNameChar(previousNonWSChar))
return true;
}
return false;
}
/**
* Determine if there is a following statement on the current line.
*/
bool ASFormatter::isMultiStatementLine() const
{
assert((isImmediatelyPostHeader || foundClosingHeader));
bool isInComment_ = false;
bool isInQuote_ = false;
int semiCount_ = 0;
int parenCount_ = 0;
int braceCount_ = 0;
for (size_t i = 0; i < currentLine.length(); i++)
{
if (isInComment_)
{
if (currentLine.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
continue;
}
}
if (currentLine.compare(i, 2, "/*") == 0)
{
isInComment_ = true;
continue;
}
if (currentLine.compare(i, 2, "//") == 0)
return false;
if (isInQuote_)
{
if (currentLine[i] == '"' || currentLine[i] == '\'')
isInQuote_ = false;
continue;
}
if (currentLine[i] == '"' || currentLine[i] == '\'')
{
isInQuote_ = true;
continue;
}
if (currentLine[i] == '(')
{
++parenCount_;
continue;
}
if (currentLine[i] == ')')
{
--parenCount_;
continue;
}
if (parenCount_ > 0)
continue;
if (currentLine[i] == '{')
{
++braceCount_;
}
if (currentLine[i] == '}')
{
--braceCount_;
}
if (braceCount_ > 0)
continue;
if (currentLine[i] == ';')
{
++semiCount_;
if (semiCount_ > 1)
return true;
continue;
}
}
return false;
}
/**
* get the next non-whitespace substring on following lines, bypassing all comments.
*
* @param firstLine the first line to check
* @return the next non-whitespace substring.
*/
string ASFormatter::peekNextText(const string& firstLine,
bool endOnEmptyLine /*false*/,
shared_ptr<ASPeekStream> streamArg /*nullptr*/) const
{
bool isFirstLine = true;
string nextLine_ = firstLine;
size_t firstChar = string::npos;
shared_ptr<ASPeekStream> stream = streamArg;
if (stream == nullptr) // Borland may need == 0
stream = make_shared<ASPeekStream>(sourceIterator);
// find the first non-blank text, bypassing all comments.
bool isInComment_ = false;
while (stream->hasMoreLines() || isFirstLine)
{
if (isFirstLine)
isFirstLine = false;
else
nextLine_ = stream->peekNextLine();
firstChar = nextLine_.find_first_not_of(" \t");
if (firstChar == string::npos)
{
if (endOnEmptyLine && !isInComment_)
break;
continue;
}
if (nextLine_.compare(firstChar, 2, "/*") == 0)
{
firstChar += 2;
isInComment_ = true;
}
if (isInComment_)
{
firstChar = nextLine_.find("*/", firstChar);
if (firstChar == string::npos)
continue;
firstChar += 2;
isInComment_ = false;
firstChar = nextLine_.find_first_not_of(" \t", firstChar);
if (firstChar == string::npos)
continue;
}
if (nextLine_.compare(firstChar, 2, "//") == 0)
continue;
// found the next text
break;
}
if (firstChar == string::npos)
nextLine_ = "";
else
nextLine_ = nextLine_.substr(firstChar);
return nextLine_;
}
/**
* adjust comment position because of adding or deleting spaces
* the spaces are added or deleted to formattedLine
* spacePadNum contains the adjustment
*/
void ASFormatter::adjustComments()
{
assert(spacePadNum != 0);
assert(isSequenceReached("//") || isSequenceReached("/*"));
// block comment must be closed on this line with nothing after it
if (isSequenceReached("/*"))
{
size_t endNum = currentLine.find("*/", charNum + 2);
if (endNum == string::npos)
return;
if (currentLine.find_first_not_of(" \t", endNum + 2) != string::npos)
return;
}
size_t len = formattedLine.length();
// don't adjust a tab
if (formattedLine[len - 1] == '\t')
return;
// if spaces were removed, need to add spaces before the comment
if (spacePadNum < 0)
{
int adjust = -spacePadNum; // make the number positive
formattedLine.append(adjust, ' ');
}
// if spaces were added, need to delete extra spaces before the comment
// if cannot be done put the comment one space after the last text
else if (spacePadNum > 0)
{
int adjust = spacePadNum;
size_t lastText = formattedLine.find_last_not_of(' ');
if (lastText != string::npos
&& lastText < len - adjust - 1)
formattedLine.resize(len - adjust);
else if (len > lastText + 2)
formattedLine.resize(lastText + 2);
else if (len < lastText + 2)
formattedLine.append(len - lastText, ' ');
}
}
/**
* append the current brace inside the end of line comments
* currentChar contains the brace, it will be appended to formattedLine
* formattedLineCommentNum is the comment location on formattedLine
*/
void ASFormatter::appendCharInsideComments()
{
if (formattedLineCommentNum == string::npos // does the comment start on the previous line?
|| formattedLineCommentNum == 0)
{
appendCurrentChar(); // don't attach
return;
}
assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0
|| formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0);
// find the previous non space char
size_t end = formattedLineCommentNum;
size_t beg = formattedLine.find_last_not_of(" \t", end - 1);
if (beg == string::npos)
{
appendCurrentChar(); // don't attach
return;
}
beg++;
// insert the brace
if (end - beg < 3) // is there room to insert?
formattedLine.insert(beg, 3 - end + beg, ' ');
if (formattedLine[beg] == '\t') // don't pad with a tab
formattedLine.insert(beg, 1, ' ');
formattedLine[beg + 1] = currentChar;
testForTimeToSplitFormattedLine();
if (isBeforeComment())
breakLine();
else if (isCharImmediatelyPostLineComment)
shouldBreakLineAtNextChar = true;
}
/**
* add or remove space padding to operators
* the operators and necessary padding will be appended to formattedLine
* the calling function should have a continue statement after calling this method
*
* @param newOperator the operator to be padded
*/
void ASFormatter::padOperators(const string* newOperator)
{
assert(shouldPadOperators);
assert(newOperator != nullptr);
char nextNonWSChar = ASBase::peekNextChar(currentLine, charNum);
bool shouldPad = (newOperator != &AS_SCOPE_RESOLUTION
&& newOperator != &AS_PLUS_PLUS
&& newOperator != &AS_MINUS_MINUS
&& newOperator != &AS_NOT
&& newOperator != &AS_BIT_NOT
&& newOperator != &AS_ARROW
&& !(newOperator == &AS_COLON && !foundQuestionMark // objC methods
&& (isInObjCMethodDefinition || isInObjCInterface
|| isInObjCSelector || squareBracketCount != 0))
&& !(newOperator == &AS_MINUS && isInExponent())
&& !(newOperator == &AS_PLUS && isInExponent())
&& !((newOperator == &AS_PLUS || newOperator == &AS_MINUS) // check for unary plus or minus
&& (previousNonWSChar == '('
|| previousNonWSChar == '['
|| previousNonWSChar == '='
|| previousNonWSChar == ','
|| previousNonWSChar == ':'
|| previousNonWSChar == '{'))
//? // commented out in release 2.05.1 - doesn't seem to do anything???
//x && !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND || newOperator == &AS_AND)
//x && isPointerOrReference())
&& !(newOperator == &AS_MULT
&& (previousNonWSChar == '.'
|| previousNonWSChar == '>')) // check for ->
&& !(newOperator == &AS_MULT && peekNextChar() == '>')
&& !((isInTemplate || isImmediatelyPostTemplate)
&& (newOperator == &AS_LS || newOperator == &AS_GR))
&& !(newOperator == &AS_GCC_MIN_ASSIGN
&& ASBase::peekNextChar(currentLine, charNum + 1) == '>')
&& !(newOperator == &AS_GR && previousNonWSChar == '?')
&& !(newOperator == &AS_QUESTION // check for Java wildcard
&& isJavaStyle()
&& (previousNonWSChar == '<'
|| nextNonWSChar == '>'
|| nextNonWSChar == '.'))
&& !(newOperator == &AS_QUESTION // check for C# null conditional operator
&& isSharpStyle()
&& (nextNonWSChar == '.'
|| nextNonWSChar == '['))
&& !isCharImmediatelyPostOperator
&& !isInCase
&& !isInAsm
&& !isInAsmOneLine
&& !isInAsmBlock
);
// pad before operator
if (shouldPad
&& !(newOperator == &AS_COLON
&& (!foundQuestionMark && !isInEnum) && currentHeader != &AS_FOR)
&& !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
&& currentLine.find(':', charNum + 1) == string::npos)
)
appendSpacePad();
appendOperator(*newOperator);
goForward(newOperator->length() - 1);
currentChar = (*newOperator)[newOperator->length() - 1];
// pad after operator
// but do not pad after a '-' that is a unary-minus.
if (shouldPad
&& !isBeforeAnyComment()
&& !(newOperator == &AS_PLUS && isUnaryOperator())
&& !(newOperator == &AS_MINUS && isUnaryOperator())
&& !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0)
&& !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0)
&& !(peekNextChar() == ',')
&& !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
&& peekNextChar() == '[')
)
appendSpaceAfter();
}
/**
* format pointer or reference
* currentChar contains the pointer or reference
* the symbol and necessary padding will be appended to formattedLine
* the calling function should have a continue statement after calling this method
*
* NOTE: Do NOT use appendCurrentChar() in this method. The line should not be
* broken once the calculation starts.
*/
void ASFormatter::formatPointerOrReference()
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
int pa = pointerAlignment;
int ra = referenceAlignment;
int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
// check for ** and &&
int ptrLength = 1;
char peekedChar = peekNextChar();
if ((currentChar == '*' && peekedChar == '*')
|| (currentChar == '&' && peekedChar == '&'))
{
ptrLength = 2;
size_t nextChar = currentLine.find_first_not_of(" \t", charNum + 2);
if (nextChar == string::npos)
peekedChar = ' ';
else
peekedChar = currentLine[nextChar];
}
// check for cast
if (peekedChar == ')' || peekedChar == '>' || peekedChar == ',')
{
formatPointerOrReferenceCast();
return;
}
// check for a padded space and remove it
if (charNum > 0
&& !isWhiteSpace(currentLine[charNum - 1])
&& formattedLine.length() > 0
&& isWhiteSpace(formattedLine[formattedLine.length() - 1]))
{
formattedLine.erase(formattedLine.length() - 1);
spacePadNum--;
}
if (itemAlignment == PTR_ALIGN_TYPE)
{
formatPointerOrReferenceToType();
}
else if (itemAlignment == PTR_ALIGN_MIDDLE)
{
formatPointerOrReferenceToMiddle();
}
else if (itemAlignment == PTR_ALIGN_NAME)
{
formatPointerOrReferenceToName();
}
else // pointerAlignment == PTR_ALIGN_NONE
{
formattedLine.append(ptrLength, currentChar);
if (ptrLength > 1)
goForward(ptrLength - 1);
}
}
/**
* format pointer or reference with align to type
*/
void ASFormatter::formatPointerOrReferenceToType()
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
// do this before bumping charNum
bool isOldPRCentered = isPointerOrReferenceCentered();
size_t prevCh = formattedLine.find_last_not_of(" \t");
if (prevCh == string::npos)
prevCh = 0;
if (formattedLine.length() == 0 || prevCh == formattedLine.length() - 1)
formattedLine.append(1, currentChar);
else
{
// exchange * or & with character following the type
// this may not work every time with a tab character
string charSave = formattedLine.substr(prevCh + 1, 1);
formattedLine[prevCh + 1] = currentChar;
formattedLine.append(charSave);
}
if (isSequenceReached("**") || isSequenceReached("&&"))
{
if (formattedLine.length() == 1)
formattedLine.append(1, currentChar);
else
formattedLine.insert(prevCh + 2, 1, currentChar);
goForward(1);
}
// if no space after then add one
if (charNum < (int) currentLine.length() - 1
&& !isWhiteSpace(currentLine[charNum + 1])
&& currentLine[charNum + 1] != ')')
appendSpacePad();
// if old pointer or reference is centered, remove a space
if (isOldPRCentered
&& isWhiteSpace(formattedLine[formattedLine.length() - 1]))
{
formattedLine.erase(formattedLine.length() - 1, 1);
spacePadNum--;
}
// update the formattedLine split point
if (maxCodeLength != string::npos)
{
size_t index = formattedLine.length() - 1;
if (isWhiteSpace(formattedLine[index]))
{
updateFormattedLineSplitPointsPointerOrReference(index);
testForTimeToSplitFormattedLine();
}
}
}
/**
* format pointer or reference with align in the middle
*/
void ASFormatter::formatPointerOrReferenceToMiddle()
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
// compute current whitespace before
size_t wsBefore = currentLine.find_last_not_of(" \t", charNum - 1);
if (wsBefore == string::npos)
wsBefore = 0;
else
wsBefore = charNum - wsBefore - 1;
string sequenceToInsert(1, currentChar);
if (isSequenceReached("**"))
{
sequenceToInsert = "**";
goForward(1);
}
else if (isSequenceReached("&&"))
{
sequenceToInsert = "&&";
goForward(1);
}
// if reference to a pointer check for conflicting alignment
else if (currentChar == '*' && peekNextChar() == '&'
&& (referenceAlignment == REF_ALIGN_TYPE
|| referenceAlignment == REF_ALIGN_MIDDLE
|| referenceAlignment == REF_SAME_AS_PTR))
{
sequenceToInsert = "*&";
goForward(1);
for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
goForward(1);
}
// if a comment follows don't align, just space pad
if (isBeforeAnyComment())
{
appendSpacePad();
formattedLine.append(sequenceToInsert);
appendSpaceAfter();
return;
}
// do this before goForward()
bool isAfterScopeResolution = previousNonWSChar == ':';
size_t charNumSave = charNum;
// if this is the last thing on the line
if (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos)
{
if (wsBefore == 0 && !isAfterScopeResolution)
formattedLine.append(1, ' ');
formattedLine.append(sequenceToInsert);
return;
}
// goForward() to convert tabs to spaces, if necessary,
// and move following characters to preceding characters
// this may not work every time with tab characters
for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
{
goForward(1);
if (formattedLine.length() > 0)
formattedLine.append(1, currentLine[i]);
else
spacePadNum--;
}
// find space padding after
size_t wsAfter = currentLine.find_first_not_of(" \t", charNumSave + 1);
if (wsAfter == string::npos || isBeforeAnyComment())
wsAfter = 0;
else
wsAfter = wsAfter - charNumSave - 1;
// don't pad before scope resolution operator, but pad after
if (isAfterScopeResolution)
{
size_t lastText = formattedLine.find_last_not_of(" \t");
formattedLine.insert(lastText + 1, sequenceToInsert);
appendSpacePad();
}
else if (formattedLine.length() > 0)
{
// whitespace should be at least 2 chars to center
if (wsBefore + wsAfter < 2)
{
size_t charsToAppend = (2 - (wsBefore + wsAfter));
formattedLine.append(charsToAppend, ' ');
spacePadNum += charsToAppend;
if (wsBefore == 0)
wsBefore++;
if (wsAfter == 0)
wsAfter++;
}
// insert the pointer or reference char
size_t padAfter = (wsBefore + wsAfter) / 2;
size_t index = formattedLine.length() - padAfter;
formattedLine.insert(index, sequenceToInsert);
}
else // formattedLine.length() == 0
{
formattedLine.append(sequenceToInsert);
if (wsAfter == 0)
wsAfter++;
formattedLine.append(wsAfter, ' ');
spacePadNum += wsAfter;
}
// update the formattedLine split point after the pointer
if (maxCodeLength != string::npos && formattedLine.length() > 0)
{
size_t index = formattedLine.find_last_not_of(" \t");
if (index != string::npos && (index < formattedLine.length() - 1))
{
index++;
updateFormattedLineSplitPointsPointerOrReference(index);
testForTimeToSplitFormattedLine();
}
}
}
/**
* format pointer or reference with align to name
*/
void ASFormatter::formatPointerOrReferenceToName()
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
// do this before bumping charNum
bool isOldPRCentered = isPointerOrReferenceCentered();
size_t startNum = formattedLine.find_last_not_of(" \t");
if (startNum == string::npos)
startNum = 0;
string sequenceToInsert(1, currentChar);
if (isSequenceReached("**"))
{
sequenceToInsert = "**";
goForward(1);
}
else if (isSequenceReached("&&"))
{
sequenceToInsert = "&&";
goForward(1);
}
// if reference to a pointer align both to name
else if (currentChar == '*' && peekNextChar() == '&')
{
sequenceToInsert = "*&";
goForward(1);
for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
goForward(1);
}
char peekedChar = peekNextChar();
bool isAfterScopeResolution = previousNonWSChar == ':'; // check for ::
// if this is not the last thing on the line
if (!isBeforeAnyComment()
&& (int) currentLine.find_first_not_of(" \t", charNum + 1) > charNum)
{
// goForward() to convert tabs to spaces, if necessary,
// and move following characters to preceding characters
// this may not work every time with tab characters
for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
{
// if a padded paren follows don't move
if (shouldPadParensOutside && peekedChar == '(' && !isOldPRCentered)
{
// empty parens don't count
size_t start = currentLine.find_first_not_of("( \t", charNum + 1);
if (start != string::npos && currentLine[start] != ')')
break;
}
goForward(1);
if (formattedLine.length() > 0)
formattedLine.append(1, currentLine[i]);
else
spacePadNum--;
}
}
// don't pad before scope resolution operator
if (isAfterScopeResolution)
{
size_t lastText = formattedLine.find_last_not_of(" \t");
if (lastText != string::npos && lastText + 1 < formattedLine.length())
formattedLine.erase(lastText + 1);
}
// if no space before * then add one
else if (formattedLine.length() > 0
&& (formattedLine.length() <= startNum + 1
|| !isWhiteSpace(formattedLine[startNum + 1])))
{
formattedLine.insert(startNum + 1, 1, ' ');
spacePadNum++;
}
appendSequence(sequenceToInsert, false);
// if old pointer or reference is centered, remove a space
if (isOldPRCentered
&& formattedLine.length() > startNum + 1
&& isWhiteSpace(formattedLine[startNum + 1])
&& !isBeforeAnyComment())
{
formattedLine.erase(startNum + 1, 1);
spacePadNum--;
}
// don't convert to *= or &=
if (peekedChar == '=')
{
appendSpaceAfter();
// if more than one space before, delete one
if (formattedLine.length() > startNum
&& isWhiteSpace(formattedLine[startNum + 1])
&& isWhiteSpace(formattedLine[startNum + 2]))
{
formattedLine.erase(startNum + 1, 1);
spacePadNum--;
}
}
// update the formattedLine split point
if (maxCodeLength != string::npos)
{
size_t index = formattedLine.find_last_of(" \t");
if (index != string::npos
&& index < formattedLine.length() - 1
&& (formattedLine[index + 1] == '*'
|| formattedLine[index + 1] == '&'
|| formattedLine[index + 1] == '^'))
{
updateFormattedLineSplitPointsPointerOrReference(index);
testForTimeToSplitFormattedLine();
}
}
}
/**
* format pointer or reference cast
* currentChar contains the pointer or reference
* NOTE: the pointers and references in function definitions
* are processed as a cast (e.g. void foo(void*, void*))
* is processed here.
*/
void ASFormatter::formatPointerOrReferenceCast()
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(!isJavaStyle());
int pa = pointerAlignment;
int ra = referenceAlignment;
int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
string sequenceToInsert(1, currentChar);
if (isSequenceReached("**") || isSequenceReached("&&"))
{
goForward(1);
sequenceToInsert.append(1, currentLine[charNum]);
}
if (itemAlignment == PTR_ALIGN_NONE)
{
appendSequence(sequenceToInsert, false);
return;
}
// remove preceding whitespace
char prevCh = ' ';
size_t prevNum = formattedLine.find_last_not_of(" \t");
if (prevNum != string::npos)
{
prevCh = formattedLine[prevNum];
if (prevNum + 1 < formattedLine.length()
&& isWhiteSpace(formattedLine[prevNum + 1])
&& prevCh != '(')
{
spacePadNum -= (formattedLine.length() - 1 - prevNum);
formattedLine.erase(prevNum + 1);
}
}
bool isAfterScopeResolution = previousNonWSChar == ':';
if ((itemAlignment == PTR_ALIGN_MIDDLE || itemAlignment == PTR_ALIGN_NAME)
&& !isAfterScopeResolution && prevCh != '(')
{
appendSpacePad();
// in this case appendSpacePad may or may not update the split point
if (maxCodeLength != string::npos && formattedLine.length() > 0)
updateFormattedLineSplitPointsPointerOrReference(formattedLine.length() - 1);
appendSequence(sequenceToInsert, false);
}
else
appendSequence(sequenceToInsert, false);
}
/**
* add or remove space padding to parens
* currentChar contains the paren
* the parens and necessary padding will be appended to formattedLine
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::padParens()
{
assert(currentChar == '(' || currentChar == ')');
assert(shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen);
int spacesOutsideToDelete = 0;
int spacesInsideToDelete = 0;
if (currentChar == '(')
{
spacesOutsideToDelete = formattedLine.length() - 1;
spacesInsideToDelete = 0;
// compute spaces outside the opening paren to delete
if (shouldUnPadParens)
{
char lastChar = ' ';
bool prevIsParenHeader = false;
size_t i = formattedLine.find_last_not_of(" \t");
if (i != string::npos)
{
// if last char is a brace the previous whitespace is an indent
if (formattedLine[i] == '{')
spacesOutsideToDelete = 0;
else if (isCharImmediatelyPostPointerOrReference)
spacesOutsideToDelete = 0;
else
{
spacesOutsideToDelete -= i;
lastChar = formattedLine[i];
// if previous word is a header, it will be a paren header
string prevWord = getPreviousWord(formattedLine, formattedLine.length());
const string* prevWordH = nullptr;
if (shouldPadHeader
&& prevWord.length() > 0
&& isCharPotentialHeader(prevWord, 0))
prevWordH = ASBase::findHeader(prevWord, 0, headers);
if (prevWordH != nullptr)
prevIsParenHeader = true;
else if (prevWord == AS_RETURN) // don't unpad
prevIsParenHeader = true;
else if ((prevWord == AS_NEW || prevWord == AS_DELETE)
&& shouldPadHeader) // don't unpad
prevIsParenHeader = true;
else if (isCStyle() && prevWord == AS_THROW && shouldPadHeader) // don't unpad
prevIsParenHeader = true;
else if (prevWord == "and" || prevWord == "or" || prevWord == "in") // don't unpad
prevIsParenHeader = true;
// don't unpad variables
else if (prevWord == "bool"
|| prevWord == "int"
|| prevWord == "void"
|| prevWord == "void*"
|| prevWord == "char"
|| prevWord == "char*"
|| prevWord == "long"
|| prevWord == "double"
|| prevWord == "float"
|| (prevWord.length() >= 4 // check end of word for _t
&& prevWord.compare(prevWord.length() - 2, 2, "_t") == 0)
|| prevWord == "Int32"
|| prevWord == "UInt32"
|| prevWord == "Int64"
|| prevWord == "UInt64"
|| prevWord == "BOOL"
|| prevWord == "DWORD"
|| prevWord == "HWND"
|| prevWord == "INT"
|| prevWord == "LPSTR"
|| prevWord == "VOID"
|| prevWord == "LPVOID"
)
{
prevIsParenHeader = true;
}
}
}
// do not unpad operators, but leave them if already padded
if (shouldPadParensOutside || prevIsParenHeader)
spacesOutsideToDelete--;
else if (lastChar == '|' // check for ||
|| lastChar == '&' // check for &&
|| lastChar == ','
|| (lastChar == '(' && shouldPadParensInside)
|| (lastChar == '>' && !foundCastOperator)
|| lastChar == '<'
|| lastChar == '?'
|| lastChar == ':'
|| lastChar == ';'
|| lastChar == '='
|| lastChar == '+'
|| lastChar == '-'
|| lastChar == '*'
|| lastChar == '/'
|| lastChar == '%'
|| lastChar == '^'
)
spacesOutsideToDelete--;
if (spacesOutsideToDelete > 0)
{
formattedLine.erase(i + 1, spacesOutsideToDelete);
spacePadNum -= spacesOutsideToDelete;
}
}
// pad open paren outside
char peekedCharOutside = peekNextChar();
if (shouldPadFirstParen && previousChar != '(' && peekedCharOutside != ')')
appendSpacePad();
else if (shouldPadParensOutside)
{
if (!(currentChar == '(' && peekedCharOutside == ')'))
appendSpacePad();
}
appendCurrentChar();
// unpad open paren inside
if (shouldUnPadParens)
{
size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
if (j != string::npos)
spacesInsideToDelete = j - charNum - 1;
if (shouldPadParensInside)
spacesInsideToDelete--;
if (spacesInsideToDelete > 0)
{
currentLine.erase(charNum + 1, spacesInsideToDelete);
spacePadNum -= spacesInsideToDelete;
}
// convert tab to space if requested
if (shouldConvertTabs
&& (int) currentLine.length() > charNum + 1
&& currentLine[charNum + 1] == '\t')
currentLine[charNum + 1] = ' ';
}
// pad open paren inside
char peekedCharInside = peekNextChar();
if (shouldPadParensInside)
if (!(currentChar == '(' && peekedCharInside == ')'))
appendSpaceAfter();
}
else if (currentChar == ')')
{
// unpad close paren inside
if (shouldUnPadParens)
{
spacesInsideToDelete = formattedLine.length();
size_t i = formattedLine.find_last_not_of(" \t");
if (i != string::npos)
spacesInsideToDelete = formattedLine.length() - 1 - i;
if (shouldPadParensInside)
spacesInsideToDelete--;
if (spacesInsideToDelete > 0)
{
formattedLine.erase(i + 1, spacesInsideToDelete);
spacePadNum -= spacesInsideToDelete;
}
}
// pad close paren inside
if (shouldPadParensInside)
if (!(previousChar == '(' && currentChar == ')'))
appendSpacePad();
appendCurrentChar();
// unpad close paren outside
// close parens outside are left unchanged
if (shouldUnPadParens)
{
//spacesOutsideToDelete = 0;
//size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
//if (j != string::npos)
// spacesOutsideToDelete = j - charNum - 1;
//if (shouldPadParensOutside)
// spacesOutsideToDelete--;
//if (spacesOutsideToDelete > 0)
//{
// currentLine.erase(charNum + 1, spacesOutsideToDelete);
// spacePadNum -= spacesOutsideToDelete;
//}
}
// pad close paren outside
char peekedCharOutside = peekNextChar();
if (shouldPadParensOutside)
if (peekedCharOutside != ';'
&& peekedCharOutside != ','
&& peekedCharOutside != '.'
&& peekedCharOutside != '+' // check for ++
&& peekedCharOutside != '-' // check for --
&& peekedCharOutside != ']')
appendSpaceAfter();
}
}
/**
* add or remove space padding to objective-c parens
* these options have precedence over the padParens methods
* the padParens method has already been called, this method adjusts
*/
void ASFormatter::padObjCMethodPrefix()
{
assert(currentChar == '(' && isImmediatelyPostObjCMethodPrefix);
assert(shouldPadMethodPrefix || shouldUnPadMethodPrefix);
size_t prefix = formattedLine.find_first_of("+-");
if (prefix == string::npos)
return;
size_t paren = formattedLine.find_first_of('(');
if (paren == string::npos)
return;
int spaces = paren - prefix - 1;
if (shouldPadMethodPrefix)
{
if (spaces == 0)
{
formattedLine.insert(prefix + 1, 1, ' ');
spacePadNum += 1;
}
else if (spaces > 1)
{
formattedLine.erase(prefix + 1, spaces - 1);
spacePadNum -= spaces - 1;
}
}
// this option will be ignored if used with pad-method-prefix
else if (shouldUnPadMethodPrefix)
{
if (spaces > 0)
{
formattedLine.erase(prefix + 1, spaces);
spacePadNum -= spaces;
}
}
}
/**
* add or remove space padding to objective-c parens
* these options have precedence over the padParens methods
* the padParens method has already been called, this method adjusts
*/
void ASFormatter::padObjCReturnType()
{
assert(currentChar == ')' && isInObjCReturnType);
assert(shouldPadReturnType || shouldUnPadReturnType);
size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextText == string::npos)
return;
int spaces = nextText - charNum - 1;
if (shouldPadReturnType)
{
if (spaces == 0)
{
// this will already be padded if pad-paren is used
if (formattedLine[formattedLine.length() - 1] != ' ')
{
formattedLine.append(" ");
spacePadNum += 1;
}
}
else if (spaces > 1)
{
// do not use goForward here
currentLine.erase(charNum + 1, spaces - 1);
spacePadNum -= spaces - 1;
}
}
// this option will be ignored if used with pad-return-type
else if (shouldUnPadReturnType)
{
// this will already be padded if pad-paren is used
if (formattedLine[formattedLine.length() - 1] == ' ')
{
spacePadNum -= formattedLine.length() - 1 - nextText;
int lastText = formattedLine.find_last_not_of(" \t");
formattedLine.resize(lastText + 1);
}
if (spaces > 0)
{
// do not use goForward here
currentLine.erase(charNum + 1, spaces);
spacePadNum -= spaces;
}
}
}
/**
* add or remove space padding to objective-c parens
* these options have precedence over the padParens methods
* the padParens method has already been called, this method adjusts
*/
void ASFormatter::padObjCParamType()
{
assert((currentChar == '(' || currentChar == ')') && isInObjCMethodDefinition);
assert(!isImmediatelyPostObjCMethodPrefix && !isInObjCReturnType);
assert(shouldPadParamType || shouldUnPadParamType);
if (currentChar == '(')
{
// open paren has already been attached to formattedLine by padParen
size_t paramOpen = formattedLine.rfind('(');
assert(paramOpen != string::npos);
size_t prevText = formattedLine.find_last_not_of(" \t", paramOpen - 1);
if (prevText == string::npos)
return;
int spaces = paramOpen - prevText - 1;
if (shouldPadParamType
|| objCColonPadMode == COLON_PAD_ALL
|| objCColonPadMode == COLON_PAD_AFTER)
{
if (spaces == 0)
{
formattedLine.insert(paramOpen, 1, ' ');
spacePadNum += 1;
}
if (spaces > 1)
{
formattedLine.erase(prevText + 1, spaces - 1);
spacePadNum -= spaces - 1;
}
}
// this option will be ignored if used with pad-param-type
else if (shouldUnPadParamType
|| objCColonPadMode == COLON_PAD_NONE
|| objCColonPadMode == COLON_PAD_BEFORE)
{
if (spaces > 0)
{
formattedLine.erase(prevText + 1, spaces);
spacePadNum -= spaces;
}
}
}
else if (currentChar == ')')
{
size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextText == string::npos)
return;
int spaces = nextText - charNum - 1;
if (shouldPadParamType)
{
if (spaces == 0)
{
// this will already be padded if pad-paren is used
if (formattedLine[formattedLine.length() - 1] != ' ')
{
formattedLine.append(" ");
spacePadNum += 1;
}
}
else if (spaces > 1)
{
// do not use goForward here
currentLine.erase(charNum + 1, spaces - 1);
spacePadNum -= spaces - 1;
}
}
// this option will be ignored if used with pad-param-type
else if (shouldUnPadParamType)
{
// this will already be padded if pad-paren is used
if (formattedLine[formattedLine.length() - 1] == ' ')
{
spacePadNum -= 1;
int lastText = formattedLine.find_last_not_of(" \t");
formattedLine.resize(lastText + 1);
}
if (spaces > 0)
{
// do not use goForward here
currentLine.erase(charNum + 1, spaces);
spacePadNum -= spaces;
}
}
}
}
/**
* format opening brace as attached or broken
* currentChar contains the brace
* the braces will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*
* @param braceType the type of brace to be formatted.
*/
void ASFormatter::formatOpeningBrace(BraceType braceType)
{
assert(!isBraceType(braceType, ARRAY_TYPE));
assert(currentChar == '{');
parenStack->emplace_back(0);
bool breakBrace = isCurrentBraceBroken();
if (breakBrace)
{
if (isBeforeAnyComment() && isOkToBreakBlock(braceType))
{
// if comment is at line end leave the comment on this line
if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
{
currentChar = ' '; // remove brace from current line
if (parenStack->size() > 1)
parenStack->pop_back();
currentLine[charNum] = currentChar;
appendOpeningBrace = true; // append brace to following line
}
// else put comment after the brace
else if (!isBeforeMultipleLineEndComments(charNum))
breakLine();
}
else if (!isBraceType(braceType, SINGLE_LINE_TYPE))
{
formattedLine = rtrim(formattedLine);
breakLine();
}
else if ((shouldBreakOneLineBlocks || isBraceType(braceType, BREAK_BLOCK_TYPE))
&& !isBraceType(braceType, EMPTY_BLOCK_TYPE))
breakLine();
else if (!isInLineBreak)
appendSpacePad();
appendCurrentChar();
// should a following comment break from the brace?
// must break the line AFTER the brace
if (isBeforeComment()
&& formattedLine.length() > 0
&& formattedLine[0] == '{'
&& isOkToBreakBlock(braceType)
&& (braceFormatMode == BREAK_MODE
|| braceFormatMode == LINUX_MODE))
{
shouldBreakLineAtNextChar = true;
}
}
else // attach brace
{
// are there comments before the brace?
if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment)
{
if (isOkToBreakBlock(braceType)
&& !(isCharImmediatelyPostComment && isCharImmediatelyPostLineComment) // don't attach if two comments on the line
&& !isImmediatelyPostPreprocessor
// && peekNextChar() != '}' // don't attach { } // removed release 2.03
&& previousCommandChar != '{' // don't attach { {
&& previousCommandChar != '}' // don't attach } {
&& previousCommandChar != ';') // don't attach ; {
{
appendCharInsideComments();
}
else
{
appendCurrentChar(); // don't attach
}
}
else if (previousCommandChar == '{'
|| (previousCommandChar == '}' && !isInClassInitializer)
|| previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';'
{
appendCurrentChar(); // don't attach
}
else
{
// if a blank line precedes this don't attach
if (isEmptyLine(formattedLine))
appendCurrentChar(); // don't attach
else if (isOkToBreakBlock(braceType)
&& !(isImmediatelyPostPreprocessor
&& currentLineBeginsWithBrace))
{
if (!isBraceType(braceType, EMPTY_BLOCK_TYPE))
{
appendSpacePad();
appendCurrentChar(false); // OK to attach
testForTimeToSplitFormattedLine(); // line length will have changed
// should a following comment attach with the brace?
// insert spaces to reposition the comment
if (isBeforeComment()
&& !isBeforeMultipleLineEndComments(charNum)
&& (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace))
{
shouldBreakLineAtNextChar = true;
currentLine.insert(charNum + 1, charNum + 1, ' ');
}
else if (!isBeforeAnyComment()) // added in release 2.03
{
shouldBreakLineAtNextChar = true;
}
}
else
{
if (currentLineBeginsWithBrace && charNum == (int) currentLineFirstBraceNum)
{
appendSpacePad();
appendCurrentChar(false); // attach
shouldBreakLineAtNextChar = true;
}
else
{
appendSpacePad();
appendCurrentChar(); // don't attach
}
}
}
else
{
if (!isInLineBreak)
appendSpacePad();
appendCurrentChar(); // don't attach
}
}
}
}
/**
* format closing brace
* currentChar contains the brace
* the calling function should have a continue statement after calling this method
*
* @param braceType the type of the opening brace for this closing brace.
*/
void ASFormatter::formatClosingBrace(BraceType braceType)
{
assert(!isBraceType(braceType, ARRAY_TYPE));
assert(currentChar == '}');
// parenStack must contain one entry
if (parenStack->size() > 1)
parenStack->pop_back();
// mark state of immediately after empty block
// this state will be used for locating braces that appear immediately AFTER an empty block (e.g. '{} \n}').
if (previousCommandChar == '{')
isImmediatelyPostEmptyBlock = true;
if (attachClosingBraceMode)
{
// for now, namespaces and classes will be attached. Uncomment the lines below to break.
if ((isEmptyLine(formattedLine) // if a blank line precedes this
|| isCharImmediatelyPostLineComment
|| isCharImmediatelyPostComment
|| (isImmediatelyPostPreprocessor && (int) currentLine.find_first_not_of(" \t") == charNum)
// || (isBraceType(braceType, CLASS_TYPE) && isOkToBreakBlock(braceType) && previousNonWSChar != '{')
// || (isBraceType(braceType, NAMESPACE_TYPE) && isOkToBreakBlock(braceType) && previousNonWSChar != '{')
)
&& (!isBraceType(braceType, SINGLE_LINE_TYPE) || isOkToBreakBlock(braceType)))
{
breakLine();
appendCurrentChar(); // don't attach
}
else
{
if (previousNonWSChar != '{'
&& (!isBraceType(braceType, SINGLE_LINE_TYPE)
|| isOkToBreakBlock(braceType)))
appendSpacePad();
appendCurrentChar(false); // attach
}
}
else if (!isBraceType(braceType, EMPTY_BLOCK_TYPE)
&& (isBraceType(braceType, BREAK_BLOCK_TYPE)
|| isOkToBreakBlock(braceType)))
{
breakLine();
appendCurrentChar();
}
else
{
appendCurrentChar();
}
// if a declaration follows a definition, space pad
if (isLegalNameChar(peekNextChar()))
appendSpaceAfter();
if (shouldBreakBlocks
&& currentHeader != nullptr
&& !isHeaderInMultiStatementLine
&& parenStack->back() == 0)
{
if (currentHeader == &AS_CASE || currentHeader == &AS_DEFAULT)
{
// do not yet insert a line if "break" statement is outside the braces
string nextText = peekNextText(currentLine.substr(charNum + 1));
if (nextText.length() > 0
&& nextText.substr(0, 5) != "break")
isAppendPostBlockEmptyLineRequested = true;
}
else
isAppendPostBlockEmptyLineRequested = true;
}
}
/**
* format array braces as attached or broken
* determine if the braces can have an inStatement indent
* currentChar contains the brace
* the braces will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*
* @param braceType the type of brace to be formatted, must be an ARRAY_TYPE.
* @param isOpeningArrayBrace indicates if this is the opening brace for the array block.
*/
void ASFormatter::formatArrayBraces(BraceType braceType, bool isOpeningArrayBrace)
{
assert(isBraceType(braceType, ARRAY_TYPE));
assert(currentChar == '{' || currentChar == '}');
if (currentChar == '{')
{
// is this the first opening brace in the array?
if (isOpeningArrayBrace)
{
if (braceFormatMode == ATTACH_MODE
|| braceFormatMode == LINUX_MODE)
{
// break an enum if mozilla
if (isBraceType(braceType, ENUM_TYPE)
&& formattingStyle == STYLE_MOZILLA)
{
isInLineBreak = true;
appendCurrentChar(); // don't attach
}
// don't attach to a preprocessor directive or '\' line
else if ((isImmediatelyPostPreprocessor
|| (formattedLine.length() > 0
&& formattedLine[formattedLine.length() - 1] == '\\'))
&& currentLineBeginsWithBrace)
{
isInLineBreak = true;
appendCurrentChar(); // don't attach
}
else if (isCharImmediatelyPostComment)
{
// TODO: attach brace to line-end comment
appendCurrentChar(); // don't attach
}
else if (isCharImmediatelyPostLineComment && !isBraceType(braceType, SINGLE_LINE_TYPE))
{
appendCharInsideComments();
}
else
{
// if a blank line precedes this don't attach
if (isEmptyLine(formattedLine))
appendCurrentChar(); // don't attach
else
{
// if brace is broken or not an assignment
if (currentLineBeginsWithBrace
&& !isBraceType(braceType, SINGLE_LINE_TYPE))
{
appendSpacePad();
appendCurrentChar(false); // OK to attach
// TODO: debug the following line
testForTimeToSplitFormattedLine(); // line length will have changed
if (currentLineBeginsWithBrace
&& (int) currentLineFirstBraceNum == charNum)
shouldBreakLineAtNextChar = true;
}
else
{
if (previousNonWSChar != '(')
{
// don't space pad C++11 uniform initialization
if (!isBraceType(braceType, INIT_TYPE))
appendSpacePad();
}
appendCurrentChar();
}
}
}
}
else if (braceFormatMode == BREAK_MODE)
{
if (isWhiteSpace(peekNextChar()) && !isInVirginLine)
breakLine();
else if (isBeforeAnyComment())
{
// do not break unless comment is at line end
if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
{
currentChar = ' '; // remove brace from current line
appendOpeningBrace = true; // append brace to following line
}
}
if (!isInLineBreak && previousNonWSChar != '(')
{
// don't space pad C++11 uniform initialization
if (!isBraceType(braceType, INIT_TYPE))
appendSpacePad();
}
appendCurrentChar();
if (currentLineBeginsWithBrace
&& (int) currentLineFirstBraceNum == charNum
&& !isBraceType(braceType, SINGLE_LINE_TYPE))
shouldBreakLineAtNextChar = true;
}
else if (braceFormatMode == RUN_IN_MODE)
{
if (isWhiteSpace(peekNextChar()) && !isInVirginLine)
breakLine();
else if (isBeforeAnyComment())
{
// do not break unless comment is at line end
if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
{
currentChar = ' '; // remove brace from current line
appendOpeningBrace = true; // append brace to following line
}
}
if (!isInLineBreak && previousNonWSChar != '(')
{
// don't space pad C++11 uniform initialization
if (!isBraceType(braceType, INIT_TYPE))
appendSpacePad();
}
appendCurrentChar();
}
else if (braceFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBrace
&& charNum == (int) currentLineFirstBraceNum)
{
appendCurrentChar(); // don't attach
}
else
{
if (previousNonWSChar != '(')
{
// don't space pad C++11 uniform initialization
if (!isBraceType(braceType, INIT_TYPE))
appendSpacePad();
}
appendCurrentChar(false); // OK to attach
}
}
}
else // not the first opening brace
{
if (braceFormatMode == RUN_IN_MODE)
{
if (previousNonWSChar == '{'
&& braceTypeStack->size() > 2
&& !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2],
SINGLE_LINE_TYPE))
formatArrayRunIn();
}
else if (!isInLineBreak
&& !isWhiteSpace(peekNextChar())
&& previousNonWSChar == '{'
&& braceTypeStack->size() > 2
&& !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2],
SINGLE_LINE_TYPE))
formatArrayRunIn();
appendCurrentChar();
}
}
else if (currentChar == '}')
{
if (attachClosingBraceMode)
{
if (isEmptyLine(formattedLine) // if a blank line precedes this
|| isImmediatelyPostPreprocessor
|| isCharImmediatelyPostLineComment
|| isCharImmediatelyPostComment)
appendCurrentChar(); // don't attach
else
{
appendSpacePad();
appendCurrentChar(false); // attach
}
}
else
{
// does this close the first opening brace in the array?
// must check if the block is still a single line because of anonymous statements
if (!isBraceType(braceType, INIT_TYPE)
&& (!isBraceType(braceType, SINGLE_LINE_TYPE)
|| formattedLine.find('{') == string::npos))
breakLine();
appendCurrentChar();
}
// if a declaration follows an enum definition, space pad
char peekedChar = peekNextChar();
if (isLegalNameChar(peekedChar)
|| peekedChar == '[')
appendSpaceAfter();
}
}
/**
* determine if a run-in can be attached.
* if it can insert the indents in formattedLine and reset the current line break.
*/
void ASFormatter::formatRunIn()
{
assert(braceFormatMode == RUN_IN_MODE || braceFormatMode == NONE_MODE);
// keep one line blocks returns true without indenting the run-in
if (formattingStyle != STYLE_PICO
&& !isOkToBreakBlock(braceTypeStack->back()))
return; // true;
// make sure the line begins with a brace
size_t lastText = formattedLine.find_last_not_of(" \t");
if (lastText == string::npos || formattedLine[lastText] != '{')
return; // false;
// make sure the brace is broken
if (formattedLine.find_first_not_of(" \t{") != string::npos)
return; // false;
if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
return; // false;
bool extraIndent = false;
bool extraHalfIndent = false;
isInLineBreak = true;
// cannot attach a class modifier without indent-classes
if (isCStyle()
&& isCharPotentialHeader(currentLine, charNum)
&& (isBraceType(braceTypeStack->back(), CLASS_TYPE)
|| (isBraceType(braceTypeStack->back(), STRUCT_TYPE)
&& isInIndentableStruct)))
{
if (findKeyword(currentLine, charNum, AS_PUBLIC)
|| findKeyword(currentLine, charNum, AS_PRIVATE)
|| findKeyword(currentLine, charNum, AS_PROTECTED))
{
if (getModifierIndent())
extraHalfIndent = true;
else if (!getClassIndent())
return; // false;
}
else if (getClassIndent())
extraIndent = true;
}
// cannot attach a 'case' statement without indent-switches
if (!getSwitchIndent()
&& isCharPotentialHeader(currentLine, charNum)
&& (findKeyword(currentLine, charNum, AS_CASE)
|| findKeyword(currentLine, charNum, AS_DEFAULT)))
return; // false;
// extra indent for switch statements
if (getSwitchIndent()
&& !preBraceHeaderStack->empty()
&& preBraceHeaderStack->back() == &AS_SWITCH
&& ((isLegalNameChar(currentChar)
&& !findKeyword(currentLine, charNum, AS_CASE))))
extraIndent = true;
isInLineBreak = false;
// remove for extra whitespace
if (formattedLine.length() > lastText + 1
&& formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos)
formattedLine.erase(lastText + 1);
if (extraHalfIndent)
{
int indentLength_ = getIndentLength();
runInIndentChars = indentLength_ / 2;
formattedLine.append(runInIndentChars - 1, ' ');
}
else if (getForceTabIndentation() && getIndentLength() != getTabLength())
{
// insert the space indents
string indent;
int indentLength_ = getIndentLength();
int tabLength_ = getTabLength();
indent.append(indentLength_, ' ');
if (extraIndent)
indent.append(indentLength_, ' ');
// replace spaces indents with tab indents
size_t tabCount = indent.length() / tabLength_; // truncate extra spaces
indent.replace(0U, tabCount * tabLength_, tabCount, '\t');
runInIndentChars = indentLength_;
if (indent[0] == ' ') // allow for brace
indent.erase(0, 1);
formattedLine.append(indent);
}
else if (getIndentString() == "\t")
{
appendChar('\t', false);
runInIndentChars = 2; // one for { and one for tab
if (extraIndent)
{
appendChar('\t', false);
runInIndentChars++;
}
}
else // spaces
{
int indentLength_ = getIndentLength();
formattedLine.append(indentLength_ - 1, ' ');
runInIndentChars = indentLength_;
if (extraIndent)
{
formattedLine.append(indentLength_, ' ');
runInIndentChars += indentLength_;
}
}
isInBraceRunIn = true;
}
/**
* remove whitespace and add indentation for an array run-in.
*/
void ASFormatter::formatArrayRunIn()
{
assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE));
// make sure the brace is broken
if (formattedLine.find_first_not_of(" \t{") != string::npos)
return;
size_t lastText = formattedLine.find_last_not_of(" \t");
if (lastText == string::npos || formattedLine[lastText] != '{')
return;
// check for extra whitespace
if (formattedLine.length() > lastText + 1
&& formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos)
formattedLine.erase(lastText + 1);
if (getIndentString() == "\t")
{
appendChar('\t', false);
runInIndentChars = 2; // one for { and one for tab
}
else
{
int indent = getIndentLength();
formattedLine.append(indent - 1, ' ');
runInIndentChars = indent;
}
isInBraceRunIn = true;
isInLineBreak = false;
}
/**
* delete a braceTypeStack vector object
* BraceTypeStack did not work with the DeleteContainer template
*/
void ASFormatter::deleteContainer(vector<BraceType>*& container)
{
if (container != nullptr)
{
container->clear();
delete (container);
container = nullptr;
}
}
/**
* delete a vector object
* T is the type of vector
* used for all vectors except braceTypeStack
*/
template<typename T>
void ASFormatter::deleteContainer(T& container)
{
if (container != nullptr)
{
container->clear();
delete (container);
container = nullptr;
}
}
/**
* initialize a braceType vector object
* braceType did not work with the DeleteContainer template
*/
void ASFormatter::initContainer(vector<BraceType>*& container, vector<BraceType>* value)
{
if (container != nullptr)
deleteContainer(container);
container = value;
}
/**
* initialize a vector object
* T is the type of vector
* used for all vectors except braceTypeStack
*/
template<typename T>
void ASFormatter::initContainer(T& container, T value)
{
// since the ASFormatter object is never deleted,
// the existing vectors must be deleted before creating new ones
if (container != nullptr)
deleteContainer(container);
container = value;
}
/**
* convert a tab to spaces.
* charNum points to the current character to convert to spaces.
* tabIncrementIn is the increment that must be added for tab indent characters
* to get the correct column for the current tab.
* replaces the tab in currentLine with the required number of spaces.
* replaces the value of currentChar.
*/
void ASFormatter::convertTabToSpaces()
{
assert(currentChar == '\t');
// do NOT replace if in quotes
if (isInQuote || isInQuoteContinuation)
return;
size_t tabSize = getTabLength();
size_t numSpaces = tabSize - ((tabIncrementIn + charNum) % tabSize);
currentLine.replace(charNum, 1, numSpaces, ' ');
currentChar = currentLine[charNum];
}
/**
* is it ok to break this block?
*/
bool ASFormatter::isOkToBreakBlock(BraceType braceType) const
{
// Actually, there should not be an ARRAY_TYPE brace here.
// But this will avoid breaking a one line block when there is.
// Otherwise they will be formatted differently on consecutive runs.
if (isBraceType(braceType, ARRAY_TYPE)
&& isBraceType(braceType, SINGLE_LINE_TYPE))
return false;
if (isBraceType(braceType, COMMAND_TYPE)
&& isBraceType(braceType, EMPTY_BLOCK_TYPE))
return false;
if (!isBraceType(braceType, SINGLE_LINE_TYPE)
|| isBraceType(braceType, BREAK_BLOCK_TYPE)
|| shouldBreakOneLineBlocks)
return true;
return false;
}
/**
* check if a sharp header is a paren or non-paren header
*/
bool ASFormatter::isSharpStyleWithParen(const string* header) const
{
return (isSharpStyle() && peekNextChar() == '('
&& (header == &AS_CATCH
|| header == &AS_DELEGATE));
}
/**
* Check for a following header when a comment is reached.
* firstLine must contain the start of the comment.
* return value is a pointer to the header or nullptr.
*/
const string* ASFormatter::checkForHeaderFollowingComment(const string& firstLine) const
{
assert(isInComment || isInLineComment);
assert(shouldBreakElseIfs || shouldBreakBlocks || isInSwitchStatement());
// look ahead to find the next non-comment text
bool endOnEmptyLine = (currentHeader == nullptr);
if (isInSwitchStatement())
endOnEmptyLine = false;
string nextText = peekNextText(firstLine, endOnEmptyLine);
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
return nullptr;
return ASBase::findHeader(nextText, 0, headers);
}
/**
* process preprocessor statements.
* charNum should be the index of the #.
*
* delete braceTypeStack entries added by #if if a #else is found.
* prevents double entries in the braceTypeStack.
*/
void ASFormatter::processPreprocessor()
{
assert(currentChar == '#');
const size_t preproc = currentLine.find_first_not_of(" \t", charNum + 1);
if (preproc == string::npos)
return;
if (currentLine.compare(preproc, 2, "if") == 0)
{
preprocBraceTypeStackSize = braceTypeStack->size();
}
else if (currentLine.compare(preproc, 4, "else") == 0)
{
// delete stack entries added in #if
// should be replaced by #else
if (preprocBraceTypeStackSize > 0)
{
int addedPreproc = braceTypeStack->size() - preprocBraceTypeStackSize;
for (int i = 0; i < addedPreproc; i++)
braceTypeStack->pop_back();
}
}
}
/**
* determine if the next line starts a comment
* and a header follows the comment or comments.
*/
bool ASFormatter::commentAndHeaderFollows()
{
// called ONLY IF shouldDeleteEmptyLines and shouldBreakBlocks are TRUE.
assert(shouldDeleteEmptyLines && shouldBreakBlocks);
// is the next line a comment
auto stream = make_shared<ASPeekStream>(sourceIterator);
if (!stream->hasMoreLines())
return false;
string nextLine_ = stream->peekNextLine();
size_t firstChar = nextLine_.find_first_not_of(" \t");
if (firstChar == string::npos
|| !(nextLine_.compare(firstChar, 2, "//") == 0
|| nextLine_.compare(firstChar, 2, "/*") == 0))
return false;
// find the next non-comment text, and reset
string nextText = peekNextText(nextLine_, false, stream);
if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
return false;
const string* newHeader = ASBase::findHeader(nextText, 0, headers);
if (newHeader == nullptr)
return false;
// if a closing header, reset break unless break is requested
if (isClosingHeader(newHeader) && !shouldBreakClosingHeaderBlocks)
{
isAppendPostBlockEmptyLineRequested = false;
return false;
}
return true;
}
/**
* determine if a brace should be attached or broken
* uses braces in the braceTypeStack
* the last brace in the braceTypeStack is the one being formatted
* returns true if the brace should be broken
*/
bool ASFormatter::isCurrentBraceBroken() const
{
assert(braceTypeStack->size() > 1);
bool breakBrace = false;
size_t stackEnd = braceTypeStack->size() - 1;
// check brace modifiers
if (shouldAttachExternC
&& isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE))
{
return false;
}
if (shouldAttachNamespace
&& isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE))
{
return false;
}
if (shouldAttachClass
&& (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE)
|| isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE)))
{
return false;
}
if (shouldAttachInline
&& isCStyle() // for C++ only
&& braceFormatMode != RUN_IN_MODE
&& !(currentLineBeginsWithBrace && peekNextChar() == '/')
&& isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE))
{
size_t i;
for (i = 1; i < braceTypeStack->size(); i++)
if (isBraceType((*braceTypeStack)[i], CLASS_TYPE)
|| isBraceType((*braceTypeStack)[i], STRUCT_TYPE))
return false;
}
// check braces
if (isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE))
{
if (currentLineBeginsWithBrace
|| braceFormatMode == RUN_IN_MODE)
breakBrace = true;
}
else if (braceFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBrace
&& (int) currentLineFirstBraceNum == charNum)
breakBrace = true;
}
else if (braceFormatMode == BREAK_MODE || braceFormatMode == RUN_IN_MODE)
{
breakBrace = true;
}
else if (braceFormatMode == LINUX_MODE)
{
// break a namespace if NOT stroustrup or mozilla
if (isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE))
{
if (formattingStyle != STYLE_STROUSTRUP
&& formattingStyle != STYLE_MOZILLA)
breakBrace = true;
}
// break a class or interface if NOT stroustrup
else if (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE)
|| isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE))
{
if (formattingStyle != STYLE_STROUSTRUP)
breakBrace = true;
}
// break a struct if mozilla - an enum is processed as an array brace
else if (isBraceType((*braceTypeStack)[stackEnd], STRUCT_TYPE))
{
if (formattingStyle == STYLE_MOZILLA)
breakBrace = true;
}
// break the first brace if a function
else if (isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE))
{
if (stackEnd == 1)
{
breakBrace = true;
}
else if (stackEnd > 1)
{
// break the first brace after these if a function
if (isBraceType((*braceTypeStack)[stackEnd - 1], NAMESPACE_TYPE)
|| isBraceType((*braceTypeStack)[stackEnd - 1], CLASS_TYPE)
|| isBraceType((*braceTypeStack)[stackEnd - 1], ARRAY_TYPE)
|| isBraceType((*braceTypeStack)[stackEnd - 1], STRUCT_TYPE)
|| isBraceType((*braceTypeStack)[stackEnd - 1], EXTERN_TYPE))
{
breakBrace = true;
}
}
}
}
return breakBrace;
}
/**
* format comment body
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatCommentBody()
{
assert(isInComment);
// append the comment
while (charNum < (int) currentLine.length())
{
currentChar = currentLine[charNum];
if (isSequenceReached("*/"))
{
formatCommentCloser();
break;
}
if (currentChar == '\t' && shouldConvertTabs)
convertTabToSpaces();
appendCurrentChar();
++charNum;
}
if (shouldStripCommentPrefix)
stripCommentPrefix();
}
/**
* format a comment opener
* the comment opener will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatCommentOpener()
{
assert(isSequenceReached("/*"));
isInComment = isInCommentStartLine = true;
isImmediatelyPostLineComment = false;
if (previousNonWSChar == '}')
resetEndOfStatement();
// Check for a following header.
// For speed do not check multiple comment lines more than once.
// For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'.
const string* followingHeader = nullptr;
if ((doesLineStartComment
&& !isImmediatelyPostCommentOnly
&& isBraceType(braceTypeStack->back(), COMMAND_TYPE))
&& (shouldBreakElseIfs
|| isInSwitchStatement()
|| (shouldBreakBlocks
&& !isImmediatelyPostEmptyLine
&& previousCommandChar != '{')))
followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum));
if (spacePadNum != 0 && !isInLineBreak)
adjustComments();
formattedLineCommentNum = formattedLine.length();
// must be done BEFORE appendSequence
if (previousCommandChar == '{'
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment)
{
if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
{
// namespace run-in is always broken.
isInLineBreak = true;
}
else if (braceFormatMode == NONE_MODE)
{
// should a run-in statement be attached?
if (currentLineBeginsWithBrace)
formatRunIn();
}
else if (braceFormatMode == ATTACH_MODE)
{
// if the brace was not attached?
if (formattedLine.length() > 0 && formattedLine[0] == '{'
&& !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
isInLineBreak = true;
}
else if (braceFormatMode == RUN_IN_MODE)
{
// should a run-in statement be attached?
if (formattedLine.length() > 0 && formattedLine[0] == '{')
formatRunIn();
}
}
else if (!doesLineStartComment)
noTrimCommentContinuation = true;
// ASBeautifier needs to know the following statements
if (shouldBreakElseIfs && followingHeader == &AS_ELSE)
elseHeaderFollowsComments = true;
if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT)
caseHeaderFollowsComments = true;
// appendSequence will write the previous line
appendSequence(AS_OPEN_COMMENT);
goForward(1);
// must be done AFTER appendSequence
// Break before the comment if a header follows the line comment.
// But not break if previous line is empty, a comment, or a '{'.
if (shouldBreakBlocks
&& followingHeader != nullptr
&& !isImmediatelyPostEmptyLine
&& previousCommandChar != '{')
{
if (isClosingHeader(followingHeader))
{
if (!shouldBreakClosingHeaderBlocks)
isPrependPostBlockEmptyLineRequested = false;
}
// if an opening header, break before the comment
else
isPrependPostBlockEmptyLineRequested = true;
}
if (previousCommandChar == '}')
currentHeader = nullptr;
}
/**
* format a comment closer
* the comment closer will be appended to the current formattedLine
*/
void ASFormatter::formatCommentCloser()
{
assert(isSequenceReached("*/"));
isInComment = false;
noTrimCommentContinuation = false;
isImmediatelyPostComment = true;
appendSequence(AS_CLOSE_COMMENT);
goForward(1);
if (doesLineStartComment
&& (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos))
lineEndsInCommentOnly = true;
if (peekNextChar() == '}'
&& previousCommandChar != ';'
&& !isBraceType(braceTypeStack->back(), ARRAY_TYPE)
&& !isInPreprocessor
&& isOkToBreakBlock(braceTypeStack->back()))
{
isInLineBreak = true;
shouldBreakLineAtNextChar = true;
}
}
/**
* format a line comment body
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatLineCommentBody()
{
assert(isInLineComment);
// append the comment
while (charNum < (int) currentLine.length())
// && !isLineReady // commented out in release 2.04, unnecessary
{
currentChar = currentLine[charNum];
if (currentChar == '\t' && shouldConvertTabs)
convertTabToSpaces();
appendCurrentChar();
++charNum;
}
// explicitly break a line when a line comment's end is found.
if (charNum == (int) currentLine.length())
{
isInLineBreak = true;
isInLineComment = false;
isImmediatelyPostLineComment = true;
currentChar = 0; //make sure it is a neutral char.
}
}
/**
* format a line comment opener
* the line comment opener will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatLineCommentOpener()
{
assert(isSequenceReached("//"));
if ((int) currentLine.length() > charNum + 2
&& currentLine[charNum + 2] == '\xf2') // check for windows line marker
isAppendPostBlockEmptyLineRequested = false;
isInLineComment = true;
isCharImmediatelyPostComment = false;
if (previousNonWSChar == '}')
resetEndOfStatement();
// Check for a following header.
// For speed do not check multiple comment lines more than once.
// For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'.
const string* followingHeader = nullptr;
if ((lineIsLineCommentOnly
&& !isImmediatelyPostCommentOnly
&& isBraceType(braceTypeStack->back(), COMMAND_TYPE))
&& (shouldBreakElseIfs
|| isInSwitchStatement()
|| (shouldBreakBlocks
&& !isImmediatelyPostEmptyLine
&& previousCommandChar != '{')))
followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum));
// do not indent if in column 1 or 2
// or in a namespace before the opening brace
if ((!shouldIndentCol1Comments && !lineCommentNoIndent)
|| foundNamespaceHeader)
{
if (charNum == 0)
lineCommentNoIndent = true;
else if (charNum == 1 && currentLine[0] == ' ')
lineCommentNoIndent = true;
}
// move comment if spaces were added or deleted
if (!lineCommentNoIndent && spacePadNum != 0 && !isInLineBreak)
adjustComments();
formattedLineCommentNum = formattedLine.length();
// must be done BEFORE appendSequence
// check for run-in statement
if (previousCommandChar == '{'
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment)
{
if (braceFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBrace)
formatRunIn();
}
else if (braceFormatMode == RUN_IN_MODE)
{
if (!lineCommentNoIndent)
formatRunIn();
else
isInLineBreak = true;
}
else if (braceFormatMode == BREAK_MODE)
{
if (formattedLine.length() > 0 && formattedLine[0] == '{')
isInLineBreak = true;
}
else
{
if (currentLineBeginsWithBrace)
isInLineBreak = true;
}
}
// ASBeautifier needs to know the following statements
if (shouldBreakElseIfs && followingHeader == &AS_ELSE)
elseHeaderFollowsComments = true;
if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT)
caseHeaderFollowsComments = true;
// appendSequence will write the previous line
appendSequence(AS_OPEN_LINE_COMMENT);
goForward(1);
// must be done AFTER appendSequence
// Break before the comment if a header follows the line comment.
// But do not break if previous line is empty, a comment, or a '{'.
if (shouldBreakBlocks
&& followingHeader != nullptr
&& !isImmediatelyPostEmptyLine
&& previousCommandChar != '{')
{
if (isClosingHeader(followingHeader))
{
if (!shouldBreakClosingHeaderBlocks)
isPrependPostBlockEmptyLineRequested = false;
}
// if an opening header, break before the comment
else
isPrependPostBlockEmptyLineRequested = true;
}
if (previousCommandChar == '}')
currentHeader = nullptr;
// if tabbed input don't convert the immediately following tabs to spaces
if (getIndentString() == "\t" && lineCommentNoIndent)
{
while (charNum + 1 < (int) currentLine.length()
&& currentLine[charNum + 1] == '\t')
{
currentChar = currentLine[++charNum];
appendCurrentChar();
}
}
// explicitly break a line when a line comment's end is found.
if (charNum + 1 == (int) currentLine.length())
{
isInLineBreak = true;
isInLineComment = false;
isImmediatelyPostLineComment = true;
currentChar = 0; //make sure it is a neutral char.
}
}
/**
* format quote body
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatQuoteBody()
{
assert(isInQuote);
if (isSpecialChar)
{
isSpecialChar = false;
}
else if (currentChar == '\\' && !isInVerbatimQuote)
{
if (peekNextChar() == ' ') // is this '\' at end of line
haveLineContinuationChar = true;
else
isSpecialChar = true;
}
else if (isInVerbatimQuote && currentChar == '"')
{
if (isCStyle())
{
string delim = ')' + verbatimDelimiter;
int delimStart = charNum - delim.length();
if (delimStart > 0 && currentLine.substr(delimStart, delim.length()) == delim)
{
isInQuote = false;
isInVerbatimQuote = false;
}
}
else if (isSharpStyle())
{
if ((int) currentLine.length() > charNum + 1
&& currentLine[charNum + 1] == '"') // check consecutive quotes
{
appendSequence("\"\"");
goForward(1);
return;
}
isInQuote = false;
isInVerbatimQuote = false;
}
}
else if (quoteChar == currentChar)
{
isInQuote = false;
}
appendCurrentChar();
// append the text to the ending quoteChar or an escape sequence
// tabs in quotes are NOT changed by convert-tabs
if (isInQuote && currentChar != '\\')
{
while (charNum + 1 < (int) currentLine.length()
&& currentLine[charNum + 1] != quoteChar
&& currentLine[charNum + 1] != '\\')
{
currentChar = currentLine[++charNum];
appendCurrentChar();
}
}
if (charNum + 1 >= (int) currentLine.length()
&& currentChar != '\\'
&& !isInVerbatimQuote)
isInQuote = false; // missing closing quote
}
/**
* format a quote opener
* the quote opener will be appended to the current formattedLine or a new formattedLine as necessary
* the calling function should have a continue statement after calling this method
*/
void ASFormatter::formatQuoteOpener()
{
assert(currentChar == '"'
|| (currentChar == '\'' && !isDigitSeparator(currentLine, charNum)));
isInQuote = true;
quoteChar = currentChar;
if (isCStyle() && previousChar == 'R')
{
int parenPos = currentLine.find('(', charNum);
if (parenPos != -1)
{
isInVerbatimQuote = true;
verbatimDelimiter = currentLine.substr(charNum + 1, parenPos - charNum - 1);
}
}
else if (isSharpStyle() && previousChar == '@')
isInVerbatimQuote = true;
// a quote following a brace is an array
if (previousCommandChar == '{'
&& !isImmediatelyPostComment
&& !isImmediatelyPostLineComment
&& isNonInStatementArray
&& !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
&& !isWhiteSpace(peekNextChar()))
{
if (braceFormatMode == NONE_MODE)
{
if (currentLineBeginsWithBrace)
formatRunIn();
}
else if (braceFormatMode == RUN_IN_MODE)
{
formatRunIn();
}
else if (braceFormatMode == BREAK_MODE)
{
if (formattedLine.length() > 0 && formattedLine[0] == '{')
isInLineBreak = true;
}
else
{
if (currentLineBeginsWithBrace)
isInLineBreak = true;
}
}
previousCommandChar = ' ';
appendCurrentChar();
}
/**
* get the next line comment adjustment that results from breaking a closing brace.
* the brace must be on the same line as the closing header.
* i.e "} else" changed to "} <NL> else".
*/
int ASFormatter::getNextLineCommentAdjustment()
{
assert(foundClosingHeader && previousNonWSChar == '}');
if (charNum < 1) // "else" is in column 1
return 0;
size_t lastBrace = currentLine.rfind('}', charNum - 1);
if (lastBrace != string::npos)
return (lastBrace - charNum); // return a negative number
return 0;
}
// for console build only
LineEndFormat ASFormatter::getLineEndFormat() const
{
return lineEnd;
}
/**
* get the current line comment adjustment that results from attaching
* a closing header to a closing brace.
* the brace must be on the line previous to the closing header.
* the adjustment is 2 chars, one for the brace and one for the space.
* i.e "} <NL> else" changed to "} else".
*/
int ASFormatter::getCurrentLineCommentAdjustment()
{
assert(foundClosingHeader && previousNonWSChar == '}');
if (charNum < 1)
return 2;
size_t lastBrace = currentLine.rfind('}', charNum - 1);
if (lastBrace == string::npos)
return 2;
return 0;
}
/**
* get the previous word on a line
* the argument 'currPos' must point to the current position.
*
* @return is the previous word or an empty string if none found.
*/
string ASFormatter::getPreviousWord(const string& line, int currPos) const
{
// get the last legal word (may be a number)
if (currPos == 0)
return string();
size_t end = line.find_last_not_of(" \t", currPos - 1);
if (end == string::npos || !isLegalNameChar(line[end]))
return string();
int start; // start of the previous word
for (start = end; start > -1; start--)
{
if (!isLegalNameChar(line[start]) || line[start] == '.')
break;
}
start++;
return (line.substr(start, end - start + 1));
}
/**
* check if a line break is needed when a closing brace
* is followed by a closing header.
* the break depends on the braceFormatMode and other factors.
*/
void ASFormatter::isLineBreakBeforeClosingHeader()
{
assert(foundClosingHeader && previousNonWSChar == '}');
if (currentHeader == &AS_WHILE && shouldAttachClosingWhile)
{
appendClosingHeader();
return;
}
if (braceFormatMode == BREAK_MODE
|| braceFormatMode == RUN_IN_MODE
|| attachClosingBraceMode)
{
isInLineBreak = true;
}
else if (braceFormatMode == NONE_MODE)
{
if (shouldBreakClosingHeaderBraces
|| getBraceIndent() || getBlockIndent())
{
isInLineBreak = true;
}
else
{
appendSpacePad();
// is closing brace broken?
size_t i = currentLine.find_first_not_of(" \t");
if (i != string::npos && currentLine[i] == '}')
isInLineBreak = false;
if (shouldBreakBlocks)
isAppendPostBlockEmptyLineRequested = false;
}
}
// braceFormatMode == ATTACH_MODE, LINUX_MODE
else
{
if (shouldBreakClosingHeaderBraces
|| getBraceIndent() || getBlockIndent())
{
isInLineBreak = true;
}
else
{
appendClosingHeader();
if (shouldBreakBlocks)
isAppendPostBlockEmptyLineRequested = false;
}
}
}
/**
* Append a closing header to the previous closing brace, if possible
*/
void ASFormatter::appendClosingHeader()
{
// if a blank line does not precede this
// or last line is not a one line block, attach header
bool previousLineIsEmpty = isEmptyLine(formattedLine);
int previousLineIsOneLineBlock = 0;
size_t firstBrace = findNextChar(formattedLine, '{');
if (firstBrace != string::npos)
previousLineIsOneLineBlock = isOneLineBlockReached(formattedLine, firstBrace);
if (!previousLineIsEmpty
&& previousLineIsOneLineBlock == 0)
{
isInLineBreak = false;
appendSpacePad();
spacePadNum = 0; // don't count as comment padding
}
}
/**
* Add braces to a single line statement following a header.
* braces are not added if the proper conditions are not met.
* braces are added to the currentLine.
*/
bool ASFormatter::addBracesToStatement()
{
assert(isImmediatelyPostHeader);
if (currentHeader != &AS_IF
&& currentHeader != &AS_ELSE
&& currentHeader != &AS_FOR
&& currentHeader != &AS_WHILE
&& currentHeader != &AS_DO
&& currentHeader != &AS_FOREACH
&& currentHeader != &AS_QFOREACH
&& currentHeader != &AS_QFOREVER
&& currentHeader != &AS_FOREVER)
return false;
if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while
return false;
// do not brace an empty statement
if (currentChar == ';')
return false;
// do not add if a header follows
if (isCharPotentialHeader(currentLine, charNum))
if (findHeader(headers) != nullptr)
return false;
// find the next semi-colon
size_t nextSemiColon = charNum;
if (currentChar != ';')
nextSemiColon = findNextChar(currentLine, ';', charNum + 1);
if (nextSemiColon == string::npos)
return false;
// add closing brace before changing the line length
if (nextSemiColon == currentLine.length() - 1)
currentLine.append(" }");
else
currentLine.insert(nextSemiColon + 1, " }");
// add opening brace
currentLine.insert(charNum, "{ ");
assert(computeChecksumIn("{}"));
currentChar = '{';
if ((int) currentLine.find_first_not_of(" \t") == charNum)
currentLineBeginsWithBrace = true;
// remove extra spaces
if (!shouldAddOneLineBraces)
{
size_t lastText = formattedLine.find_last_not_of(" \t");
if ((formattedLine.length() - 1) - lastText > 1)
formattedLine.erase(lastText + 1);
}
return true;
}
/**
* Remove braces from a single line statement following a header.
* braces are not removed if the proper conditions are not met.
* The first brace is replaced by a space.
*/
bool ASFormatter::removeBracesFromStatement()
{
assert(isImmediatelyPostHeader);
assert(currentChar == '{');
if (currentHeader != &AS_IF
&& currentHeader != &AS_ELSE
&& currentHeader != &AS_FOR
&& currentHeader != &AS_WHILE
&& currentHeader != &AS_FOREACH)
return false;
if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while
return false;
bool isFirstLine = true;
string nextLine_;
// leave nextLine_ empty if end of line comment follows
if (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace)
nextLine_ = currentLine.substr(charNum + 1);
size_t nextChar = 0;
// find the first non-blank text
ASPeekStream stream(sourceIterator);
while (stream.hasMoreLines() || isFirstLine)
{
if (isFirstLine)
isFirstLine = false;
else
{
nextLine_ = stream.peekNextLine();
nextChar = 0;
}
nextChar = nextLine_.find_first_not_of(" \t", nextChar);
if (nextChar != string::npos)
break;
}
// don't remove if comments or a header follow the brace
if ((nextLine_.compare(nextChar, 2, "/*") == 0)
|| (nextLine_.compare(nextChar, 2, "//") == 0)
|| (isCharPotentialHeader(nextLine_, nextChar)
&& ASBase::findHeader(nextLine_, nextChar, headers) != nullptr))
return false;
// find the next semi-colon
size_t nextSemiColon = nextChar;
if (nextLine_[nextChar] != ';')
nextSemiColon = findNextChar(nextLine_, ';', nextChar + 1);
if (nextSemiColon == string::npos)
return false;
// find the closing brace
isFirstLine = true;
nextChar = nextSemiColon + 1;
while (stream.hasMoreLines() || isFirstLine)
{
if (isFirstLine)
isFirstLine = false;
else
{
nextLine_ = stream.peekNextLine();
nextChar = 0;
}
nextChar = nextLine_.find_first_not_of(" \t", nextChar);
if (nextChar != string::npos)
break;
}
if (nextLine_.length() == 0 || nextLine_[nextChar] != '}')
return false;
// remove opening brace
currentLine[charNum] = currentChar = ' ';
assert(adjustChecksumIn(-'{'));
return true;
}
/**
* Find the next character that is not in quotes or a comment.
*
* @param line the line to be searched.
* @param searchChar the char to find.
* @param searchStart the start position on the line (default is 0).
* @return the position on the line or string::npos if not found.
*/
size_t ASFormatter::findNextChar(const string& line, char searchChar, int searchStart /*0*/) const
{
// find the next searchChar
size_t i;
for (i = searchStart; i < line.length(); i++)
{
if (line.compare(i, 2, "//") == 0)
return string::npos;
if (line.compare(i, 2, "/*") == 0)
{
size_t endComment = line.find("*/", i + 2);
if (endComment == string::npos)
return string::npos;
i = endComment + 2;
if (i >= line.length())
return string::npos;
}
if (line[i] == '"'
|| (line[i] == '\'' && !isDigitSeparator(line, i)))
{
char quote = line[i];
while (i < line.length())
{
size_t endQuote = line.find(quote, i + 1);
if (endQuote == string::npos)
return string::npos;
i = endQuote;
if (line[endQuote - 1] != '\\') // check for '\"'
break;
if (line[endQuote - 2] == '\\') // check for '\\'
break;
}
}
if (line[i] == searchChar)
break;
// for now don't process C# 'delegate' braces
// do this last in case the search char is a '{'
if (line[i] == '{')
return string::npos;
}
if (i >= line.length()) // didn't find searchChar
return string::npos;
return i;
}
/**
* Look ahead in the file to see if a struct has access modifiers.
*
* @param firstLine a reference to the line to indent.
* @param index the current line index.
* @return true if the struct has access modifiers.
*/
bool ASFormatter::isStructAccessModified(const string& firstLine, size_t index) const
{
assert(firstLine[index] == '{');
assert(isCStyle());
bool isFirstLine = true;
size_t braceCount = 1;
string nextLine_ = firstLine.substr(index + 1);
ASPeekStream stream(sourceIterator);
// find the first non-blank text, bypassing all comments and quotes.
bool isInComment_ = false;
bool isInQuote_ = false;
char quoteChar_ = ' ';
while (stream.hasMoreLines() || isFirstLine)
{
if (isFirstLine)
isFirstLine = false;
else
nextLine_ = stream.peekNextLine();
// parse the line
for (size_t i = 0; i < nextLine_.length(); i++)
{
if (isWhiteSpace(nextLine_[i]))
continue;
if (nextLine_.compare(i, 2, "/*") == 0)
isInComment_ = true;
if (isInComment_)
{
if (nextLine_.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (nextLine_[i] == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (nextLine_[i] == quoteChar_)
isInQuote_ = false;
continue;
}
if (nextLine_[i] == '"'
|| (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
{
isInQuote_ = true;
quoteChar_ = nextLine_[i];
continue;
}
if (nextLine_.compare(i, 2, "//") == 0)
{
i = nextLine_.length();
continue;
}
// handle braces
if (nextLine_[i] == '{')
++braceCount;
if (nextLine_[i] == '}')
--braceCount;
if (braceCount == 0)
return false;
// check for access modifiers
if (isCharPotentialHeader(nextLine_, i))
{
if (findKeyword(nextLine_, i, AS_PUBLIC)
|| findKeyword(nextLine_, i, AS_PRIVATE)
|| findKeyword(nextLine_, i, AS_PROTECTED))
return true;
string name = getCurrentWord(nextLine_, i);
i += name.length() - 1;
}
} // end of for loop
} // end of while loop
return false;
}
/**
* Look ahead in the file to see if a preprocessor block is indentable.
*
* @param firstLine a reference to the line to indent.
* @param index the current line index.
* @return true if the block is indentable.
*/
bool ASFormatter::isIndentablePreprocessorBlock(const string& firstLine, size_t index)
{
assert(firstLine[index] == '#');
bool isFirstLine = true;
bool isInIndentableBlock = false;
bool blockContainsBraces = false;
bool blockContainsDefineContinuation = false;
bool isInClassConstructor = false;
bool isPotentialHeaderGuard = false; // ifndef is first preproc statement
bool isPotentialHeaderGuard2 = false; // define is within the first proproc
int numBlockIndents = 0;
int lineParenCount = 0;
string nextLine_ = firstLine.substr(index);
auto stream = make_shared<ASPeekStream>(sourceIterator);
// find end of the block, bypassing all comments and quotes.
bool isInComment_ = false;
bool isInQuote_ = false;
char quoteChar_ = ' ';
while (stream->hasMoreLines() || isFirstLine)
{
if (isFirstLine)
isFirstLine = false;
else
nextLine_ = stream->peekNextLine();
// parse the line
for (size_t i = 0; i < nextLine_.length(); i++)
{
if (isWhiteSpace(nextLine_[i]))
continue;
if (nextLine_.compare(i, 2, "/*") == 0)
isInComment_ = true;
if (isInComment_)
{
if (nextLine_.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (nextLine_[i] == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (nextLine_[i] == quoteChar_)
isInQuote_ = false;
continue;
}
if (nextLine_[i] == '"'
|| (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
{
isInQuote_ = true;
quoteChar_ = nextLine_[i];
continue;
}
if (nextLine_.compare(i, 2, "//") == 0)
{
i = nextLine_.length();
continue;
}
// handle preprocessor statement
if (nextLine_[i] == '#')
{
string preproc = ASBeautifier::extractPreprocessorStatement(nextLine_);
if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef
{
numBlockIndents += 1;
isInIndentableBlock = true;
// flag first preprocessor conditional for header include guard check
if (!processedFirstConditional)
{
processedFirstConditional = true;
isFirstPreprocConditional = true;
if (isNDefPreprocStatement(nextLine_, preproc))
isPotentialHeaderGuard = true;
}
}
else if (preproc == "endif")
{
if (numBlockIndents > 0)
numBlockIndents -= 1;
// must exit BOTH loops
if (numBlockIndents == 0)
goto EndOfWhileLoop;
}
else if (preproc == "define")
{
if (nextLine_[nextLine_.length() - 1] == '\\')
blockContainsDefineContinuation = true;
// check for potential header include guards
else if (isPotentialHeaderGuard && numBlockIndents == 1)
isPotentialHeaderGuard2 = true;
}
i = nextLine_.length();
continue;
}
// handle exceptions
if (nextLine_[i] == '{' || nextLine_[i] == '}')
blockContainsBraces = true;
else if (nextLine_[i] == '(')
++lineParenCount;
else if (nextLine_[i] == ')')
--lineParenCount;
else if (nextLine_[i] == ':')
{
// check for '::'
if (nextLine_.length() > i && nextLine_[i + 1] == ':')
++i;
else
isInClassConstructor = true;
}
// bypass unnecessary parsing - must exit BOTH loops
if (blockContainsBraces || isInClassConstructor || blockContainsDefineContinuation)
goto EndOfWhileLoop;
} // end of for loop, end of line
if (lineParenCount != 0)
break;
} // end of while loop
EndOfWhileLoop:
preprocBlockEnd = sourceIterator->tellg();
if (preprocBlockEnd < 0)
preprocBlockEnd = sourceIterator->getStreamLength();
if (blockContainsBraces
|| isInClassConstructor
|| blockContainsDefineContinuation
|| lineParenCount != 0
|| numBlockIndents != 0)
isInIndentableBlock = false;
// find next executable instruction
// this WILL RESET the get pointer
string nextText = peekNextText("", false, stream);
// bypass header include guards
if (isFirstPreprocConditional)
{
isFirstPreprocConditional = false;
if (nextText.empty() && isPotentialHeaderGuard2)
{
isInIndentableBlock = false;
preprocBlockEnd = 0;
}
}
// this allows preprocessor blocks within this block to be indented
if (!isInIndentableBlock)
preprocBlockEnd = 0;
// peekReset() is done by previous peekNextText()
return isInIndentableBlock;
}
bool ASFormatter::isNDefPreprocStatement(const string& nextLine_, const string& preproc) const
{
if (preproc == "ifndef")
return true;
// check for '!defined'
if (preproc == "if")
{
size_t i = nextLine_.find('!');
if (i == string::npos)
return false;
i = nextLine_.find_first_not_of(" \t", ++i);
if (i != string::npos && nextLine_.compare(i, 7, "defined") == 0)
return true;
}
return false;
}
/**
* Check to see if this is an EXEC SQL statement.
*
* @param line a reference to the line to indent.
* @param index the current line index.
* @return true if the statement is EXEC SQL.
*/
bool ASFormatter::isExecSQL(const string& line, size_t index) const
{
if (line[index] != 'e' && line[index] != 'E') // quick check to reject most
return false;
string word;
if (isCharPotentialHeader(line, index))
word = getCurrentWord(line, index);
for (size_t i = 0; i < word.length(); i++)
word[i] = (char) toupper(word[i]);
if (word != "EXEC")
return false;
size_t index2 = index + word.length();
index2 = line.find_first_not_of(" \t", index2);
if (index2 == string::npos)
return false;
word.erase();
if (isCharPotentialHeader(line, index2))
word = getCurrentWord(line, index2);
for (size_t i = 0; i < word.length(); i++)
word[i] = (char) toupper(word[i]);
if (word != "SQL")
return false;
return true;
}
/**
* The continuation lines must be adjusted so the leading spaces
* is equivalent to the text on the opening line.
*
* Updates currentLine and charNum.
*/
void ASFormatter::trimContinuationLine()
{
size_t len = currentLine.length();
size_t tabSize = getTabLength();
charNum = 0;
if (leadingSpaces > 0 && len > 0)
{
size_t i;
size_t continuationIncrementIn = 0;
for (i = 0; (i < len) && (i + continuationIncrementIn < leadingSpaces); i++)
{
if (!isWhiteSpace(currentLine[i])) // don't delete any text
{
if (i < continuationIncrementIn)
leadingSpaces = i + tabIncrementIn;
continuationIncrementIn = tabIncrementIn;
break;
}
if (currentLine[i] == '\t')
continuationIncrementIn += tabSize - 1 - ((continuationIncrementIn + i) % tabSize);
}
if ((int) continuationIncrementIn == tabIncrementIn)
charNum = i;
else
{
// build a new line with the equivalent leading chars
string newLine;
int leadingChars = 0;
if ((int) leadingSpaces > tabIncrementIn)
leadingChars = leadingSpaces - tabIncrementIn;
newLine.append(leadingChars, ' ');
newLine.append(currentLine, i, len - i);
currentLine = newLine;
charNum = leadingChars;
if (currentLine.length() == 0)
currentLine = string(" "); // a null is inserted if this is not done
}
if (i >= len)
charNum = 0;
}
}
/**
* Determine if a header is a closing header
*
* @return true if the header is a closing header.
*/
bool ASFormatter::isClosingHeader(const string* header) const
{
return (header == &AS_ELSE
|| header == &AS_CATCH
|| header == &AS_FINALLY);
}
/**
* Determine if a * following a closing paren is immediately.
* after a cast. If so it is a deference and not a multiply.
* e.g. "(int*) *ptr" is a deference.
*/
bool ASFormatter::isImmediatelyPostCast() const
{
assert(previousNonWSChar == ')' && currentChar == '*');
// find preceding closing paren on currentLine or readyFormattedLine
string line; // currentLine or readyFormattedLine
size_t paren = currentLine.rfind(')', charNum);
if (paren != string::npos)
line = currentLine;
// if not on currentLine it must be on the previous line
else
{
line = readyFormattedLine;
paren = line.rfind(')');
if (paren == string::npos)
return false;
}
if (paren == 0)
return false;
// find character preceding the closing paren
size_t lastChar = line.find_last_not_of(" \t", paren - 1);
if (lastChar == string::npos)
return false;
// check for pointer cast
if (line[lastChar] == '*')
return true;
return false;
}
/**
* Determine if a < is a template definition or instantiation.
* Sets the class variables isInTemplate and templateDepth.
*/
void ASFormatter::checkIfTemplateOpener()
{
assert(!isInTemplate && currentChar == '<');
// find first char after the '<' operators
size_t firstChar = currentLine.find_first_not_of("< \t", charNum);
if (firstChar == string::npos
|| currentLine[firstChar] == '=')
{
// this is not a template -> leave...
isInTemplate = false;
return;
}
bool isFirstLine = true;
int parenDepth_ = 0;
int maxTemplateDepth = 0;
templateDepth = 0;
string nextLine_ = currentLine.substr(charNum);
ASPeekStream stream(sourceIterator);
// find the angle braces, bypassing all comments and quotes.
bool isInComment_ = false;
bool isInQuote_ = false;
char quoteChar_ = ' ';
while (stream.hasMoreLines() || isFirstLine)
{
if (isFirstLine)
isFirstLine = false;
else
nextLine_ = stream.peekNextLine();
// parse the line
for (size_t i = 0; i < nextLine_.length(); i++)
{
char currentChar_ = nextLine_[i];
if (isWhiteSpace(currentChar_))
continue;
if (nextLine_.compare(i, 2, "/*") == 0)
isInComment_ = true;
if (isInComment_)
{
if (nextLine_.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (currentChar_ == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (currentChar_ == quoteChar_)
isInQuote_ = false;
continue;
}
if (currentChar_ == '"'
|| (currentChar_ == '\'' && !isDigitSeparator(nextLine_, i)))
{
isInQuote_ = true;
quoteChar_ = currentChar_;
continue;
}
if (nextLine_.compare(i, 2, "//") == 0)
{
i = nextLine_.length();
continue;
}
// not in a comment or quote
if (currentChar_ == '<')
{
++templateDepth;
++maxTemplateDepth;
continue;
}
else if (currentChar_ == '>')
{
--templateDepth;
if (templateDepth == 0)
{
if (parenDepth_ == 0)
{
// this is a template!
isInTemplate = true;
templateDepth = maxTemplateDepth;
}
return;
}
continue;
}
else if (currentChar_ == '(' || currentChar_ == ')')
{
if (currentChar_ == '(')
++parenDepth_;
else
--parenDepth_;
if (parenDepth_ >= 0)
continue;
// this is not a template -> leave...
isInTemplate = false;
templateDepth = 0;
return;
}
else if (nextLine_.compare(i, 2, AS_AND) == 0
|| nextLine_.compare(i, 2, AS_OR) == 0)
{
// this is not a template -> leave...
isInTemplate = false;
templateDepth = 0;
return;
}
else if (currentChar_ == ',' // comma, e.g. A<int, char>
|| currentChar_ == '&' // reference, e.g. A<int&>
|| currentChar_ == '*' // pointer, e.g. A<int*>
|| currentChar_ == '^' // C++/CLI managed pointer, e.g. A<int^>
|| currentChar_ == ':' // ::, e.g. std::string
|| currentChar_ == '=' // assign e.g. default parameter
|| currentChar_ == '[' // [] e.g. string[]
|| currentChar_ == ']' // [] e.g. string[]
|| currentChar_ == '(' // (...) e.g. function definition
|| currentChar_ == ')' // (...) e.g. function definition
|| (isJavaStyle() && currentChar_ == '?') // Java wildcard
)
{
continue;
}
else if (!isLegalNameChar(currentChar_))
{
// this is not a template -> leave...
isInTemplate = false;
templateDepth = 0;
return;
}
string name = getCurrentWord(nextLine_, i);
i += name.length() - 1;
} // end for loop
} // end while loop
}
void ASFormatter::updateFormattedLineSplitPoints(char appendedChar)
{
assert(maxCodeLength != string::npos);
assert(formattedLine.length() > 0);
if (!isOkToSplitFormattedLine())
return;
char nextChar = peekNextChar();
// don't split before an end of line comment
if (nextChar == '/')
return;
// don't split before or after a brace
if (appendedChar == '{' || appendedChar == '}'
|| previousNonWSChar == '{' || previousNonWSChar == '}'
|| nextChar == '{' || nextChar == '}'
|| currentChar == '{' || currentChar == '}') // currentChar tests for an appended brace
return;
// don't split before or after a block paren
if (appendedChar == '[' || appendedChar == ']'
|| previousNonWSChar == '['
|| nextChar == '[' || nextChar == ']')
return;
if (isWhiteSpace(appendedChar))
{
if (nextChar != ')' // space before a closing paren
&& nextChar != '(' // space before an opening paren
&& nextChar != '/' // space before a comment
&& nextChar != ':' // space before a colon
&& currentChar != ')' // appended space before and after a closing paren
&& currentChar != '(' // appended space before and after a opening paren
&& previousNonWSChar != '(' // decided at the '('
// don't break before a pointer or reference aligned to type
&& !(nextChar == '*'
&& !isCharPotentialOperator(previousNonWSChar)
&& pointerAlignment == PTR_ALIGN_TYPE)
&& !(nextChar == '&'
&& !isCharPotentialOperator(previousNonWSChar)
&& (referenceAlignment == REF_ALIGN_TYPE
|| (referenceAlignment == REF_SAME_AS_PTR && pointerAlignment == PTR_ALIGN_TYPE)))
)
{
if (formattedLine.length() - 1 <= maxCodeLength)
maxWhiteSpace = formattedLine.length() - 1;
else
maxWhiteSpacePending = formattedLine.length() - 1;
}
}
// unpadded closing parens may split after the paren (counts as whitespace)
else if (appendedChar == ')')
{
if (nextChar != ')'
&& nextChar != ' '
&& nextChar != ';'
&& nextChar != ','
&& nextChar != '.'
&& !(nextChar == '-' && pointerSymbolFollows())) // check for ->
{
if (formattedLine.length() <= maxCodeLength)
maxWhiteSpace = formattedLine.length();
else
maxWhiteSpacePending = formattedLine.length();
}
}
// unpadded commas may split after the comma
else if (appendedChar == ',')
{
if (formattedLine.length() <= maxCodeLength)
maxComma = formattedLine.length();
else
maxCommaPending = formattedLine.length();
}
else if (appendedChar == '(')
{
if (nextChar != ')' && nextChar != '(' && nextChar != '"' && nextChar != '\'')
{
// if follows an operator break before
size_t parenNum;
if (isCharPotentialOperator(previousNonWSChar))
parenNum = formattedLine.length() - 1;
else
parenNum = formattedLine.length();
if (formattedLine.length() <= maxCodeLength)
maxParen = parenNum;
else
maxParenPending = parenNum;
}
}
else if (appendedChar == ';')
{
if (nextChar != ' ' && nextChar != '}' && nextChar != '/') // check for following comment
{
if (formattedLine.length() <= maxCodeLength)
maxSemi = formattedLine.length();
else
maxSemiPending = formattedLine.length();
}
}
}
void ASFormatter::updateFormattedLineSplitPointsOperator(const string& sequence)
{
assert(maxCodeLength != string::npos);
assert(formattedLine.length() > 0);
if (!isOkToSplitFormattedLine())
return;
char nextChar = peekNextChar();
// don't split before an end of line comment
if (nextChar == '/')
return;
// check for logical conditional
if (sequence == "||" || sequence == "&&" || sequence == "or" || sequence == "and")
{
if (shouldBreakLineAfterLogical)
{
if (formattedLine.length() <= maxCodeLength)
maxAndOr = formattedLine.length();
else
maxAndOrPending = formattedLine.length();
}
else
{
// adjust for leading space in the sequence
size_t sequenceLength = sequence.length();
if (formattedLine.length() > sequenceLength
&& isWhiteSpace(formattedLine[formattedLine.length() - sequenceLength - 1]))
sequenceLength++;
if (formattedLine.length() - sequenceLength <= maxCodeLength)
maxAndOr = formattedLine.length() - sequenceLength;
else
maxAndOrPending = formattedLine.length() - sequenceLength;
}
}
// comparison operators will split after the operator (counts as whitespace)
else if (sequence == "==" || sequence == "!=" || sequence == ">=" || sequence == "<=")
{
if (formattedLine.length() <= maxCodeLength)
maxWhiteSpace = formattedLine.length();
else
maxWhiteSpacePending = formattedLine.length();
}
// unpadded operators that will split BEFORE the operator (counts as whitespace)
else if (sequence == "+" || sequence == "-" || sequence == "?")
{
if (charNum > 0
&& !(sequence == "+" && isInExponent())
&& !(sequence == "-" && isInExponent())
&& (isLegalNameChar(currentLine[charNum - 1])
|| currentLine[charNum - 1] == ')'
|| currentLine[charNum - 1] == ']'
|| currentLine[charNum - 1] == '\"'))
{
if (formattedLine.length() - 1 <= maxCodeLength)
maxWhiteSpace = formattedLine.length() - 1;
else
maxWhiteSpacePending = formattedLine.length() - 1;
}
}
// unpadded operators that will USUALLY split AFTER the operator (counts as whitespace)
else if (sequence == "=" || sequence == ":")
{
// split BEFORE if the line is too long
// do NOT use <= here, must allow for a brace attached to an array
size_t splitPoint = 0;
if (formattedLine.length() < maxCodeLength)
splitPoint = formattedLine.length();
else
splitPoint = formattedLine.length() - 1;
// padded or unpadded arrays
if (previousNonWSChar == ']')
{
if (formattedLine.length() - 1 <= maxCodeLength)
maxWhiteSpace = splitPoint;
else
maxWhiteSpacePending = splitPoint;
}
else if (charNum > 0
&& (isLegalNameChar(currentLine[charNum - 1])
|| currentLine[charNum - 1] == ')'
|| currentLine[charNum - 1] == ']'))
{
if (formattedLine.length() <= maxCodeLength)
maxWhiteSpace = splitPoint;
else
maxWhiteSpacePending = splitPoint;
}
}
}
/**
* Update the split point when a pointer or reference is formatted.
* The argument is the maximum index of the last whitespace character.
*/
void ASFormatter::updateFormattedLineSplitPointsPointerOrReference(size_t index)
{
assert(maxCodeLength != string::npos);
assert(formattedLine.length() > 0);
assert(index < formattedLine.length());
if (!isOkToSplitFormattedLine())
return;
if (index < maxWhiteSpace) // just in case
return;
if (index <= maxCodeLength)
maxWhiteSpace = index;
else
maxWhiteSpacePending = index;
}
bool ASFormatter::isOkToSplitFormattedLine()
{
assert(maxCodeLength != string::npos);
// Is it OK to split the line?
if (shouldKeepLineUnbroken
|| isInLineComment
|| isInComment
|| isInQuote
|| isInCase
|| isInPreprocessor
|| isInExecSQL
|| isInAsm || isInAsmOneLine || isInAsmBlock
|| isInTemplate)
return false;
if (!isOkToBreakBlock(braceTypeStack->back()) && currentChar != '{')
{
shouldKeepLineUnbroken = true;
clearFormattedLineSplitPoints();
return false;
}
if (isBraceType(braceTypeStack->back(), ARRAY_TYPE))
{
shouldKeepLineUnbroken = true;
if (!isBraceType(braceTypeStack->back(), ARRAY_NIS_TYPE))
clearFormattedLineSplitPoints();
return false;
}
return true;
}
/* This is called if the option maxCodeLength is set.
*/
void ASFormatter::testForTimeToSplitFormattedLine()
{
// DO NOT ASSERT maxCodeLength HERE
// should the line be split
if (formattedLine.length() > maxCodeLength && !isLineReady)
{
size_t splitPoint = findFormattedLineSplitPoint();
if (splitPoint > 0 && splitPoint < formattedLine.length())
{
string splitLine = formattedLine.substr(splitPoint);
formattedLine = formattedLine.substr(0, splitPoint);
breakLine(true);
formattedLine = splitLine;
// if break-blocks is requested and this is a one-line statement
string nextWord = ASBeautifier::getNextWord(currentLine, charNum - 1);
if (isAppendPostBlockEmptyLineRequested
&& (nextWord == "break" || nextWord == "continue"))
{
isAppendPostBlockEmptyLineRequested = false;
isPrependPostBlockEmptyLineRequested = true;
}
else
isPrependPostBlockEmptyLineRequested = false;
// adjust max split points
maxAndOr = (maxAndOr > splitPoint) ? (maxAndOr - splitPoint) : 0;
maxSemi = (maxSemi > splitPoint) ? (maxSemi - splitPoint) : 0;
maxComma = (maxComma > splitPoint) ? (maxComma - splitPoint) : 0;
maxParen = (maxParen > splitPoint) ? (maxParen - splitPoint) : 0;
maxWhiteSpace = (maxWhiteSpace > splitPoint) ? (maxWhiteSpace - splitPoint) : 0;
if (maxSemiPending > 0)
{
maxSemi = (maxSemiPending > splitPoint) ? (maxSemiPending - splitPoint) : 0;
maxSemiPending = 0;
}
if (maxAndOrPending > 0)
{
maxAndOr = (maxAndOrPending > splitPoint) ? (maxAndOrPending - splitPoint) : 0;
maxAndOrPending = 0;
}
if (maxCommaPending > 0)
{
maxComma = (maxCommaPending > splitPoint) ? (maxCommaPending - splitPoint) : 0;
maxCommaPending = 0;
}
if (maxParenPending > 0)
{
maxParen = (maxParenPending > splitPoint) ? (maxParenPending - splitPoint) : 0;
maxParenPending = 0;
}
if (maxWhiteSpacePending > 0)
{
maxWhiteSpace = (maxWhiteSpacePending > splitPoint) ? (maxWhiteSpacePending - splitPoint) : 0;
maxWhiteSpacePending = 0;
}
// don't allow an empty formatted line
size_t firstText = formattedLine.find_first_not_of(" \t");
if (firstText == string::npos && formattedLine.length() > 0)
{
formattedLine.erase();
clearFormattedLineSplitPoints();
if (isWhiteSpace(currentChar))
for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
goForward(1);
}
else if (firstText > 0)
{
formattedLine.erase(0, firstText);
maxSemi = (maxSemi > firstText) ? (maxSemi - firstText) : 0;
maxAndOr = (maxAndOr > firstText) ? (maxAndOr - firstText) : 0;
maxComma = (maxComma > firstText) ? (maxComma - firstText) : 0;
maxParen = (maxParen > firstText) ? (maxParen - firstText) : 0;
maxWhiteSpace = (maxWhiteSpace > firstText) ? (maxWhiteSpace - firstText) : 0;
}
// reset formattedLineCommentNum
if (formattedLineCommentNum != string::npos)
{
formattedLineCommentNum = formattedLine.find("//");
if (formattedLineCommentNum == string::npos)
formattedLineCommentNum = formattedLine.find("/*");
}
}
}
}
size_t ASFormatter::findFormattedLineSplitPoint() const
{
assert(maxCodeLength != string::npos);
// determine where to split
size_t minCodeLength = 10;
size_t splitPoint = 0;
splitPoint = maxSemi;
if (maxAndOr >= minCodeLength)
splitPoint = maxAndOr;
if (splitPoint < minCodeLength)
{
splitPoint = maxWhiteSpace;
// use maxParen instead if it is long enough
if (maxParen > splitPoint
|| maxParen >= maxCodeLength * .7)
splitPoint = maxParen;
// use maxComma instead if it is long enough
// increasing the multiplier causes more splits at whitespace
if (maxComma > splitPoint
|| maxComma >= maxCodeLength * .3)
splitPoint = maxComma;
}
// replace split point with first available break point
if (splitPoint < minCodeLength)
{
splitPoint = string::npos;
if (maxSemiPending > 0 && maxSemiPending < splitPoint)
splitPoint = maxSemiPending;
if (maxAndOrPending > 0 && maxAndOrPending < splitPoint)
splitPoint = maxAndOrPending;
if (maxCommaPending > 0 && maxCommaPending < splitPoint)
splitPoint = maxCommaPending;
if (maxParenPending > 0 && maxParenPending < splitPoint)
splitPoint = maxParenPending;
if (maxWhiteSpacePending > 0 && maxWhiteSpacePending < splitPoint)
splitPoint = maxWhiteSpacePending;
if (splitPoint == string::npos)
splitPoint = 0;
}
// if remaining line after split is too long
else if (formattedLine.length() - splitPoint > maxCodeLength)
{
// if end of the currentLine, find a new split point
size_t newCharNum;
if (isCharPotentialHeader(currentLine, charNum))
newCharNum = getCurrentWord(currentLine, charNum).length() + charNum;
else
newCharNum = charNum + 2;
if (newCharNum + 1 > currentLine.length())
{
// don't move splitPoint from before a conditional to after
if (maxWhiteSpace > splitPoint + 3)
splitPoint = maxWhiteSpace;
if (maxParen > splitPoint)
splitPoint = maxParen;
}
}
return splitPoint;
}
void ASFormatter::clearFormattedLineSplitPoints()
{
maxSemi = 0;
maxAndOr = 0;
maxComma = 0;
maxParen = 0;
maxWhiteSpace = 0;
maxSemiPending = 0;
maxAndOrPending = 0;
maxCommaPending = 0;
maxParenPending = 0;
maxWhiteSpacePending = 0;
}
/**
* Check if a pointer symbol (->) follows on the currentLine.
*/
bool ASFormatter::pointerSymbolFollows() const
{
size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (peekNum == string::npos || currentLine.compare(peekNum, 2, "->") != 0)
return false;
return true;
}
/**
* Compute the input checksum.
* This is called as an assert so it for is debug config only
*/
bool ASFormatter::computeChecksumIn(const string& currentLine_)
{
for (size_t i = 0; i < currentLine_.length(); i++)
if (!isWhiteSpace(currentLine_[i]))
checksumIn += currentLine_[i];
return true;
}
/**
* Adjust the input checksum for deleted chars.
* This is called as an assert so it for is debug config only
*/
bool ASFormatter::adjustChecksumIn(int adjustment)
{
checksumIn += adjustment;
return true;
}
/**
* get the value of checksumIn for unit testing
*
* @return checksumIn.
*/
size_t ASFormatter::getChecksumIn() const
{
return checksumIn;
}
/**
* Compute the output checksum.
* This is called as an assert so it is for debug config only
*/
bool ASFormatter::computeChecksumOut(const string& beautifiedLine)
{
for (size_t i = 0; i < beautifiedLine.length(); i++)
if (!isWhiteSpace(beautifiedLine[i]))
checksumOut += beautifiedLine[i];
return true;
}
/**
* Return isLineReady for the final check at end of file.
*/
bool ASFormatter::getIsLineReady() const
{
return isLineReady;
}
/**
* get the value of checksumOut for unit testing
*
* @return checksumOut.
*/
size_t ASFormatter::getChecksumOut() const
{
return checksumOut;
}
/**
* Return the difference in checksums.
* If zero all is okay.
*/
int ASFormatter::getChecksumDiff() const
{
return checksumOut - checksumIn;
}
// for unit testing
int ASFormatter::getFormatterFileType() const
{
return formatterFileType;
}
// Check if an operator follows the next word.
// The next word must be a legal name.
const string* ASFormatter::getFollowingOperator() const
{
// find next word
size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextNum == string::npos)
return nullptr;
if (!isLegalNameChar(currentLine[nextNum]))
return nullptr;
// bypass next word and following spaces
while (nextNum < currentLine.length())
{
if (!isLegalNameChar(currentLine[nextNum])
&& !isWhiteSpace(currentLine[nextNum]))
break;
nextNum++;
}
if (nextNum >= currentLine.length()
|| !isCharPotentialOperator(currentLine[nextNum])
|| currentLine[nextNum] == '/') // comment
return nullptr;
const string* newOperator = ASBase::findOperator(currentLine, nextNum, operators);
return newOperator;
}
// Check following data to determine if the current character is an array operator.
bool ASFormatter::isArrayOperator() const
{
assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE));
// find next word
size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextNum == string::npos)
return false;
if (!isLegalNameChar(currentLine[nextNum]))
return false;
// bypass next word and following spaces
while (nextNum < currentLine.length())
{
if (!isLegalNameChar(currentLine[nextNum])
&& !isWhiteSpace(currentLine[nextNum]))
break;
nextNum++;
}
// check for characters that indicate an operator
if (currentLine[nextNum] == ','
|| currentLine[nextNum] == '}'
|| currentLine[nextNum] == ')'
|| currentLine[nextNum] == '(')
return true;
return false;
}
// Reset the flags that indicate various statement information.
void ASFormatter::resetEndOfStatement()
{
foundQuestionMark = false;
foundNamespaceHeader = false;
foundClassHeader = false;
foundStructHeader = false;
foundInterfaceHeader = false;
foundPreDefinitionHeader = false;
foundPreCommandHeader = false;
foundPreCommandMacro = false;
foundTrailingReturnType = false;
foundCastOperator = false;
isInPotentialCalculation = false;
isSharpAccessor = false;
isSharpDelegate = false;
isInObjCMethodDefinition = false;
isInObjCInterface = false;
isInObjCSelector = false;
isInEnum = false;
isInExternC = false;
elseHeaderFollowsComments = false;
nonInStatementBrace = 0;
while (!questionMarkStack->empty())
questionMarkStack->pop_back();
}
// Find the colon alignment for Objective-C method definitions and method calls.
int ASFormatter::findObjCColonAlignment() const
{
assert(currentChar == '+' || currentChar == '-' || currentChar == '[');
assert(getAlignMethodColon());
bool isFirstLine = true;
bool haveFirstColon = false;
bool foundMethodColon = false;
bool isInComment_ = false;
bool isInQuote_ = false;
char quoteChar_ = ' ';
int sqBracketCount = 0;
int colonAdjust = 0;
int colonAlign = 0;
string nextLine_ = currentLine;
ASPeekStream stream(sourceIterator);
// peek next line
while (sourceIterator->hasMoreLines() || isFirstLine)
{
if (!isFirstLine)
nextLine_ = stream.peekNextLine();
// parse the line
haveFirstColon = false;
nextLine_ = ASBeautifier::trim(nextLine_);
for (size_t i = 0; i < nextLine_.length(); i++)
{
if (isWhiteSpace(nextLine_[i]))
continue;
if (nextLine_.compare(i, 2, "/*") == 0)
isInComment_ = true;
if (isInComment_)
{
if (nextLine_.compare(i, 2, "*/") == 0)
{
isInComment_ = false;
++i;
}
continue;
}
if (nextLine_[i] == '\\')
{
++i;
continue;
}
if (isInQuote_)
{
if (nextLine_[i] == quoteChar_)
isInQuote_ = false;
continue;
}
if (nextLine_[i] == '"'
|| (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
{
isInQuote_ = true;
quoteChar_ = nextLine_[i];
continue;
}
if (nextLine_.compare(i, 2, "//") == 0)
{
i = nextLine_.length();
continue;
}
// process the current char
if ((nextLine_[i] == '{' && (currentChar == '-' || currentChar == '+'))
|| nextLine_[i] == ';')
goto EndOfWhileLoop; // end of method definition
if (nextLine_[i] == ']')
{
--sqBracketCount;
if (sqBracketCount == 0)
goto EndOfWhileLoop; // end of method call
}
if (nextLine_[i] == '[')
++sqBracketCount;
if (isFirstLine) // colon align does not include the first line
continue;
if (sqBracketCount > 1)
continue;
if (haveFirstColon) // multiple colons per line
continue;
// compute colon adjustment
if (nextLine_[i] == ':')
{
haveFirstColon = true;
foundMethodColon = true;
if (shouldPadMethodColon)
{
int spacesStart;
for (spacesStart = i; spacesStart > 0; spacesStart--)
if (!isWhiteSpace(nextLine_[spacesStart - 1]))
break;
int spaces = i - spacesStart;
if (objCColonPadMode == COLON_PAD_ALL || objCColonPadMode == COLON_PAD_BEFORE)
colonAdjust = 1 - spaces;
else if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_AFTER)
colonAdjust = 0 - spaces;
}
// compute alignment
int colonPosition = i + colonAdjust;
if (colonPosition > colonAlign)
colonAlign = colonPosition;
}
} // end of for loop
isFirstLine = false;
} // end of while loop
EndOfWhileLoop:
if (!foundMethodColon)
colonAlign = -1;
return colonAlign;
}
// pad an Objective-C method colon
void ASFormatter::padObjCMethodColon()
{
assert(currentChar == ':');
int commentAdjust = 0;
char nextChar = peekNextChar();
if (objCColonPadMode == COLON_PAD_NONE
|| objCColonPadMode == COLON_PAD_AFTER
|| nextChar == ')')
{
// remove spaces before
for (int i = formattedLine.length() - 1; (i > -1) && isWhiteSpace(formattedLine[i]); i--)
{
formattedLine.erase(i);
--commentAdjust;
}
}
else
{
// pad space before
for (int i = formattedLine.length() - 1; (i > 0) && isWhiteSpace(formattedLine[i]); i--)
if (isWhiteSpace(formattedLine[i - 1]))
{
formattedLine.erase(i);
--commentAdjust;
}
appendSpacePad();
}
if (objCColonPadMode == COLON_PAD_NONE
|| objCColonPadMode == COLON_PAD_BEFORE
|| nextChar == ')')
{
// remove spaces after
int nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextText == (int)string::npos)
nextText = currentLine.length();
int spaces = nextText - charNum - 1;
if (spaces > 0)
{
// do not use goForward here
currentLine.erase(charNum + 1, spaces);
spacePadNum -= spaces;
}
}
else
{
// pad space after
int nextText = currentLine.find_first_not_of(" \t", charNum + 1);
if (nextText == (int)string::npos)
nextText = currentLine.length();
int spaces = nextText - charNum - 1;
if (spaces == 0)
{
currentLine.insert(charNum + 1, 1, ' ');
spacePadNum += 1;
}
else if (spaces > 1)
{
// do not use goForward here
currentLine.erase(charNum + 1, spaces - 1);
spacePadNum -= spaces - 1;
}
}
spacePadNum += commentAdjust;
}
// Remove the leading '*' from a comment line and indent to the next tab.
void ASFormatter::stripCommentPrefix()
{
int firstChar = formattedLine.find_first_not_of(" \t");
if (firstChar < 0)
return;
if (isInCommentStartLine)
{
// comment opener must begin the line
if (formattedLine.compare(firstChar, 2, "/*") != 0)
return;
int commentOpener = firstChar;
// ignore single line comments
int commentEnd = formattedLine.find("*/", firstChar + 2);
if (commentEnd != -1)
return;
// first char after the comment opener must be at least one indent
int followingText = formattedLine.find_first_not_of(" \t", commentOpener + 2);
if (followingText < 0)
return;
if (formattedLine[followingText] == '*' || formattedLine[followingText] == '!')
followingText = formattedLine.find_first_not_of(" \t", followingText + 1);
if (followingText < 0)
return;
if (formattedLine[followingText] == '*')
return;
int indentLen = getIndentLength();
int followingTextIndent = followingText - commentOpener;
if (followingTextIndent < indentLen)
{
string stringToInsert(indentLen - followingTextIndent, ' ');
formattedLine.insert(followingText, stringToInsert);
}
return;
}
// comment body including the closer
if (formattedLine[firstChar] == '*')
{
if (formattedLine.compare(firstChar, 2, "*/") == 0)
{
// line starts with an end comment
formattedLine = "*/";
}
else
{
// build a new line with one indent
int secondChar = formattedLine.find_first_not_of(" \t", firstChar + 1);
if (secondChar < 0)
{
adjustChecksumIn(-'*');
formattedLine.erase();
return;
}
if (formattedLine[secondChar] == '*')
return;
// replace the leading '*'
int indentLen = getIndentLength();
adjustChecksumIn(-'*');
// second char must be at least one indent
if (formattedLine.substr(0, secondChar).find('\t') != string::npos)
{
formattedLine.erase(firstChar, 1);
}
else
{
int spacesToInsert = 0;
if (secondChar >= indentLen)
spacesToInsert = secondChar;
else
spacesToInsert = indentLen;
formattedLine = string(spacesToInsert, ' ') + formattedLine.substr(secondChar);
}
// remove a trailing '*'
int lastChar = formattedLine.find_last_not_of(" \t");
if (lastChar > -1 && formattedLine[lastChar] == '*')
{
adjustChecksumIn(-'*');
formattedLine[lastChar] = ' ';
}
}
}
else
{
// first char not a '*'
// first char must be at least one indent
if (formattedLine.substr(0, firstChar).find('\t') == string::npos)
{
int indentLen = getIndentLength();
if (firstChar < indentLen)
{
string stringToInsert(indentLen, ' ');
formattedLine = stringToInsert + formattedLine.substr(firstChar);
}
}
}
}
} // end namespace astyle