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