mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-28 00:17:30 -05:00
instead of adding an extra CMakeLists in .ci/travis/code_layout to build API doc, astyle and run tests (indentation, spelling, sip, doc coverage), the top CMakeLists has been adapted to allow not building core libraries and possibly just the static code layout * astyle has been moved from /src/astyle to /lib/astyle (I would propose to move all external libraries, and possibly add git submodules)
7715 lines
216 KiB
C++
Executable File
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
|