From 8b731e941f766c92af4955328e818e4f8053de2a Mon Sep 17 00:00:00 2001 From: Fox Mulder Date: Wed, 26 Nov 2025 02:01:23 +0300 Subject: [PATCH] astyle port draft. TODO: utime, directory_iterator, is_absolute --- Makefile | 32 + astyle/AStyle/src/ASBeautifier.cpp | 4443 ++++++++++++++ astyle/AStyle/src/ASEnhancer.cpp | 775 +++ astyle/AStyle/src/ASFormatter.cpp | 8738 ++++++++++++++++++++++++++++ astyle/AStyle/src/ASLocalizer.cpp | 1163 ++++ astyle/AStyle/src/ASLocalizer.h | 170 + astyle/AStyle/src/ASResource.cpp | 828 +++ astyle/AStyle/src/astyle.h | 1178 ++++ astyle/AStyle/src/astyle_main.cpp | 4116 +++++++++++++ astyle/AStyle/src/astyle_main.h | 445 ++ filesystem.h | 184 + main.cpp | 55 + 12 files changed, 22127 insertions(+) create mode 100644 Makefile create mode 100644 astyle/AStyle/src/ASBeautifier.cpp create mode 100644 astyle/AStyle/src/ASEnhancer.cpp create mode 100644 astyle/AStyle/src/ASFormatter.cpp create mode 100644 astyle/AStyle/src/ASLocalizer.cpp create mode 100644 astyle/AStyle/src/ASLocalizer.h create mode 100644 astyle/AStyle/src/ASResource.cpp create mode 100644 astyle/AStyle/src/astyle.h create mode 100644 astyle/AStyle/src/astyle_main.cpp create mode 100644 astyle/AStyle/src/astyle_main.h create mode 100644 filesystem.h create mode 100644 main.cpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8e314e2 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +CXX:=kos32-g++ +LD:=kos32-ld +SDK_DIR:=/home/pt/dev/kolibri/latest/contrib/sdk +CXXFLAGS:=-c -fno-ident -O2 -std=c++17 -fomit-frame-pointer -U__WIN32__ -U_Win32 -U_WIN32 -U__MINGW32__ -UWIN32 -Wno-pointer-arith +#-c -m32 -march=native -mtune=generic -std=c99 -O2 -fomit-frame-pointer -U__WIN32__ -U_Win32 -U_WIN32 -U__MINGW32__ -UWIN32 +#LDFLAGS:=-static -nostdlib --subsystem native --image-base 0 -T $(SDK_DIR)/sources/newlib/static.lds -Map=$app_name.map +INCLUDE:=-I $(SDK_DIR)/sources/newlib/libc/include -I $(SDK_DIR)/sources/libstdc++-v3/include -I /usr/local/kos32/lib/gcc/mingw32/5.4.0/include/c++/experimental +LDFLAGS:=-static -S -Tapp-dynamic.lds --image-base 0 +LIBPATH:=-L $(SDK_DIR)/lib -L /usr/local/kos32/lib + +all: compile link make_iso copy_iso + +compile: + $(CXX) $(CXXFLAGS) $(INCLUDE) -o main.o main.cpp +clean: + rm -f main.o main iso app.iso +link: + $(LD) $(LDFLAGS) $(LIBPATH) main.o -o main -lstdc++ -lsupc++ -lgcc -lc.dll +#-lc -lgcc -lstdc++ -lsupc++ + kos32-strip -s main -o main + kos32-objcopy main -O binary +copy: + cp main /mnt/e/ +make_iso: + rm -rf iso + mkdir -p iso + cp main iso/ + mkisofs -appid KolibriTestApp -volid KolibriTestApp -XA -sysid KOLIBRI -publisher pvoid -volset-size 1 -volset-seqno 1 -iso-level 3 -J -o app.iso iso/main + rm -rf iso +copy_iso: + rm -f /mnt/e/app.iso + cp app.iso /mnt/e/ diff --git a/astyle/AStyle/src/ASBeautifier.cpp b/astyle/AStyle/src/ASBeautifier.cpp new file mode 100644 index 0000000..8443914 --- /dev/null +++ b/astyle/AStyle/src/ASBeautifier.cpp @@ -0,0 +1,4443 @@ +// ASBeautifier.cpp +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +//----------------------------------------------------------------------------- +// headers +//----------------------------------------------------------------------------- + +#include "astyle.h" + +#include +#include + +//----------------------------------------------------------------------------- +// astyle namespace +//----------------------------------------------------------------------------- + +namespace astyle { + +// +// this must be global +static int g_preprocessorCppExternCBrace; + +//----------------------------------------------------------------------------- +// ASBeautifier class +//----------------------------------------------------------------------------- + +/** + * ASBeautifier's constructor + * This constructor is called only once for each source file. + * The cloned ASBeautifier objects are created with the copy constructor. + */ +ASBeautifier::ASBeautifier() +{ + waitingBeautifierStack = nullptr; + activeBeautifierStack = nullptr; + waitingBeautifierStackLengthStack = nullptr; + activeBeautifierStackLengthStack = nullptr; + + headerStack = nullptr; + tempStacks = nullptr; + parenDepthStack = nullptr; + blockStatementStack = nullptr; + parenStatementStack = nullptr; + braceBlockStateStack = nullptr; + continuationIndentStack = nullptr; + continuationIndentStackSizeStack = nullptr; + parenIndentStack = nullptr; + preprocIndentStack = nullptr; + lambdaDepthStack = nullptr; + sourceIterator = nullptr; + isModeManuallySet = false; + shouldForceTabIndentation = false; + setSpaceIndentation(4); + setContinuationIndentation(1); + setMinConditionalIndentOption(MINCOND_TWO); + setMaxContinuationIndentLength(40); + classInitializerIndents = 1; + tabLength = 0; + setClassIndent(false); + setModifierIndent(false); + setSwitchIndent(false); + setCaseIndent(false); + setSqueezeWhitespace(false); + setPreserveWhitespace(false); + setLambdaIndentation(false); + setBlockIndent(false); + setBraceIndent(false); + setBraceIndentVtk(false); + setNamespaceIndent(false); + setAfterParenIndent(false); + setLabelIndent(false); + setEmptyLineFill(false); + setCStyle(); + setPreprocDefineIndent(false); + setPreprocConditionalIndent(false); + setAlignMethodColon(false); + isInAssignment = isInInitializerList = isInMultiLineString = false; + + // initialize ASBeautifier member vectors + beautifierFileType = INVALID_TYPE; // reset to an invalid type + headers = new std::vector; + nonParenHeaders = new std::vector; + assignmentOperators = new std::vector; + nonAssignmentOperators = new std::vector; + preBlockStatements = new std::vector; + preCommandHeaders = new std::vector; + indentableHeaders = new std::vector; + lambdaEndIndent = lambdaStartIndent = 0; +} + +/** + * ASBeautifier's copy constructor + * Copy the vector objects to vectors in the new ASBeautifier + * object so the new object can be destroyed without deleting + * the vector objects in the copied vector. + * This is the reason a copy constructor is needed. + * + * Must explicitly call the base class copy constructor. + */ +ASBeautifier::ASBeautifier(const ASBeautifier& other) : ASBase(other) +{ + // these don't need to copy the stack + waitingBeautifierStack = nullptr; + activeBeautifierStack = nullptr; + waitingBeautifierStackLengthStack = nullptr; + activeBeautifierStackLengthStack = nullptr; + + // vector '=' operator performs a DEEP copy of all elements in the vector + + headerStack = new std::vector; + *headerStack = *other.headerStack; + + tempStacks = copyTempStacks(other); + + parenDepthStack = new std::vector; + *parenDepthStack = *other.parenDepthStack; + + blockStatementStack = new std::vector; + *blockStatementStack = *other.blockStatementStack; + + parenStatementStack = new std::vector; + *parenStatementStack = *other.parenStatementStack; + + braceBlockStateStack = new std::vector; + *braceBlockStateStack = *other.braceBlockStateStack; + + continuationIndentStack = new std::vector; + *continuationIndentStack = *other.continuationIndentStack; + + continuationIndentStackSizeStack = new std::vector; + *continuationIndentStackSizeStack = *other.continuationIndentStackSizeStack; + + parenIndentStack = new std::vector; + *parenIndentStack = *other.parenIndentStack; + + preprocIndentStack = new std::vector >; + *preprocIndentStack = *other.preprocIndentStack; + + lambdaDepthStack = new std::vector; + *lambdaDepthStack = *other.lambdaDepthStack; + + // Copy the pointers to std::vectors. + // This is ok because the original ASBeautifier object + // is not deleted until end of job. + beautifierFileType = other.beautifierFileType; + headers = other.headers; + nonParenHeaders = other.nonParenHeaders; + assignmentOperators = other.assignmentOperators; + nonAssignmentOperators = other.nonAssignmentOperators; + preBlockStatements = other.preBlockStatements; + preCommandHeaders = other.preCommandHeaders; + indentableHeaders = other.indentableHeaders; + + // protected variables + // variables set by ASFormatter + // must also be updated in activeBeautifierStack + inLineNumber = other.inLineNumber; + runInIndentContinuation = other.runInIndentContinuation; + nonInStatementBrace = other.nonInStatementBrace; + objCColonAlignSubsequent = other.objCColonAlignSubsequent; + lineCommentNoBeautify = other.lineCommentNoBeautify; + isElseHeaderIndent = other.isElseHeaderIndent; + isCaseHeaderCommentIndent = other.isCaseHeaderCommentIndent; + isNonInStatementArray = other.isNonInStatementArray; + isSharpAccessor = other.isSharpAccessor; + isSharpDelegate = other.isSharpDelegate; + isInExternC = other.isInExternC; + isInBeautifySQL = other.isInBeautifySQL; + isInIndentableStruct = other.isInIndentableStruct; + isInIndentablePreproc = other.isInIndentablePreproc; + + // private variables + sourceIterator = other.sourceIterator; + currentHeader = other.currentHeader; + previousLastLineHeader = other.previousLastLineHeader; + probationHeader = other.probationHeader; + lastLineHeader = other.lastLineHeader; + indentString = other.indentString; + verbatimDelimiter = other.verbatimDelimiter; + isInQuote = other.isInQuote; + isInVerbatimQuote = other.isInVerbatimQuote; + haveLineContinuationChar = other.haveLineContinuationChar; + isInAsm = other.isInAsm; + isInAsmOneLine = other.isInAsmOneLine; + isInAsmBlock = other.isInAsmBlock; + isInComment = other.isInComment; + isInPreprocessorComment = other.isInPreprocessorComment; + isInRunInComment = other.isInRunInComment; + isInCase = other.isInCase; + isInQuestion = other.isInQuestion; + isContinuation = other.isContinuation; + isInHeader = other.isInHeader; + isInTemplate = other.isInTemplate; + isInDefine = other.isInDefine; + isInDefineDefinition = other.isInDefineDefinition; + classIndent = other.classIndent; + isIndentModeOff = other.isIndentModeOff; + isInClassHeader = other.isInClassHeader; + isInClassHeaderTab = other.isInClassHeaderTab; + isInClassInitializer = other.isInClassInitializer; + isInClass = other.isInClass; + isInObjCMethodDefinition = other.isInObjCMethodDefinition; + isInObjCMethodCall = other.isInObjCMethodCall; + isInObjCMethodCallFirst = other.isInObjCMethodCallFirst; + isImmediatelyPostObjCMethodDefinition = other.isImmediatelyPostObjCMethodDefinition; + isImmediatelyPostObjCMethodCall = other.isImmediatelyPostObjCMethodCall; + isInIndentablePreprocBlock = other.isInIndentablePreprocBlock; + isInObjCInterface = other.isInObjCInterface; + isInEnum = other.isInEnum; + isInEnumTypeID = other.isInEnumTypeID; + isInStruct = other.isInStruct; + isInLet = other.isInLet; + isInTrailingReturnType = other.isInTrailingReturnType; + modifierIndent = other.modifierIndent; + switchIndent = other.switchIndent; + caseIndent = other.caseIndent; + squeezeWhitespace = other.squeezeWhitespace; + preserveWhitespace = other.preserveWhitespace; + + attemptLambdaIndentation = other.attemptLambdaIndentation; + isInAssignment = other.isInAssignment; + isInInitializerList = other.isInInitializerList; + isInMultiLineString = other.isInMultiLineString; + + namespaceIndent = other.namespaceIndent; + braceIndent = other.braceIndent; + braceIndentVtk = other.braceIndentVtk; + blockIndent = other.blockIndent; + shouldIndentAfterParen = other.shouldIndentAfterParen; + labelIndent = other.labelIndent; + isInConditional = other.isInConditional; + isModeManuallySet = other.isModeManuallySet; + shouldForceTabIndentation = other.shouldForceTabIndentation; + emptyLineFill = other.emptyLineFill; + lineOpensWithLineComment = other.lineOpensWithLineComment; + lineOpensWithComment = other.lineOpensWithComment; + lineStartsInComment = other.lineStartsInComment; + backslashEndsPrevLine = other.backslashEndsPrevLine; + quoteContinuationIndent = other.quoteContinuationIndent; + + blockCommentNoIndent = other.blockCommentNoIndent; + blockCommentNoBeautify = other.blockCommentNoBeautify; + previousLineProbationTab = other.previousLineProbationTab; + lineBeginsWithOpenBrace = other.lineBeginsWithOpenBrace; + lineBeginsWithCloseBrace = other.lineBeginsWithCloseBrace; + lineBeginsWithComma = other.lineBeginsWithComma; + lineIsCommentOnly = other.lineIsCommentOnly; + lineIsLineCommentOnly = other.lineIsLineCommentOnly; + shouldIndentBracedLine = other.shouldIndentBracedLine; + isInSwitch = other.isInSwitch; + foundPreCommandHeader = other.foundPreCommandHeader; + foundPreCommandMacro = other.foundPreCommandMacro; + shouldAlignMethodColon = other.shouldAlignMethodColon; + shouldIndentPreprocDefine = other.shouldIndentPreprocDefine; + shouldIndentPreprocConditional = other.shouldIndentPreprocConditional; + lambdaIndicator = other.lambdaIndicator; + lambdaDepth = other.lambdaDepth; + lastLambdaDepth = other.lastLambdaDepth; + + + indentCount = other.indentCount; + spaceIndentCount = other.spaceIndentCount; + spaceIndentObjCMethodAlignment = other.spaceIndentObjCMethodAlignment; + bracePosObjCMethodAlignment = other.bracePosObjCMethodAlignment; + colonIndentObjCMethodAlignment = other.colonIndentObjCMethodAlignment; + lineOpeningBlocksNum = other.lineOpeningBlocksNum; + lineClosingBlocksNum = other.lineClosingBlocksNum; + fileType = other.fileType; + minConditionalOption = other.minConditionalOption; + minConditionalIndent = other.minConditionalIndent; + parenDepth = other.parenDepth; + indentLength = other.indentLength; + tabLength = other.tabLength; + continuationIndent = other.continuationIndent; + blockTabCount = other.blockTabCount; + maxContinuationIndent = other.maxContinuationIndent; + classInitializerIndents = other.classInitializerIndents; + templateDepth = other.templateDepth; + squareBracketCount = other.squareBracketCount; + prevFinalLineSpaceIndentCount = other.prevFinalLineSpaceIndentCount; + prevFinalLineIndentCount = other.prevFinalLineIndentCount; + defineIndentCount = other.defineIndentCount; + preprocBlockIndent = other.preprocBlockIndent; + quoteChar = other.quoteChar; + prevNonSpaceCh = other.prevNonSpaceCh; + currentNonSpaceCh = other.currentNonSpaceCh; + currentNonLegalCh = other.currentNonLegalCh; + prevNonLegalCh = other.prevNonLegalCh; + bracesNestingLevel = other.bracesNestingLevel; + bracesNestingLevelOfStruct = other.bracesNestingLevelOfStruct; + lambdaEndIndent = other.lambdaEndIndent; + lambdaStartIndent = other.lambdaStartIndent; +} + +/** + * ASBeautifier's destructor + */ +ASBeautifier::~ASBeautifier() +{ + deleteBeautifierContainer(waitingBeautifierStack); + deleteBeautifierContainer(activeBeautifierStack); + deleteContainer(waitingBeautifierStackLengthStack); + deleteContainer(activeBeautifierStackLengthStack); + deleteContainer(headerStack); + deleteTempStacksContainer(tempStacks); + deleteContainer(parenDepthStack); + deleteContainer(blockStatementStack); + deleteContainer(parenStatementStack); + deleteContainer(braceBlockStateStack); + deleteContainer(continuationIndentStack); + deleteContainer(continuationIndentStackSizeStack); + deleteContainer(parenIndentStack); + deleteContainer(preprocIndentStack); + deleteContainer(lambdaDepthStack); +} + +/** + * initialize the ASBeautifier. + * + * This init() should be called every time a ABeautifier object is to start + * beautifying a NEW source file. + * It is called only when a new ASFormatter object is created. + * init() receives a pointer to a ASSourceIterator object that will be + * used to iterate through the source code. + * + * @param iter a pointer to the ASSourceIterator or ASStreamIterator object. + */ +void ASBeautifier::init(ASSourceIterator* iter) +{ + sourceIterator = iter; + initVectors(); + ASBase::init(getFileType()); + g_preprocessorCppExternCBrace = 0; + + initContainer(waitingBeautifierStack, new std::vector); + initContainer(activeBeautifierStack, new std::vector); + + initContainer(waitingBeautifierStackLengthStack, new std::vector); + initContainer(activeBeautifierStackLengthStack, new std::vector); + + initContainer(headerStack, new std::vector); + + initTempStacksContainer(tempStacks, new std::vector*>); + tempStacks->emplace_back(new std::vector); + + initContainer(parenDepthStack, new std::vector); + initContainer(blockStatementStack, new std::vector); + initContainer(parenStatementStack, new std::vector); + initContainer(braceBlockStateStack, new std::vector); + // do not use emplace_back on std::vector until supported by macOS + braceBlockStateStack->push_back(true); + initContainer(continuationIndentStack, new std::vector); + initContainer(continuationIndentStackSizeStack, new std::vector); + continuationIndentStackSizeStack->emplace_back(0); + initContainer(parenIndentStack, new std::vector); + initContainer(preprocIndentStack, new std::vector >); + initContainer(lambdaDepthStack, new std::vector); + + previousLastLineHeader = nullptr; + currentHeader = nullptr; + + isInQuote = false; + isInVerbatimQuote = false; + haveLineContinuationChar = false; + isInAsm = false; + isInAsmOneLine = false; + isInAsmBlock = false; + isInComment = false; + isInPreprocessorComment = false; + isInRunInComment = false; + isContinuation = false; + isInCase = false; + isInQuestion = false; + isIndentModeOff = false; + isInClassHeader = false; + isInClassHeaderTab = false; + isInClassInitializer = false; + isInClass = false; + isInObjCMethodDefinition = false; + isInObjCMethodCall = false; + isInObjCMethodCallFirst = false; + isImmediatelyPostObjCMethodDefinition = false; + isImmediatelyPostObjCMethodCall = false; + isInIndentablePreprocBlock = false; + isInObjCInterface = false; + isInEnum = false; + isInEnumTypeID = false; + isInStruct = false; + isInLet = false; + isInHeader = false; + isInTemplate = false; + isInConditional = false; + isInTrailingReturnType = false; + lambdaIndicator = false; + lambdaDepth = 0; + lastLambdaDepth = 0; + + indentCount = 0; + spaceIndentCount = 0; + spaceIndentObjCMethodAlignment = 0; + bracePosObjCMethodAlignment = 0; + colonIndentObjCMethodAlignment = 0; + lineOpeningBlocksNum = 0; + lineClosingBlocksNum = 0; + templateDepth = 0; + squareBracketCount = 0; + parenDepth = 0; + blockTabCount = 0; + prevFinalLineSpaceIndentCount = 0; + prevFinalLineIndentCount = 0; + defineIndentCount = 0; + preprocBlockIndent = 0; + prevNonSpaceCh = '{'; + currentNonSpaceCh = '{'; + prevNonLegalCh = '{'; + currentNonLegalCh = '{'; + quoteChar = ' '; + probationHeader = nullptr; + lastLineHeader = nullptr; + backslashEndsPrevLine = false; + quoteContinuationIndent = 0; + lineOpensWithLineComment = false; + lineOpensWithComment = false; + lineStartsInComment = false; + isInDefine = false; + isInDefineDefinition = false; + lineCommentNoBeautify = false; + isElseHeaderIndent = false; + isCaseHeaderCommentIndent = false; + blockCommentNoIndent = false; + blockCommentNoBeautify = false; + previousLineProbationTab = false; + lineBeginsWithOpenBrace = false; + lineBeginsWithCloseBrace = false; + lineBeginsWithComma = false; + lineIsCommentOnly = false; + lineIsLineCommentOnly = false; + shouldIndentBracedLine = true; + isInSwitch = false; + foundPreCommandHeader = false; + foundPreCommandMacro = false; + + isNonInStatementArray = false; + isSharpAccessor = false; + isSharpDelegate = false; + isInExternC = false; + isInBeautifySQL = false; + isInIndentableStruct = false; + isInIndentablePreproc = false; + + inLineNumber = 0; + runInIndentContinuation = 0; + nonInStatementBrace = 0; + objCColonAlignSubsequent = 0; + bracesNestingLevel = 0; + bracesNestingLevelOfStruct = 0; + lambdaEndIndent = lambdaStartIndent = 0; +} + +/* + * initialize the vectors + */ +void ASBeautifier::initVectors() +{ + if (fileType == beautifierFileType) // don't build unless necessary + return; + + beautifierFileType = fileType; + + headers->clear(); + nonParenHeaders->clear(); + assignmentOperators->clear(); + nonAssignmentOperators->clear(); + preBlockStatements->clear(); + preCommandHeaders->clear(); + indentableHeaders->clear(); + + ASResource::buildHeaders(headers, fileType, true); + ASResource::buildNonParenHeaders(nonParenHeaders, fileType, true); + ASResource::buildAssignmentOperators(assignmentOperators); + ASResource::buildNonAssignmentOperators(nonAssignmentOperators, fileType); + ASResource::buildPreBlockStatements(preBlockStatements, fileType); + ASResource::buildPreCommandHeaders(preCommandHeaders, fileType); + ASResource::buildIndentableHeaders(indentableHeaders); +} + +/** + * beautify a line of source code. + * every line of source code in a source code file should be sent + * one after the other to the beautify method. + * + * @return the indented line. + * @param originalLine the original unindented line. + */ +std::string ASBeautifier::beautify(const std::string& originalLine) +{ + std::string line; + bool isInQuoteContinuation = isInVerbatimQuote || haveLineContinuationChar; + + currentHeader = nullptr; + lastLineHeader = nullptr; + blockCommentNoBeautify = blockCommentNoIndent; + isInClass = false; + isInSwitch = false; + lineBeginsWithOpenBrace = false; + lineBeginsWithCloseBrace = false; + lineBeginsWithComma = false; + lineIsCommentOnly = false; + lineIsLineCommentOnly = false; + shouldIndentBracedLine = true; + isInAsmOneLine = false; + lineOpensWithLineComment = false; + lineOpensWithComment = false; + lineStartsInComment = isInComment; + previousLineProbationTab = false; + lineOpeningBlocksNum = 0; + lineClosingBlocksNum = 0; + if (isImmediatelyPostObjCMethodDefinition) + clearObjCMethodDefinitionAlignment(); + if (isImmediatelyPostObjCMethodCall) + { + isImmediatelyPostObjCMethodCall = false; + isInObjCMethodCall = false; + objCColonAlignSubsequent = 0; + } + + // handle and remove white spaces around the line: + // If not in comment, first find out size of white space before line, + // so that possible comments starting in the line continue in + // relation to the preliminary white-space. + if (isInQuoteContinuation) + { + // trim a single space added by ASFormatter, otherwise leave it alone + if (!(originalLine.length() == 1 && originalLine[0] == ' ')) + line = originalLine; + } + else if (isInComment || isInBeautifySQL) + { + // trim the end of comment and SQL lines + line = originalLine; + size_t trimEnd = line.find_last_not_of(" \t"); + if (trimEnd == std::string::npos) + trimEnd = 0; + else + trimEnd++; + if (trimEnd < line.length()) + line.erase(trimEnd); + // does a brace open the line + size_t firstChar = line.find_first_not_of(" \t"); + if (firstChar != std::string::npos) + { + if (line[firstChar] == '{') + lineBeginsWithOpenBrace = true; + else if (line[firstChar] == '}') + lineBeginsWithCloseBrace = true; + else if (line[firstChar] == ',') + lineBeginsWithComma = true; + } + } + else + { + line = trim(originalLine); + if (!line.empty()) + { + if (line[0] == '{') + lineBeginsWithOpenBrace = true; + else if (line[0] == '}') + lineBeginsWithCloseBrace = true; + else if (line[0] == ',') + lineBeginsWithComma = true; + else if (line.compare(0, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0) + lineIsLineCommentOnly = true; + else if (line.compare(0, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0) + { + if (line.find(ASResource::AS_CLOSE_COMMENT, ASResource::AS_CLOSE_COMMENT.length()) != std::string::npos) + lineIsCommentOnly = true; + } + else if (line.compare(0, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0) + { + if (line.find(ASResource::AS_GSC_CLOSE_COMMENT, ASResource::AS_GSC_CLOSE_COMMENT.length()) != std::string::npos) + lineIsCommentOnly = true; + } + } + + isInRunInComment = false; + size_t j = line.find_first_not_of(" \t{"); + if ( j != std::string::npos + && line.compare(j, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0) + lineOpensWithLineComment = true; + if (j != std::string::npos + && (line.compare(j, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(j, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0)) + { + lineOpensWithComment = true; + size_t k = line.find_first_not_of(" \t"); + if (k != std::string::npos && line.compare(k, 1, "{") == 0) + isInRunInComment = true; + } + } + + // When indent is OFF the lines must still be processed by ASBeautifier. + // Otherwise the lines immediately following may not be indented correctly. + if ((lineIsLineCommentOnly || lineIsCommentOnly) + && line.find("*INDENT-OFF*", 0) != std::string::npos) + isIndentModeOff = true; + + // Handle git conflict markers - treat them as single line comments with no formatting + if (isGitConflictMarker(line)) + return originalLine; + + if (line.empty()) + { + if (backslashEndsPrevLine) + { + backslashEndsPrevLine = false; + // check if this line ends a multi-line #define + // if so, remove the #define's cloned beautifier from the active + // beautifier stack and delete it. + if (isInDefineDefinition && !isInDefine) + { + isInDefineDefinition = false; + if (!activeBeautifierStack->empty()) + { + ASBeautifier* defineBeautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + delete defineBeautifier; + } + } + } + if (emptyLineFill && !isInQuoteContinuation) + { + if (isInIndentablePreprocBlock) + return preLineWS(preprocBlockIndent, 0); + + if (!headerStack->empty() || isInEnum || isInStruct) + return preLineWS(prevFinalLineIndentCount, prevFinalLineSpaceIndentCount); + // must fall thru here + } + else + return line; + } + + if (isCStyle() + && !isInComment + && !parenDepth + //&& line.find('(', 0) == std::string::npos + && line.find('#', 0) == std::string::npos + && line.find("//", 0) == std::string::npos + && line.find("/*", 0) == std::string::npos + && (line[line.length() - 1] == '"' || line[line.length() - 1] == '<' || quoteContinuationIndent)) + { + quoteContinuationIndent = std::max(quoteContinuationIndent, line.find('"', 0) ); + } + + // handle preprocessor commands + if (isInIndentablePreprocBlock + && !line.empty() + && line[0] != '#') + { + if (isIndentModeOff) + return originalLine; + + if (isInClassHeaderTab || isInClassInitializer) + { + // parsing is turned off in ASFormatter by indent-off + // the originalLine will probably never be returned here + return preLineWS(prevFinalLineIndentCount, prevFinalLineSpaceIndentCount) + line; + } + return preLineWS(preprocBlockIndent, 0) + line; + } + + if (!isInComment + && !isInQuoteContinuation + && !line.empty() + && ((line[0] == '#' && !isIndentedPreprocessor(line, 0)) + || backslashEndsPrevLine)) + { + if (line[0] == '#' && !isInDefine) + { + std::string preproc = extractPreprocessorStatement(line); + processPreprocessor(preproc, line); + if (isInIndentablePreprocBlock || isInIndentablePreproc) + { + std::string indentedLine; + if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef + { + indentedLine = preLineWS(preprocBlockIndent, 0) + line; + preprocBlockIndent += 1; + isInIndentablePreprocBlock = true; + } + else if (preproc == "else" || preproc == "elif") + { + indentedLine = preLineWS(preprocBlockIndent - 1, 0) + line; + } + else if (preproc == "endif") + { + preprocBlockIndent -= 1; + indentedLine = preLineWS(preprocBlockIndent, 0) + line; + if (preprocBlockIndent == 0) + isInIndentablePreprocBlock = false; + } + else + indentedLine = preLineWS(preprocBlockIndent, 0) + line; + + if (isIndentModeOff) + return originalLine; + else + return indentedLine; + } + if (shouldIndentPreprocConditional && !preproc.empty()) + { + + if (isIndentModeOff) + return originalLine; + + std::string indentedLine; + if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef + { + std::pair entry; // indentCount, spaceIndentCount + if (!isInDefine && activeBeautifierStack != nullptr && !activeBeautifierStack->empty()) + entry = activeBeautifierStack->back()->computePreprocessorIndent(); + else + entry = computePreprocessorIndent(); + preprocIndentStack->emplace_back(entry); + indentedLine = preLineWS(preprocIndentStack->back().first, + preprocIndentStack->back().second) + line; + return indentedLine; + } + if (preproc == "else" || preproc == "elif") + { + if (!preprocIndentStack->empty()) // if no entry don't indent + { + indentedLine = preLineWS(preprocIndentStack->back().first, + preprocIndentStack->back().second) + line; + return indentedLine; + } + } + else if (preproc == "endif") + { + if (!preprocIndentStack->empty()) // if no entry don't indent + { + indentedLine = preLineWS(preprocIndentStack->back().first, + preprocIndentStack->back().second) + line; + preprocIndentStack->pop_back(); + return indentedLine; + } + } + } + } + + // check if the last char is a backslash + if (!line.empty()) + backslashEndsPrevLine = (line[line.length() - 1] == '\\'); + + // comments within the definition line can be continued without the backslash + if (isInPreprocessorUnterminatedComment(line)) + backslashEndsPrevLine = true; + + // check if this line ends a multi-line #define + // if so, use the #define's cloned beautifier for the line's indentation + // and then remove it from the active beautifier stack and delete it. + if (!backslashEndsPrevLine && isInDefineDefinition && !isInDefine) + { + isInDefineDefinition = false; + // this could happen with invalid input + if (activeBeautifierStack->empty() || isIndentModeOff) + return originalLine; + ASBeautifier* defineBeautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + + std::string indentedLine = defineBeautifier->beautify(line); + delete defineBeautifier; + return indentedLine; + } + + // unless this is a multi-line #define, return this precompiler line as is. + if (!isInDefine && !isInDefineDefinition) + return originalLine; + } + + // if there exists any worker beautifier in the activeBeautifierStack, + // then use it instead of me to indent the current line. + // variables set by ASFormatter must be updated. + if (!isInDefine && activeBeautifierStack != nullptr && !activeBeautifierStack->empty()) + { + activeBeautifierStack->back()->inLineNumber = inLineNumber; + activeBeautifierStack->back()->runInIndentContinuation = runInIndentContinuation; + activeBeautifierStack->back()->nonInStatementBrace = nonInStatementBrace; + activeBeautifierStack->back()->objCColonAlignSubsequent = objCColonAlignSubsequent; + activeBeautifierStack->back()->lineCommentNoBeautify = lineCommentNoBeautify; + activeBeautifierStack->back()->isElseHeaderIndent = isElseHeaderIndent; + activeBeautifierStack->back()->isCaseHeaderCommentIndent = isCaseHeaderCommentIndent; + activeBeautifierStack->back()->isNonInStatementArray = isNonInStatementArray; + activeBeautifierStack->back()->isSharpAccessor = isSharpAccessor; + activeBeautifierStack->back()->isSharpDelegate = isSharpDelegate; + activeBeautifierStack->back()->isInExternC = isInExternC; + activeBeautifierStack->back()->isInBeautifySQL = isInBeautifySQL; + activeBeautifierStack->back()->isInIndentableStruct = isInIndentableStruct; + activeBeautifierStack->back()->isInIndentablePreproc = isInIndentablePreproc; + // must return originalLine not the trimmed line + return activeBeautifierStack->back()->beautify(originalLine); + } + + // Flag an indented header in case this line is a one-line block. + // The header in the header stack will be deleted by a one-line block. + bool isInExtraHeaderIndent = false; + if (!headerStack->empty() + && lineBeginsWithOpenBrace + && (headerStack->back() != &ASResource::AS_OPEN_BRACE + || probationHeader != nullptr)) + isInExtraHeaderIndent = true; + + size_t iPrelim = headerStack->size(); + + // calculate preliminary indentation based on headerStack and data from past lines + computePreliminaryIndentation(); + + // parse characters in the current line. + parseCurrentLine(line); + + for (auto it = squeezeWSStack.rbegin(); it != squeezeWSStack.rend(); ++it) + { + line.erase(it->first, it->second); + } + squeezeWSStack.clear(); + + // handle special cases of indentation + adjustParsedLineIndentation(iPrelim, isInExtraHeaderIndent); + + if (isInObjCMethodDefinition) + adjustObjCMethodDefinitionIndentation(line); + + if (isInObjCMethodCall) + adjustObjCMethodCallIndentation(line); + + if (isInDefine) + { + if (!line.empty() && line[0] == '#') + { + // the 'define' does not have to be attached to the '#' + std::string preproc = trim(line.substr(1)); + if (preproc.compare(0, 6, "define") == 0) + { + if (!continuationIndentStack->empty() + && continuationIndentStack->back() > 0) + { + defineIndentCount = indentCount; + } + else + { + defineIndentCount = indentCount - 1; + --indentCount; + // logIndent(1); + } + } + } + + indentCount -= defineIndentCount; + // logIndent(2); + } + + if (indentCount < 0){ + indentCount = 0; + // logIndent(3); + } + + if (lineCommentNoBeautify || blockCommentNoBeautify || isInQuoteContinuation){ + indentCount = spaceIndentCount = 0; + // logIndent(4); + } + + // finally, insert indentations into beginning of line + + std::string indentedLine = isIndentModeOff ? originalLine : preLineWS(indentCount, spaceIndentCount) + line; + prevFinalLineSpaceIndentCount = spaceIndentCount; + prevFinalLineIndentCount = indentCount; + + if (lastLineHeader != nullptr) + previousLastLineHeader = lastLineHeader; + + if ((lineIsLineCommentOnly || lineIsCommentOnly) + && line.find("*INDENT-ON*", 0) != std::string::npos) + isIndentModeOff = false; + + return indentedLine; +} + +/** + * set indentation style to C/C++. + */ +void ASBeautifier::setCStyle() +{ + fileType = C_TYPE; +} + +/** + * set indentation style to Java. + */ +void ASBeautifier::setJavaStyle() +{ + fileType = JAVA_TYPE; +} + +/** + * set indentation style to JavaScript. + */ +void ASBeautifier::setJSStyle() +{ + fileType = JS_TYPE; +} + +/** + * set indentation style to JavaScript. + */ +void ASBeautifier::setObjCStyle() +{ + fileType = OBJC_TYPE; +} + +/** + * set indentation style to C#. + */ +void ASBeautifier::setSharpStyle() +{ + fileType = SHARP_TYPE; +} + +/** + * set indentation style to GSC. + */ +void ASBeautifier::setGSCStyle() +{ + fileType = GSC_TYPE; +} + +/** + * set mode manually set flag + */ +void ASBeautifier::setModeManuallySet(bool state) +{ + isModeManuallySet = state; +} + +/** + * set tabLength equal to indentLength. + * This is done when tabLength is not explicitly set by + * "indent=force-tab-x" + * + */ +void ASBeautifier::setDefaultTabLength() +{ + tabLength = indentLength; +} + +/** + * indent using a different tab setting for indent=force-tab + * + * @param length number of spaces per tab. + */ +void ASBeautifier::setForceTabXIndentation(int length) +{ + // set tabLength instead of indentLength + indentString = "\t"; + tabLength = length; + shouldForceTabIndentation = true; +} + +/** + * indent using one tab per indentation + */ +void ASBeautifier::setTabIndentation(int length, bool forceTabs) +{ + indentString = "\t"; + indentLength = length; + shouldForceTabIndentation = forceTabs; +} + +/** + * indent using a number of spaces per indentation. + * + * @param length number of spaces per indent. + */ +void ASBeautifier::setSpaceIndentation(int length) +{ + indentString = std::string(length, ' '); + indentLength = length; +} + +/** +* indent continuation lines using a number of indents. +* +* @param indent number of indents per line. +*/ +void ASBeautifier::setContinuationIndentation(int indent) +{ + continuationIndent = indent; +} + +/** + * set the maximum indentation between two lines in a multi-line statement. + * + * @param max maximum indentation length. + */ +void ASBeautifier::setMaxContinuationIndentLength(int max) +{ + maxContinuationIndent = max; +} + +// retained for compatibility with release 2.06 +// "MaxInStatementIndent" has been changed to "MaxContinuationIndent" in 3.0 +// it is referenced only by the old "MaxInStatementIndent" options +void ASBeautifier::setMaxInStatementIndentLength(int max) +{ + setMaxContinuationIndentLength(max); +} + +/** + * set the minimum conditional indentation option. + * + * @param min minimal indentation option. + */ +void ASBeautifier::setMinConditionalIndentOption(int min) +{ + minConditionalOption = min; +} + +/** + * set minConditionalIndent from the minConditionalOption. + */ +void ASBeautifier::setMinConditionalIndentLength() +{ + if (minConditionalOption == MINCOND_ZERO) + minConditionalIndent = 0; + else if (minConditionalOption == MINCOND_ONE) + minConditionalIndent = indentLength; + else if (minConditionalOption == MINCOND_ONEHALF) + minConditionalIndent = indentLength / 2; + // minConditionalOption = INDENT_TWO + else + minConditionalIndent = indentLength * 2; +} + +/** + * set the state of the brace indent option. If true, braces will + * be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setBraceIndent(bool state) +{ + braceIndent = state; +} + +/** +* set the state of the brace indent VTK option. If true, braces will +* be indented one additional indent, except for the opening brace. +* +* @param state state of option. +*/ +void ASBeautifier::setBraceIndentVtk(bool state) +{ + // need to set both of these + setBraceIndent(state); + braceIndentVtk = state; +} + +/** + * set the state of the block indentation option. If true, entire blocks + * will be indented one additional indent, similar to the GNU indent style. + * + * @param state state of option. + */ +void ASBeautifier::setBlockIndent(bool state) +{ + blockIndent = state; +} + +/** + * set the state of the class indentation option. If true, C++ class + * definitions will be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setClassIndent(bool state) +{ + classIndent = state; +} + +/** + * set the state of the modifier indentation option. If true, C++ class + * access modifiers will be indented one-half an indent. + * + * @param state state of option. + */ +void ASBeautifier::setModifierIndent(bool state) +{ + modifierIndent = state; +} + +/** + * set the state of the switch indentation option. If true, blocks of 'switch' + * statements will be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setSwitchIndent(bool state) +{ + switchIndent = state; +} + +/** + * set the state of the case indentation option. If true, lines of 'case' + * statements will be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setCaseIndent(bool state) +{ + caseIndent = state; +} + +/** + * set the state of the namespace indentation option. + * If true, blocks of 'namespace' statements will be indented one + * additional indent. Otherwise, NO indentation will be added. + * + * @param state state of option. + */ +void ASBeautifier::setNamespaceIndent(bool state) +{ + namespaceIndent = state; +} + +/** +* set the state of the indent after parens option. +* +* @param state state of option. +*/ +void ASBeautifier::setAfterParenIndent(bool state) +{ + shouldIndentAfterParen = state; +} + +/** + * set the state of the label indentation option. + * If true, labels will be indented one indent LESS than the + * current indentation level. + * If false, labels will be flushed to the left with NO + * indent at all. + * + * @param state state of option. + */ +void ASBeautifier::setLabelIndent(bool state) +{ + labelIndent = state; +} + +/** + * set the state of the preprocessor indentation option. + * If true, multi-line #define statements will be indented. + * + * @param state state of option. + */ +void ASBeautifier::setPreprocDefineIndent(bool state) +{ + shouldIndentPreprocDefine = state; +} + +void ASBeautifier::setPreprocConditionalIndent(bool state) +{ + shouldIndentPreprocConditional = state; +} + +/** + * set the state of the empty line fill option. + * If true, empty lines will be filled with the whitespace. + * of their previous lines. + * If false, these lines will remain empty. + * + * @param state state of option. + */ +void ASBeautifier::setEmptyLineFill(bool state) +{ + emptyLineFill = state; +} + +void ASBeautifier::setAlignMethodColon(bool state) +{ + shouldAlignMethodColon = state; +} + +/** + * set the state of the squeeze whitespace option. If true, + * superfluous whitespace will be removed + * + * @param state state of option. + */ +void ASBeautifier::setSqueezeWhitespace(bool state) +{ + squeezeWhitespace = state; +} + +/** + * set the state of the preserve whitespace option. If true, + * original whitespace will be kept near comma operators + * + * @param state state of option. + */ +void ASBeautifier::setPreserveWhitespace(bool state) +{ + preserveWhitespace = state; +} + + +/** + * set the state of the lambda indentation option. If true, + * the parser tries to recognize lambda code. Indentation + * is bad if the lambda code is more complex (if/switch etc) + * + * @param state state of option. + */ +void ASBeautifier::setLambdaIndentation(bool state) +{ + attemptLambdaIndentation = state; +} + +/** + * get the file type. + */ +int ASBeautifier::getFileType() const +{ + return fileType; +} + +/** + * get the number of spaces per indent + * + * @return value of indentLength option. + */ +int ASBeautifier::getIndentLength() const +{ + return indentLength; +} + +/** + * get the char used for indentation, space or tab + * + * @return the char used for indentation. + */ +std::string ASBeautifier::getIndentString() const +{ + return indentString; +} + +/** + * get mode manually set flag + */ +bool ASBeautifier::getModeManuallySet() const +{ + return isModeManuallySet; +} + +/** + * get the state of the force tab indentation option. + * + * @return state of force tab indentation. + */ +bool ASBeautifier::getForceTabIndentation() const +{ + return shouldForceTabIndentation; +} + +/** +* Get the state of the Objective-C align method colon option. +* +* @return state of shouldAlignMethodColon option. +*/ +bool ASBeautifier::getAlignMethodColon() const +{ + return shouldAlignMethodColon; +} + +/** + * get the state of the block indentation option. + * + * @return state of blockIndent option. + */ +bool ASBeautifier::getBlockIndent() const +{ + return blockIndent; +} + +/** + * get the state of the brace indentation option. + * + * @return state of braceIndent option. + */ +bool ASBeautifier::getBraceIndent() const +{ + return braceIndent; +} + +/** +* Get the state of the namespace indentation option. If true, blocks +* of the 'namespace' statement will be indented one additional indent. +* +* @return state of namespaceIndent option. +*/ +bool ASBeautifier::getNamespaceIndent() const +{ + return namespaceIndent; +} + +/** + * Get the state of the class indentation option. If true, blocks of + * the 'class' statement will be indented one additional indent. + * + * @return state of classIndent option. + */ +bool ASBeautifier::getClassIndent() const +{ + return classIndent; +} + +/** + * Get the state of the class access modifier indentation option. + * If true, the class access modifiers will be indented one-half indent. + * + * @return state of modifierIndent option. + */ +bool ASBeautifier::getModifierIndent() const +{ + return modifierIndent; +} + +/** + * get the state of the switch indentation option. If true, blocks of + * the 'switch' statement will be indented one additional indent. + * + * @return state of switchIndent option. + */ +bool ASBeautifier::getSwitchIndent() const +{ + return switchIndent; +} + +/** + * get the state of the case indentation option. If true, lines of 'case' + * statements will be indented one additional indent. + * + * @return state of caseIndent option. + */ +bool ASBeautifier::getCaseIndent() const +{ + return caseIndent; +} + +/** + * get the state of the empty line fill option. + * If true, empty lines will be filled with the whitespace. + * of their previous lines. + * If false, these lines will remain empty. + * + * @return state of emptyLineFill option. + */ +bool ASBeautifier::getEmptyLineFill() const +{ + return emptyLineFill; +} + +/** + * get the state of the preprocessor indentation option. + * If true, preprocessor "define" lines will be indented. + * If false, preprocessor "define" lines will be unchanged. + * + * @return state of shouldIndentPreprocDefine option. + */ +bool ASBeautifier::getPreprocDefineIndent() const +{ + return shouldIndentPreprocDefine; +} + +/** + * get the length of the tab indentation option. + * + * @return length of tab indent option. + */ +int ASBeautifier::getTabLength() const +{ + return tabLength; +} + +std::string ASBeautifier::preLineWS(int lineIndentCount, int lineSpaceIndentCount) +{ + + if (shouldForceTabIndentation) + { + if (tabLength != indentLength) + { + // adjust for different tab length + int indentCountOrig = lineIndentCount; + int spaceIndentCountOrig = lineSpaceIndentCount; + lineIndentCount = ((indentCountOrig * indentLength) + spaceIndentCountOrig) / tabLength; + lineSpaceIndentCount = ((indentCountOrig * indentLength) + spaceIndentCountOrig) % tabLength; + } + else + { + lineIndentCount += lineSpaceIndentCount / indentLength; + lineSpaceIndentCount = lineSpaceIndentCount % indentLength; + } + } + + + std::string ws; + + if (lambdaDepthStack->size()) { + if (!lambdaStartIndent) + lineSpaceIndentCount = lambdaDepthStack->back(); + lambdaStartIndent = 0; + } + else if (lambdaEndIndent>0 ) { + lineSpaceIndentCount = lambdaEndIndent; + if (!lambdaIndicator) + lambdaEndIndent = 0; + } + + for (int i = 0; i < lineIndentCount; i++) + ws += indentString; + while ((lineSpaceIndentCount--) > 0) + ws += std::string(" "); + return ws; +} + +/** + * register a continuation indent. + */ +void ASBeautifier::registerContinuationIndent(std::string_view line, int i, int spaceIndentCount_, + int tabIncrementIn, int minIndent, bool updateParenStack) +{ + //return; //todo 585 + assert(i >= -1); + int remainingCharNum = line.length() - i; + int nextNonWSChar = getNextProgramCharDistance(line, i); + + // if indent is around the last char in the line OR indent-after-paren is requested, + // indent with the continuation indent + if (nextNonWSChar == remainingCharNum || shouldIndentAfterParen) + { + int previousIndent = spaceIndentCount_; + + if (!continuationIndentStack->empty()) + previousIndent = continuationIndentStack->back(); + + int currIndent = continuationIndent * indentLength + previousIndent; + + + // GL29 / GL45 + if (shouldIndentAfterParen) + { + size_t countOpenParen = std::count_if( line.begin(), line.end(), []( char c ) {return c == '(';}); + size_t countCloseParen = std::count_if( line.begin(), line.end(), []( char c ) {return c == ')';}); + + if (countOpenParen > 1 && countOpenParen > countCloseParen) + currIndent = indentLength; + } + + if (currIndent > maxContinuationIndent && line[i] != '{') + currIndent = indentLength * 2 + spaceIndentCount_; + + continuationIndentStack->emplace_back(currIndent); + if (updateParenStack) + { + parenIndentStack->emplace_back(previousIndent); + } + + return; + } + + if (updateParenStack) + { + parenIndentStack->emplace_back(i + spaceIndentCount_ - runInIndentContinuation); + if (parenIndentStack->back() < 0) + parenIndentStack->back() = 0; + } + + int tabIncrement = tabIncrementIn; + + // check for following tabs + for (int j = i + 1; j < (i + nextNonWSChar); j++) + { + if (line[j] == '\t') + tabIncrement += convertTabToSpaces(j, tabIncrement); + } + + int continuationIndentCount = i + nextNonWSChar + spaceIndentCount_ + tabIncrement; + + // check for run-in statement + if (i > 0 && line[0] == '{') + continuationIndentCount -= indentLength; + + if (continuationIndentCount < minIndent) + continuationIndentCount = minIndent + spaceIndentCount_; + + // this is not done for an in-statement array + int multiplier = isInAssignment ? 1 : 2; // GL16 - no multiply in assignments + if (continuationIndentCount > maxContinuationIndent + && !(prevNonLegalCh == '=' && currentNonLegalCh == '{')) + continuationIndentCount = indentLength * multiplier + spaceIndentCount_; + + if (!continuationIndentStack->empty() + && continuationIndentCount < continuationIndentStack->back()) + continuationIndentCount = continuationIndentStack->back(); + + // the block opener is not indented for a NonInStatementArray + if ((isNonInStatementArray && i >= 0 && line[i] == '{') + && !isInEnum && !isInStruct && !braceBlockStateStack->empty() && braceBlockStateStack->back()) + continuationIndentCount = 0; + continuationIndentStack->emplace_back(continuationIndentCount); +} + +/** +* Register a continuation indent for a class header or a class initializer colon. +*/ +void ASBeautifier::registerContinuationIndentColon(std::string_view line, int i, int tabIncrementIn) +{ + assert(line[i] == ':'); + assert(isInClassInitializer || isInClassHeaderTab); + + // register indent at first word after the colon + size_t firstChar = line.find_first_not_of(" \t"); + if (firstChar == (size_t) i) // firstChar is ':' + { + size_t firstWord = line.find_first_not_of(" \t", firstChar + 1); + if (firstWord != std::string::npos) + { + int continuationIndentCount = firstWord + spaceIndentCount + tabIncrementIn; + continuationIndentStack->emplace_back(continuationIndentCount); + isContinuation = true; + } + } +} + +/** + * Compute indentation for a preprocessor #if statement. + * This may be called for the activeBeautiferStack + * instead of the active ASBeautifier object. + */ +std::pair ASBeautifier::computePreprocessorIndent() +{ + computePreliminaryIndentation(); + std::pair entry(indentCount, spaceIndentCount); + if (!headerStack->empty() + && entry.first > 0 + && (headerStack->back() == &ASResource::AS_IF + || headerStack->back() == &ASResource::AS_ELSE + || headerStack->back() == &ASResource::AS_FOR + || headerStack->back() == &ASResource::AS_WHILE)) + --entry.first; + return entry; +} + +/** + * get distance to the next non-white space, non-comment character in the line. + * if no such character exists, return the length remaining to the end of the line. + */ +int ASBeautifier::getNextProgramCharDistance(std::string_view line, int i) const +{ + bool inComment = false; + int remainingCharNum = line.length() - i; + int charDistance; + char ch = ' '; + + for (charDistance = 1; charDistance < remainingCharNum; charDistance++) + { + ch = line[i + charDistance]; + if (inComment) + { + if (line.compare(i + charDistance, ASResource::AS_CLOSE_COMMENT.length(), ASResource::AS_CLOSE_COMMENT) == 0 + || line.compare(i + charDistance, ASResource::AS_GSC_CLOSE_COMMENT.length(), ASResource::AS_GSC_CLOSE_COMMENT) == 0) + { + charDistance++; + inComment = false; + } + continue; + } + if (std::isblank(ch)) + continue; + if (ch == '/') + { + if (line.compare(i + charDistance, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0) + return remainingCharNum; + if (line.compare(i + charDistance, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(i + charDistance, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0) + { + charDistance++; + inComment = true; + } + } + else + return charDistance; + } + + return charDistance; +} + +/** + * find the index number of a std::string element in a container of std::strings + * + * @return the index number of element in the container. -1 if element not found. + * @param container a vector of std::strings. + * @param element the element to find . + */ +int ASBeautifier::indexOf(const std::vector& container, const std::string* element) const +{ + std::vector::const_iterator where; + + where = find(container.begin(), container.end(), element); + if (where == container.end()) + return -1; + return (int) (where - container.begin()); +} + +/** + * convert tabs to spaces. + * i is the position of the 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. + */ +int ASBeautifier::convertTabToSpaces(int i, int tabIncrementIn) const +{ + int tabToSpacesAdjustment = indentLength - 1 - ((tabIncrementIn + i) % indentLength); + return tabToSpacesAdjustment; +} + +/** + * trim removes the white space surrounding a line. + * + * @return the trimmed line. + * @param str the line to trim. + */ +std::string ASBeautifier::trim(std::string_view str) const +{ + int start = 0; + int end = str.length() - 1; + + while (start < end && std::isblank(str[start])) + start++; + + while (start <= end && std::isblank(str[end])) + end--; + + // don't trim if it ends in a continuation + if (end > -1 && str[end] == '\\') + end = str.length() - 1; + + std::string returnStr(str.data(), start, end + 1 - start); + return returnStr; +} + +/** + * rtrim removes the white space from the end of a line. + * + * @return the trimmed line. + * @param str the line to trim. + */ +std::string ASBeautifier::rtrim(std::string_view str) const +{ + size_t len = str.length(); + size_t end = str.find_last_not_of(" \t"); + if (end == std::string::npos + || end == len - 1) + return std::string(str); + return std::string(str.substr(0, end + 1)); +} + +/** + * Copy tempStacks for the copy constructor. + * The value of the vectors must also be copied. + */ +std::vector*>* ASBeautifier::copyTempStacks(const ASBeautifier& other) const +{ + auto* tempStacksNew = new std::vector*>; + std::vector*>::iterator iter; + for (iter = other.tempStacks->begin(); + iter != other.tempStacks->end(); + ++iter) + { + auto* newVec = new std::vector; + *newVec = **iter; + tempStacksNew->emplace_back(newVec); + } + return tempStacksNew; +} + +/** + * delete a member vectors to eliminate memory leak reporting + */ +void ASBeautifier::deleteBeautifierVectors() +{ + beautifierFileType = INVALID_TYPE; // reset to an invalid type + delete headers; + delete nonParenHeaders; + delete preBlockStatements; + delete preCommandHeaders; + delete assignmentOperators; + delete nonAssignmentOperators; + delete indentableHeaders; +} + +/** + * delete a vector object + * T is the type of vector + * used for all vectors except tempStacks + */ +template +void ASBeautifier::deleteContainer(T& container) +{ + if (container != nullptr) + { + container->clear(); + delete (container); + container = nullptr; + } +} + +/** + * Delete the ASBeautifier vector object. + * This is a vector of pointers to ASBeautifier objects allocated with the 'new' operator. + * Therefore the ASBeautifier objects have to be deleted in addition to the + * ASBeautifier pointer entries. + */ +void ASBeautifier::deleteBeautifierContainer(std::vector*& container) +{ + if (container != nullptr) + { + auto iter = container->begin(); + while (iter < container->end()) + { + delete *iter; + ++iter; + } + container->clear(); + delete (container); + container = nullptr; + } +} + +/** + * Delete the tempStacks vector object. + * The tempStacks is a vector of pointers to std::strings allocated with the 'new' operator. + * Therefore the std::strings have to be deleted in addition to the tempStacks entries. + */ +void ASBeautifier::deleteTempStacksContainer(std::vector*>*& container) +{ + if (container != nullptr) + { + auto iter = container->begin(); + while (iter < container->end()) + { + delete *iter; + ++iter; + } + container->clear(); + delete (container); + container = nullptr; + } +} + +/** + * initialize a vector object + * T is the type of vector used for all vectors + */ +template +void ASBeautifier::initContainer(T& container, T value) +{ + // since the ASFormatter object is never deleted, + // the existing vectors must be deleted before creating new ones + if (container != nullptr) + deleteContainer(container); + container = value; +} + +/** + * Initialize the tempStacks vector object. + * The tempStacks is a vector of pointers to std::strings allocated with the 'new' operator. + * Any residual entries are deleted before the vector is initialized. + */ +void ASBeautifier::initTempStacksContainer(std::vector*>*& container, + std::vector*>* value) +{ + if (container != nullptr) + deleteTempStacksContainer(container); + container = value; +} + +/** + * Determine if an assignment statement ends with a comma + * that is not in a function argument. It ends with a + * comma if a comma is the last char on the line. + * + * @return true if line ends with a comma, otherwise false. + */ +bool ASBeautifier::statementEndsWithComma(std::string_view line, int index) const +{ + assert(line[index] == '='); + + bool isInComment_ = false; + bool isInQuote_ = false; + int parenCount = 0; + size_t lineLength = line.length(); + size_t i = 0; + char quoteChar_ = ' '; + + for (i = index + 1; i < lineLength; ++i) + { + char ch = line[i]; + + if (isInComment_) + { + if (line.compare(i, ASResource::AS_CLOSE_COMMENT.length(), ASResource::AS_CLOSE_COMMENT) == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + + if (ch == '\\') + { + ++i; + continue; + } + + if (isInQuote_) + { + if (ch == quoteChar_) + isInQuote_ = false; + continue; + } + + if (ch == '"' + || (ch == '\'' && !isDigitSeparator(line, i))) + { + isInQuote_ = true; + quoteChar_ = ch; + continue; + } + + if (line.compare(i, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0) + break; + + if (line.compare(i, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(i, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0) + { + if (isLineEndComment(line, i)) + break; + isInComment_ = true; + ++i; + continue; + } + + if (ch == '(') + parenCount++; + if (ch == ')') + parenCount--; + } + if (isInComment_ + || isInQuote_ + || parenCount > 0) + return false; + + size_t lastChar = line.find_last_not_of(" \t", i - 1); + + if (lastChar == std::string::npos || line[lastChar] != ',') + return false; + + return true; +} + +/** + * check if current comment is a line-end comment + * + * @return is before a line-end comment. + */ +bool ASBeautifier::isLineEndComment(std::string_view line, int startPos) const +{ + assert(line.compare(startPos, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(startPos, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0 ); + + bool isCppComment = line.compare(startPos, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT); + + // comment must be closed on this line with nothing after it + size_t endNum = line.find(isCppComment ? ASResource::AS_CLOSE_COMMENT : ASResource::AS_GSC_CLOSE_COMMENT, startPos + 2); + if (endNum != std::string::npos) + { + size_t nextChar = line.find_first_not_of(" \t", endNum + 2); + if (nextChar == std::string::npos) + return true; + } + return false; +} + +/** + * get the previous word index for an assignment operator + * + * @return is the index to the previous word (the in statement indent). + */ +int ASBeautifier::getContinuationIndentAssign(std::string_view line, size_t currPos) const +{ + assert(line[currPos] == '='); + + if (currPos == 0) + return 0; + + // get the last legal word (may be a number) + size_t end = line.find_last_not_of(" \t", currPos - 1); + if (end == std::string::npos || !isLegalNameChar(line[end])) + return 0; + + int start; // start of the previous word + for (start = end; start > -1; start--) + { + if (!isLegalNameChar(line[start])) + break; + } + start++; + + return start; +} + +/** + * get the continuation indent for a comma + * + * @return is the indent to the second word on the line (the in statement indent). + */ +int ASBeautifier::getContinuationIndentComma(std::string_view line, size_t currPos) const +{ + assert(line[currPos] == ','); + + // get first word on a line + size_t indent = line.find_first_not_of(" \t"); + if (indent == std::string::npos || !isLegalNameChar(line[indent])) + return 0; + + // bypass first word + for (; indent < currPos; indent++) + { + if (!isLegalNameChar(line[indent])) + break; + } + indent++; + if (indent >= currPos || indent < 4) + return 0; + + // point to second word or assignment operator + indent = line.find_first_not_of(" \t", indent); + if (indent == std::string::npos || indent >= currPos) + return 0; + + return indent; +} + +/** + * get the next word on a line + * the argument 'currPos' must point to the current position. + * + * @return is the next word or an empty std::string if none found. + */ +std::string ASBeautifier::getNextWord(const std::string& line, size_t currPos) const +{ + size_t lineLength = line.length(); + // get the last legal word (may be a number) + if (currPos == lineLength - 1) + return std::string(); + + size_t start = line.find_first_not_of(" \t", currPos + 1); + if (start == std::string::npos || !isLegalNameChar(line[start])) + return std::string(); + + size_t end; // end of the current word + for (end = start + 1; end <= lineLength; end++) + { + if (!isLegalNameChar(line[end]) || line[end] == '.') + break; + } + + return line.substr(start, end - start); +} + +/** + * Check if a preprocessor directive is always indented. + * C# "region" and "endregion" are always indented. + * C/C++ "pragma omp" is always indented. + * + * @return is true or false. + */ +bool ASBeautifier::isIndentedPreprocessor(std::string_view line, size_t currPos) const +{ + assert(line[0] == '#'); + std::string nextWord = getNextWord(std::string(line), currPos); + if (nextWord == "region" || nextWord == "endregion") + return true; + // is it #pragma omp + if (nextWord == "pragma") + { + // find pragma + size_t start = line.find("pragma"); + if (start == std::string::npos || !isLegalNameChar(line[start])) + return false; + // bypass pragma + for (; start < line.length(); start++) + { + if (!isLegalNameChar(line[start])) + break; + } + start++; + if (start >= line.length()) + return false; + // point to start of second word + start = line.find_first_not_of(" \t", start); + if (start == std::string::npos) + return false; + // point to end of second word + size_t end; + for (end = start; end < line.length(); end++) + { + if (!isLegalNameChar(line[end])) + break; + } + // check for "pragma omp" + std::string_view word = line.substr(start, end - start); + if (word == "omp" || word == "region" || word == "endregion") + return true; + } + return false; +} + +/** + * Check if a preprocessor directive is checking for __cplusplus defined. + * + * @return is true or false. + */ +bool ASBeautifier::isPreprocessorConditionalCplusplus(std::string_view line) const +{ + std::string preproc = trim(line.substr(1)); + if (preproc.compare(0, 5, "ifdef") == 0 && getNextWord(preproc, 4) == "__cplusplus") + return true; + if (preproc.compare(0, 2, "if") == 0) + { + // check for " #if defined(__cplusplus)" + size_t charNum = 2; + charNum = preproc.find_first_not_of(" \t", charNum); + if (charNum != std::string::npos && preproc.compare(charNum, 7, "defined") == 0) + { + charNum += 7; + charNum = preproc.find_first_not_of(" \t", charNum); + if (charNum != std::string::npos && preproc.compare(charNum, 1, "(") == 0) + { + ++charNum; + charNum = preproc.find_first_not_of(" \t", charNum); + if (charNum != std::string::npos && preproc.compare(charNum, 11, "__cplusplus") == 0) + return true; + } + } + } + return false; +} + +/** + * Check if a preprocessor definition contains an unterminated comment. + * Comments within a preprocessor definition can be continued without the backslash. + * + * @return is true or false. + */ +bool ASBeautifier::isInPreprocessorUnterminatedComment(std::string_view line) +{ + if (!isInPreprocessorComment) + { + size_t startPos = line.find(ASResource::AS_OPEN_COMMENT); + if (startPos == std::string::npos) + return false; + // handle edge case #SF600 + // #define Q 16 //*32 bit unsigned long + size_t startPosSingle = line.find(ASResource::AS_OPEN_LINE_COMMENT); + if (startPos == startPosSingle+1) + return false; + } + size_t endNum = line.find(ASResource::AS_CLOSE_COMMENT); + if (endNum != std::string::npos) + { + isInPreprocessorComment = false; + return false; + } + isInPreprocessorComment = true; + return true; +} + +void ASBeautifier::popLastContinuationIndent() +{ + assert(!continuationIndentStackSizeStack->empty()); + int previousIndentStackSize = continuationIndentStackSizeStack->back(); + if (continuationIndentStackSizeStack->size() > 1) + continuationIndentStackSizeStack->pop_back(); + while (previousIndentStackSize < (int) continuationIndentStack->size()) + continuationIndentStack->pop_back(); +} + +// for unit testing +int ASBeautifier::getBeautifierFileType() const +{ return beautifierFileType; } + +/** + * Process preprocessor statements and update the beautifier stacks. + */ +void ASBeautifier::processPreprocessor(std::string_view preproc, std::string_view line) +{ + // When finding a multi-lined #define statement, the original beautifier + // 1. sets its isInDefineDefinition flag + // 2. clones a new beautifier that will be used for the actual indentation + // of the #define. This clone is put into the activeBeautifierStack in order + // to be called for the actual indentation. + // The original beautifier will have isInDefineDefinition = true, isInDefine = false + // The cloned beautifier will have isInDefineDefinition = true, isInDefine = true + if (shouldIndentPreprocDefine && preproc == "define" && line[line.length() - 1] == '\\') + { + if (!isInDefineDefinition) + { + // this is the original beautifier + isInDefineDefinition = true; + + // push a new beautifier into the active stack + // this beautifier will be used for the indentation of this define + auto* defineBeautifier = new ASBeautifier(*this); + activeBeautifierStack->emplace_back(defineBeautifier); + } + else + { + // the is the cloned beautifier that is in charge of indenting the #define. + isInDefine = true; + } + } + else if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") + { + if (isPreprocessorConditionalCplusplus(line) && !g_preprocessorCppExternCBrace) + g_preprocessorCppExternCBrace = 1; + // push a new beautifier into the stack + waitingBeautifierStackLengthStack->emplace_back(waitingBeautifierStack->size()); + activeBeautifierStackLengthStack->emplace_back(activeBeautifierStack->size()); + if (activeBeautifierStackLengthStack->back() == 0) + waitingBeautifierStack->emplace_back(new ASBeautifier(*this)); + else + waitingBeautifierStack->emplace_back(new ASBeautifier(*activeBeautifierStack->back())); + } + else if (preproc == "else") + { + if ((waitingBeautifierStack != nullptr) && !waitingBeautifierStack->empty()) + { + // MOVE current waiting beautifier to active stack. + activeBeautifierStack->emplace_back(waitingBeautifierStack->back()); + waitingBeautifierStack->pop_back(); + } + } + else if (preproc == "elif") + { + if ((waitingBeautifierStack != nullptr) && !waitingBeautifierStack->empty()) + { + // append a COPY current waiting beautifier to active stack, WITHOUT deleting the original. + activeBeautifierStack->emplace_back(new ASBeautifier(*(waitingBeautifierStack->back()))); + } + } + else if (preproc == "endif") + { + int stackLength = 0; + ASBeautifier* beautifier = nullptr; + + if (waitingBeautifierStackLengthStack != nullptr && !waitingBeautifierStackLengthStack->empty()) + { + stackLength = waitingBeautifierStackLengthStack->back(); + waitingBeautifierStackLengthStack->pop_back(); + while ((int) waitingBeautifierStack->size() > stackLength) + { + beautifier = waitingBeautifierStack->back(); + waitingBeautifierStack->pop_back(); + delete beautifier; + } + } + + if (!activeBeautifierStackLengthStack->empty()) + { + stackLength = activeBeautifierStackLengthStack->back(); + activeBeautifierStackLengthStack->pop_back(); + while ((int) activeBeautifierStack->size() > stackLength) + { + beautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + delete beautifier; + } + } + } +} + +// Compute the preliminary indentation based on data in the headerStack +// and data from previous lines. +// Update the class variable indentCount. +void ASBeautifier::computePreliminaryIndentation() +{ + indentCount = 0; + // logIndent(5); + spaceIndentCount = 0; + isInClassHeaderTab = false; + + if (isInObjCMethodDefinition && !continuationIndentStack->empty()) + spaceIndentObjCMethodAlignment = continuationIndentStack->back(); + + if (!continuationIndentStack->empty()) + spaceIndentCount = continuationIndentStack->back(); + + for (size_t i = 0; i < headerStack->size(); i++) + { + isInClass = false; + + if (blockIndent) + { + // do NOT indent opening block for these headers + if (!((*headerStack)[i] == &ASResource::AS_NAMESPACE + || (*headerStack)[i] == &ASResource::AS_MODULE + || (*headerStack)[i] == &ASResource::AS_CLASS + || (*headerStack)[i] == &ASResource::AS_STRUCT + || (*headerStack)[i] == &ASResource::AS_UNION + || (*headerStack)[i] == &ASResource::AS_INTERFACE + || (*headerStack)[i] == &ASResource::AS_THROWS + || (*headerStack)[i] == &ASResource::AS_STATIC)){ + ++indentCount; + // logIndent(6); + } + + } + else + { + //GL37 + if (!(i > 0 && (*headerStack)[i - 1] != &ASResource::AS_OPEN_BRACE + && (*headerStack)[i] == &ASResource::AS_OPEN_BRACE)){ + ++indentCount; + + // logIndent(7); + } + + } + + if (!isJavaStyle() && !namespaceIndent && i > 0 + && ((*headerStack)[i - 1] == &ASResource::AS_NAMESPACE + || (*headerStack)[i - 1] == &ASResource::AS_MODULE) + && (*headerStack)[i] == &ASResource::AS_OPEN_BRACE){ + --indentCount; + // logIndent(8); + + } + + if (isCStyle() && i >= 1 + && (*headerStack)[i - 1] == &ASResource::AS_CLASS + && (*headerStack)[i] == &ASResource::AS_OPEN_BRACE) + { + if (classIndent){ + ++indentCount; + // logIndent(9); + } + + isInClass = true; + } + + // is the switchIndent option is on, indent switch statements an additional indent. + else if (switchIndent && i > 1 + && (*headerStack)[i - 1] == &ASResource::AS_SWITCH + && (*headerStack)[i] == &ASResource::AS_OPEN_BRACE) + { + ++indentCount; + // logIndent(10); + isInSwitch = true; + } + + // GL26 check if line is a label.... do not indent + //std::cerr << "line "<empty()) + spaceIndentCount -= continuationIndentStack->back(); + } + else if (blockIndent) + { + if (!lineBeginsWithOpenBrace){ + ++indentCount; + // logIndent(12); + } + + } + } + + if (isInClassInitializer || isInEnumTypeID) + { + indentCount += classInitializerIndents; + // logIndent(13); + } + + if ( (isInEnum || isInStruct) && lineBeginsWithComma && !continuationIndentStack->empty()) + { + // unregister '=' indent from the previous line + continuationIndentStack->pop_back(); + isContinuation = false; + spaceIndentCount = 0; + } + + // Objective-C interface continuation line + if (isInObjCInterface){ + ++indentCount; + // logIndent(14); + + } + + // unindent a class closing brace... + if (!lineStartsInComment + && isCStyle() + && isInClass + && classIndent + && headerStack->size() >= 2 + && (*headerStack)[headerStack->size() - 2] == &ASResource::AS_CLASS + && (*headerStack)[headerStack->size() - 1] == &ASResource::AS_OPEN_BRACE + && lineBeginsWithCloseBrace + && braceBlockStateStack->back()){ + --indentCount; + // logIndent(15); + } + // unindent an indented switch closing brace... + else if (!lineStartsInComment + && isInSwitch + && switchIndent + && headerStack->size() >= 2 + && (*headerStack)[headerStack->size() - 2] == &ASResource::AS_SWITCH + && (*headerStack)[headerStack->size() - 1] == &ASResource::AS_OPEN_BRACE + && lineBeginsWithCloseBrace){ + --indentCount; + // logIndent(16); + + } + // handle special case of run-in comment in an indented class statement + if (isInClass + && classIndent + && isInRunInComment + && !lineOpensWithComment + && headerStack->size() > 1 + && (*headerStack)[headerStack->size() - 2] == &ASResource::AS_CLASS){ + --indentCount; + // logIndent(17); + + } + if (isInConditional){ + --indentCount; + // logIndent(18); + + } + if (g_preprocessorCppExternCBrace >= 4){ + --indentCount; + // logIndent(19); + } + + +} + +void ASBeautifier::adjustParsedLineIndentation(size_t iPrelim, bool isInExtraHeaderIndent) +{ + if (lineStartsInComment) + return; + + // unindent a one-line statement in a header indent + if (!blockIndent + && lineBeginsWithOpenBrace + && headerStack->size() < iPrelim + && isInExtraHeaderIndent + && (lineOpeningBlocksNum > 0 && lineOpeningBlocksNum <= lineClosingBlocksNum) + && shouldIndentBracedLine){ + + + --indentCount; + // logIndent(20); + } + + + /* + * if '{' doesn't follow an immediately previous '{' in the headerStack + * (but rather another header such as "for" or "if", then unindent it + * by one indentation relative to its block. + */ + else if (!blockIndent + && lineBeginsWithOpenBrace + && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum <= lineClosingBlocksNum) + && (headerStack->size() > 1 && (*headerStack)[headerStack->size() - 2] != &ASResource::AS_OPEN_BRACE) + && shouldIndentBracedLine) + { + --indentCount; + // logIndent(21); + } + + // must check one less in headerStack if more than one header on a line (allow-addins)... + else if (headerStack->size() > iPrelim + 1 + && !blockIndent + && lineBeginsWithOpenBrace + && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum <= lineClosingBlocksNum) + && (headerStack->size() > 2 && (*headerStack)[headerStack->size() - 3] != &ASResource::AS_OPEN_BRACE) + && shouldIndentBracedLine) + { + + --indentCount; + // logIndent(22); + } + + // unindent a closing brace... + else if (lineBeginsWithCloseBrace + && shouldIndentBracedLine) + { + + --indentCount; + + + // logIndent(23); + } + + // correctly indent one-line-blocks... + else if (lineOpeningBlocksNum > 0 + && lineOpeningBlocksNum == lineClosingBlocksNum + && previousLineProbationTab) + { + + --indentCount; + // logIndent(24); + } + + if (indentCount < 0) + { + + indentCount = 0; + // logIndent(25); + } + // take care of extra brace indentation option... + if (!lineStartsInComment + && braceIndent + && shouldIndentBracedLine + && (lineBeginsWithOpenBrace || lineBeginsWithCloseBrace)) + { + if (!braceIndentVtk){ + ++indentCount; + // logIndent(26); + } + else + { + // determine if a style VTK brace is indented + bool haveUnindentedBrace = false; + for (size_t i = 0; i < headerStack->size(); i++) + { + if (((*headerStack)[i] == &ASResource::AS_NAMESPACE + || (*headerStack)[i] == &ASResource::AS_MODULE + || (*headerStack)[i] == &ASResource::AS_CLASS + || (*headerStack)[i] == &ASResource::AS_STRUCT) + && i + 1 < headerStack->size() + && (*headerStack)[i + 1] == &ASResource::AS_OPEN_BRACE) + i++; + else if (lineBeginsWithOpenBrace) + { + // don't double count the current brace + if (i + 1 < headerStack->size() + && (*headerStack)[i] == &ASResource::AS_OPEN_BRACE) + haveUnindentedBrace = true; + } + else if ((*headerStack)[i] == &ASResource::AS_OPEN_BRACE) + haveUnindentedBrace = true; + } // end of for loop + if (haveUnindentedBrace){ + ++indentCount; + // logIndent(27); + } + + } + } +} + +/** + * Compute indentCount adjustment when in a series of else-if statements + * and shouldBreakElseIfs is requested. + * It increments by one for each 'else' in the tempStack. + */ +int ASBeautifier::adjustIndentCountForBreakElseIfComments() const +{ + assert(isElseHeaderIndent && !tempStacks->empty()); + int indentCountIncrement = 0; + std::vector* lastTempStack = tempStacks->back(); + if (lastTempStack != nullptr) + { + for (const std::string* const lastTemp : *lastTempStack) + { + if (*lastTemp == ASResource::AS_ELSE){ + indentCountIncrement++; + } + + } + } + return indentCountIncrement; +} + +/** + * Extract a preprocessor statement without the #. + * If a error occurs an empty std::string is returned. + */ +std::string ASBeautifier::extractPreprocessorStatement(std::string_view line) const +{ + std::string preproc; + size_t start = line.find_first_not_of("#/ \t"); + if (start == std::string::npos) + return preproc; + size_t end = line.find_first_of("/ \t", start); + if (end == std::string::npos) + end = line.length(); + const auto lineSubstr = line.substr(start, end - start); + preproc = std::string(lineSubstr.data(), lineSubstr.length()); + return preproc; +} + +void ASBeautifier::adjustObjCMethodDefinitionIndentation(std::string_view line_) +{ + // register indent for Objective-C continuation line + if (!line_.empty() + && (line_[0] == '-' || line_[0] == '+')) + { + if (shouldAlignMethodColon && objCColonAlignSubsequent != -1) + { + std::string convertedLine = getIndentedSpaceEquivalent(line_); + colonIndentObjCMethodAlignment = findObjCColonAlignment(convertedLine); + int objCColonAlignSubsequentIndent = objCColonAlignSubsequent + indentLength; + if (objCColonAlignSubsequentIndent > colonIndentObjCMethodAlignment) + colonIndentObjCMethodAlignment = objCColonAlignSubsequentIndent; + } + else if (continuationIndentStack->empty() + || continuationIndentStack->back() == 0) + { + continuationIndentStack->emplace_back(indentLength); + isContinuation = true; + } + } + // set indent for last definition line + else if (!lineBeginsWithOpenBrace) + { + if (shouldAlignMethodColon) + spaceIndentCount = computeObjCColonAlignment(line_, colonIndentObjCMethodAlignment); + else if (continuationIndentStack->empty()) + spaceIndentCount = spaceIndentObjCMethodAlignment; + } +} + +void ASBeautifier::adjustObjCMethodCallIndentation(std::string_view line_) +{ + static int keywordIndentObjCMethodAlignment = 0; + if (shouldAlignMethodColon && objCColonAlignSubsequent != -1) + { + if (isInObjCMethodCallFirst) + { + isInObjCMethodCallFirst = false; + std::string convertedLine = getIndentedSpaceEquivalent(line_); + bracePosObjCMethodAlignment = convertedLine.find('['); + keywordIndentObjCMethodAlignment = + getObjCFollowingKeyword(convertedLine, bracePosObjCMethodAlignment); + colonIndentObjCMethodAlignment = findObjCColonAlignment(convertedLine); + if (colonIndentObjCMethodAlignment >= 0) + { + int objCColonAlignSubsequentIndent = objCColonAlignSubsequent + indentLength; + if (objCColonAlignSubsequentIndent > colonIndentObjCMethodAlignment) + colonIndentObjCMethodAlignment = objCColonAlignSubsequentIndent; + if (lineBeginsWithOpenBrace) + colonIndentObjCMethodAlignment -= indentLength; + } + } + else + { + if (findObjCColonAlignment(line_) != -1) + { + if (colonIndentObjCMethodAlignment < 0) + spaceIndentCount += computeObjCColonAlignment(line_, objCColonAlignSubsequent); + else if (objCColonAlignSubsequent > colonIndentObjCMethodAlignment) + spaceIndentCount = computeObjCColonAlignment(line_, objCColonAlignSubsequent); + else + spaceIndentCount = computeObjCColonAlignment(line_, colonIndentObjCMethodAlignment); + } + else + { + if (spaceIndentCount < colonIndentObjCMethodAlignment) + spaceIndentCount += keywordIndentObjCMethodAlignment; + } + } + } + else // align keywords instead of colons + { + if (isInObjCMethodCallFirst) + { + isInObjCMethodCallFirst = false; + std::string convertedLine = getIndentedSpaceEquivalent(line_); + bracePosObjCMethodAlignment = convertedLine.find('['); + keywordIndentObjCMethodAlignment = + getObjCFollowingKeyword(convertedLine, bracePosObjCMethodAlignment); + } + else + { + if (spaceIndentCount < keywordIndentObjCMethodAlignment + bracePosObjCMethodAlignment) + spaceIndentCount += keywordIndentObjCMethodAlignment; + } + } +} + +/** + * Clear the variables used to align the Objective-C method definitions. + */ +void ASBeautifier::clearObjCMethodDefinitionAlignment() +{ + assert(isImmediatelyPostObjCMethodDefinition); + spaceIndentCount = 0; + spaceIndentObjCMethodAlignment = 0; + colonIndentObjCMethodAlignment = 0; + isInObjCMethodDefinition = false; + isImmediatelyPostObjCMethodDefinition = false; + if (!continuationIndentStack->empty()) + continuationIndentStack->pop_back(); +} + +/** + * Find the first alignment colon on a line. + * Ternary operators (?) are bypassed. + */ +int ASBeautifier::findObjCColonAlignment(std::string_view line) const +{ + bool haveTernary = false; + for (size_t i = 0; i < line.length(); i++) + { + i = line.find_first_of(":?", i); + if (i == std::string::npos) + break; + + if (line[i] == '?') + { + haveTernary = true; + continue; + } + if (haveTernary) + { + haveTernary = false; + continue; + } + return i; + } + return -1; +} + +/* chatgpt + +int ASBeautifier::findObjCColonAlignment(const std::string& line) const +{ + bool haveTernary = false; + for (char c : line) + { + if (c == ':' || c == '?') + { + if (c == '?') + { + haveTernary = true; + continue; + } + + if (haveTernary) + { + haveTernary = false; + continue; + } + + return &c - line.c_str(); // Calculate the index of the found character + } + } + return -1; +} +*/ + +/** + * Compute the spaceIndentCount necessary to align the current line colon + * with the colon position in the argument. + * If it cannot be aligned indentLength is returned and a new colon + * position is calculated. + */ +int ASBeautifier::computeObjCColonAlignment(std::string_view line, int colonAlignPosition) const +{ + int colonPosition = findObjCColonAlignment(line); + if (colonPosition < 0 || colonPosition > colonAlignPosition) + return indentLength; + return (colonAlignPosition - colonPosition); +} + +/* + * Compute position of the keyword following the method call object. + * This is oversimplified to find unusual method calls. + * Use for now and see what happens. + * Most programmers will probably use align-method-colon anyway. + */ +int ASBeautifier::getObjCFollowingKeyword(std::string_view line, int bracePos) const +{ + assert(line[bracePos] == '['); + size_t firstText = line.find_first_not_of(" \t", bracePos + 1); + if (firstText == std::string::npos) + return -(indentCount * indentLength - 1); + size_t searchBeg = firstText; + size_t objectEnd = 0; // end of object text + if (line[searchBeg] == '[') + { + objectEnd = line.find(']', searchBeg + 1); + if (objectEnd == std::string::npos) + return 0; + } + else + { + if (line[searchBeg] == '(') + { + searchBeg = line.find(')', searchBeg + 1); + if (searchBeg == std::string::npos) + return 0; + } + // bypass the object name + objectEnd = line.find_first_of(" \t", searchBeg + 1); + if (objectEnd == std::string::npos) + return 0; + --objectEnd; + } + size_t keyPos = line.find_first_not_of(" \t", objectEnd + 1); + if (keyPos == std::string::npos) + return 0; + + return static_cast(keyPos - firstText); // Cast to int for return value +} + +/** + * Get a line using the current space indent with all tabs replaced by spaces. + * The indentCount is NOT included + * Needed to compute an accurate alignment. + */ +std::string ASBeautifier::getIndentedSpaceEquivalent(std::string_view line_) const +{ + std::string spaceIndent; + + spaceIndent.append(spaceIndentCount, ' '); + std::string convertedLine = spaceIndent + std::string(line_); + for (size_t i = spaceIndent.length(); i < convertedLine.length(); i++) + { + if (convertedLine[i] == '\t') + { + size_t numSpaces = indentLength - (i % indentLength); + convertedLine.replace(i, 1, numSpaces, ' '); + i += indentLength - 1; + } + } + return convertedLine; +} + +/** + * Determine if an item is at a top level. + */ +bool ASBeautifier::isTopLevel() const +{ + if (headerStack->empty()) + return true; + if (headerStack->back() == &ASResource::AS_OPEN_BRACE + && headerStack->size() >= 2) + { + if ((*headerStack)[headerStack->size() - 2] == &ASResource::AS_NAMESPACE + || (*headerStack)[headerStack->size() - 2] == &ASResource::AS_MODULE + || (*headerStack)[headerStack->size() - 2] == &ASResource::AS_CLASS + || (*headerStack)[headerStack->size() - 2] == &ASResource::AS_INTERFACE + || (*headerStack)[headerStack->size() - 2] == &ASResource::AS_STRUCT + || (*headerStack)[headerStack->size() - 2] == &ASResource::AS_UNION) + return true; + } + if (headerStack->back() == &ASResource::AS_NAMESPACE + || headerStack->back() == &ASResource::AS_MODULE + || headerStack->back() == &ASResource::AS_CLASS + || headerStack->back() == &ASResource::AS_INTERFACE + || headerStack->back() == &ASResource::AS_STRUCT + || headerStack->back() == &ASResource::AS_UNION) + return true; + return false; +} + + +bool ASBeautifier::handleHeaderSection(std::string_view line, size_t* i, bool closingBraceReached, bool *haveCaseIndent) +{ + const std::string* newHeader = findHeader(line, *i, headers); + + // java can have a 'default' not in a switch + if (newHeader == &ASResource::AS_DEFAULT + && peekNextChar(line, (*i + (*newHeader).length() - 1)) != ':') + newHeader = nullptr; + // Qt headers may be variables in C++ + if (isCStyle() + && (newHeader == &ASResource::AS_FOREVER || newHeader == &ASResource::AS_FOREACH)) + { + if (line.find_first_of("=;", *i) != std::string::npos) + newHeader = nullptr; + } + else if (isSharpStyle() + && (newHeader == &ASResource::AS_GET || newHeader == &ASResource::AS_SET)) + { + if (getNextWord(std::string(line), *i + (*newHeader).length()) == "is") + newHeader = nullptr; + } + else if (newHeader == &ASResource::AS_USING + && peekNextChar(line, *i + (*newHeader).length() - 1) != '(') + newHeader = nullptr; + + if (newHeader != nullptr) + { + // if we reached here, then this is a header... + bool isIndentableHeader = true; + + isInHeader = true; + + std::vector* lastTempStack = nullptr; + if (!tempStacks->empty()) + lastTempStack = tempStacks->back(); + + // if a new block is opened, push a new stack into tempStacks to hold the + // future list of headers in the new block. + + lastLambdaDepth = 0; + + // take care of the special case: 'else if (...)' + if (newHeader == &ASResource::AS_IF && lastLineHeader == &ASResource::AS_ELSE) + { + if (!headerStack->empty()) + headerStack->pop_back(); + } + + // take care of 'else' + else if (newHeader == &ASResource::AS_ELSE && !lambdaDepth) + { + if (lastTempStack != nullptr) + { + int indexOfIf = indexOf(*lastTempStack, &ASResource::AS_IF); + if (indexOfIf != -1) + { + // recreate the header list in headerStack up to the previous 'if' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfIf - 1; + for (int r = 0; r < restackSize; r++) + { + headerStack->emplace_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + if (!closingBraceReached){ + indentCount += restackSize; + // logIndent(30); + } + + } + /* + * If the above if is not true, i.e. no 'if' before the 'else', + * then nothing beautiful will come out of this... + * I should think about inserting an Exception here to notify the caller of this... + */ + } + } + + // check if 'while' closes a previous 'do' + else if (newHeader == &ASResource::AS_WHILE) + { + if (lastTempStack != nullptr) + { + int indexOfDo = indexOf(*lastTempStack, &ASResource::AS_DO); + if (indexOfDo != -1) + { + // recreate the header list in headerStack up to the previous 'do' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfDo - 1; + for (int r = 0; r < restackSize; r++) + { + headerStack->emplace_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + if (!closingBraceReached) + indentCount += restackSize; + } + } + } + // check if 'catch' closes a previous 'try' or 'catch' + else if (newHeader == &ASResource::AS_CATCH || newHeader == &ASResource::AS_FINALLY) + { + if (lastTempStack != nullptr) + { + int indexOfTry = indexOf(*lastTempStack, &ASResource::AS_TRY); + if (indexOfTry == -1) + indexOfTry = indexOf(*lastTempStack, &ASResource::AS_CATCH); + if (indexOfTry != -1) + { + // recreate the header list in headerStack up to the previous 'try' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfTry - 1; + for (int r = 0; r < restackSize; r++) + { + headerStack->emplace_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + + if (!closingBraceReached){ + indentCount += restackSize; + // logIndent(31); + } + + } + } + } + else if (newHeader == &ASResource::AS_CASE) + { + isInCase = true; + if (!*haveCaseIndent) + { + *haveCaseIndent = true; + if (!lineBeginsWithOpenBrace){ + --indentCount; + // logIndent(32); + } + } + } + else if (newHeader == &ASResource::AS_DEFAULT) + { + isInCase = true; + --indentCount; + // logIndent(33); + } + else if (newHeader == &ASResource::AS_STATIC + || newHeader == &ASResource::AS_SYNCHRONIZED) + { + if (!headerStack->empty() + && (headerStack->back() == &ASResource::AS_STATIC + || headerStack->back() == &ASResource::AS_SYNCHRONIZED)) + { + isIndentableHeader = false; + } + else + { + isIndentableHeader = false; + probationHeader = newHeader; + } + } + else if (newHeader == &ASResource::AS_TEMPLATE) + { + isInTemplate = true; + isIndentableHeader = false; + } + + if (isIndentableHeader) + { + headerStack->emplace_back(newHeader); + isContinuation = false; + if (indexOf(*nonParenHeaders, newHeader) == -1) + { + isInConditional = true; + } + lastLineHeader = newHeader; + } + else + isInHeader = false; + + *i += newHeader->length() - 1; + + return false; + } // newHeader != nullptr + + if (findHeader(line, *i, preCommandHeaders) != nullptr) + // must be after function arguments + if (prevNonSpaceCh == ')') + foundPreCommandHeader = true; + + // Objective-C NSException macros are preCommandHeaders + if (isObjCStyle() && findKeyword(line, *i, ASResource::AS_NS_DURING)) + foundPreCommandMacro = true; + if (isObjCStyle() && findKeyword(line, *i, ASResource::AS_NS_HANDLER)) + foundPreCommandMacro = true; + + //https://sourceforge.net/p/astyle/bugs/353/ + // new is ending the line? + if (isJavaStyle() && findKeyword(line, *i, ASResource::AS_NEW) && line.length() - 3 == *i) + { + headerStack->emplace_back(&ASResource::AS_FIXED); // needs to be something which will not match - need to define a token which will never match + } + + //https://sourceforge.net/p/astyle/bugs/550/ + //enum can be function return value + if (parenDepth == 0 && findKeyword(line, *i, ASResource::AS_ENUM) && line.find_first_of(ASResource::AS_OPEN_PAREN, *i) == std::string::npos) + { + isInEnum = true; + } + + if (parenDepth == 0 && (findKeyword(line, *i, ASResource::AS_TYPEDEF_STRUCT) || findKeyword(line, *i, ASResource::AS_STRUCT)) && line.find_first_of(ASResource::AS_SEMICOLON, *i) == std::string::npos) + { + isInStruct = true; + isInTemplate = false; + bracesNestingLevelOfStruct = bracesNestingLevel; + } + + // avoid regression with neovim test dataset + if (parenDepth == 0 && findKeyword(line, *i, ASResource::AS_UNION) ) + { + isInStruct = false; + } + + if (isSharpStyle() && findKeyword(line, *i, ASResource::AS_LET)) + isInLet = true; + + return true; +} + +bool ASBeautifier::isNumericVariable(std::string_view word) const +{ + if (word == "bool" + || word == "int" + || word == "void" + || word == "char" + || word == "long" + || word == "unsigned" + || word == "short" + || word == "double" + || word == "float" + || (word.length() >= 4 // check end of word for _t + && word.compare(word.length() - 2, 2, "_t") == 0) + + || word == "BOOL" + || word == "DWORD" + || word == "HWND" + || word == "INT" + || word == "LPSTR" + || word == "VOID" + || word == "LPVOID" + || word == "wxFontEncoding" + ) + return true; + return false; +} + + +bool ASBeautifier::lineStartsWithNumericType(std::string_view line) const +{ + size_t firstCharOfLine = line.find_first_not_of(" \t"); + if (firstCharOfLine != std::string::npos && isCStyle()) + { + size_t endOfWord = line.find_first_of(" \t", firstCharOfLine + 1); + std::string_view word = line.substr(firstCharOfLine, endOfWord - firstCharOfLine); + return isNumericVariable(word); + } + return false; +} + +bool ASBeautifier::isGitConflictMarker(std::string_view line) const +{ + if (isInComment || isInPreprocessorComment) + return false; + + size_t firstCharOfLine = line.find_first_not_of(" \t"); + if (firstCharOfLine == std::string::npos) + return false; + + std::string_view trimmedLine = line.substr(firstCharOfLine); + + // Check for git conflict markers + if (trimmedLine.length() >= 7) + { + if (trimmedLine.substr(0, 7) == ASResource::AS_OPEN_CONFLICT || + trimmedLine.substr(0, 7) == ASResource::AS_MIDDLE_CONFLICT || + trimmedLine.substr(0, 7) == ASResource::AS_CLOSE_CONFLICT) + { + return true; + } + } + + return false; +} + +bool ASBeautifier::handleColonSection(std::string_view line, size_t* i, bool tabIncrementIn, char* ch) +{ + if (line.length() > *i + 1 && line[*i + 1] == ':') // look for :: + { + ++*i; + return false; + } + else if (isInQuestion) // NOLINT + { + // do nothing special + } + else if (parenDepth > 0) + { + // found a 'for' loop or an objective-C statement + // so do nothing special + } + else if (isInEnum) + { + // found an enum with a base-type + isInEnumTypeID = true; + if (*i == 0){ + indentCount += classInitializerIndents; + // logIndent(34); + } + + } + else if ((isCStyle() || isSharpStyle()) + && !isInCase + && (prevNonSpaceCh == ')' || foundPreCommandHeader)) + { + // found a 'class' c'tor initializer + isInClassInitializer = true; + registerContinuationIndentColon(line, *i, tabIncrementIn); + if (*i == 0){ + indentCount += classInitializerIndents; + // logIndent(35); + } + + } + + else if (isInClassHeader || isInObjCInterface) + { + // is in a 'class A : public B' definition + isInClassHeaderTab = true; + registerContinuationIndentColon(line, *i, tabIncrementIn); + } + else if (isInAsm || isInAsmOneLine || isInAsmBlock) + { + // do nothing special + } + else if (isDigit(peekNextChar(line, *i)) || lineStartsWithNumericType(line)) + { + // found a bit field - do nothing special + } + // SF585 + GL91 + else if (isCStyle() && (isInClass || isInStruct) && prevNonSpaceCh != ')' && !isInCase) + { + // found a 'private:' or 'public:' inside a class definition + --indentCount; + // logIndent(36); + if (modifierIndent) + spaceIndentCount += (indentLength / 2); + } + // SF585 + else if (isCStyle() && !isInClass && !isInStruct + && headerStack->size() >= 2 + && (*headerStack)[headerStack->size() - 2] == &ASResource::AS_CLASS + && (*headerStack)[headerStack->size() - 1] == &ASResource::AS_OPEN_BRACE) + { + // found a 'private:' or 'public:' inside a class definition + // and on the same line as the class opening brace + // do nothing + } + else if (isJavaStyle() && lastLineHeader == &ASResource::AS_FOR) + { + // found a java for-each statement + // so do nothing special + } + // do not trigger this in class definitions https://gitlab.com/saalen/astyle/-/issues/4 + else if (isInStruct && !isInCase) + { + if (*i == 0){ + indentCount += classInitializerIndents; + // logIndent(37); + } + + } + else + { + currentNonSpaceCh = ';'; // so that braces after the ':' will appear as block-openers + char peekedChar = peekNextChar(line, *i); + if (isInCase) + { + isInCase = false; + *ch = ';'; // from here on, treat char as ';' + } + else if (isCStyle() || (isSharpStyle() && peekedChar == ';')) + { + // is in a label (e.g. 'label1:') + if (labelIndent) + --indentCount; // unindent label by one indent + else if (!lineBeginsWithOpenBrace){ + indentCount = 0; // completely flush indent to left + // logIndent(38); + } + + } + } + return true; +} + + +void ASBeautifier::handleEndOfStatement(size_t i, bool *closingBraceReached, char* ch) +{ + isInAssignment = isInInitializerList = false; + quoteContinuationIndent = 0; + if (*ch == '}') + { + // Improved lambda end handling with depth tracking + if ( !lambdaDepthStack->empty()) + { + // Check if this brace ends a lambda body + if (lambdaDepth && braceBlockStateStack->size() > 1 && braceBlockStateStack->back()) + { + // This was a lambda body, pop the lambda depth + lambdaDepthStack->pop_back(); + lambdaDepth--; + if (lambdaDepth == 0) + { + lambdaIndicator = false; + } + } + } + + // first check if this '}' closes a previous block, or a static array... + if (braceBlockStateStack->size() > 1) + { + bool braceBlockState = braceBlockStateStack->back(); + braceBlockStateStack->pop_back(); + if (!braceBlockState) + { + if (!continuationIndentStackSizeStack->empty()) + { + // this brace is a static array + popLastContinuationIndent(); + parenDepth--; + if (i == 0) + shouldIndentBracedLine = false; + + if (!parenIndentStack->empty()) + { + int poppedIndent = parenIndentStack->back(); + parenIndentStack->pop_back(); + if (i == 0) + spaceIndentCount = poppedIndent; + } + } + return; + } + } + + // this brace is block closer... + + ++lineClosingBlocksNum; + + if (!continuationIndentStackSizeStack->empty()) + popLastContinuationIndent(); + + if (!parenDepthStack->empty()) + { + parenDepth = parenDepthStack->back(); + parenDepthStack->pop_back(); + isContinuation = blockStatementStack->back(); + blockStatementStack->pop_back(); + + if (isContinuation) + blockTabCount--; + } + + *closingBraceReached = true; + if (i == 0) + spaceIndentCount = 0; + isInAsmBlock = false; + isInAsm = isInAsmOneLine = isInQuote = isInTemplate = false; // close these just in case + + //isInStruct = bracesNestingLevel <= 0; + if (!bracesNestingLevelOfStruct || !bracesNestingLevel || (bracesNestingLevelOfStruct > 0 && bracesNestingLevel <= bracesNestingLevelOfStruct)) { + isInStruct = false; + } + + int headerPlace = indexOf(*headerStack, &ASResource::AS_OPEN_BRACE); + if (headerPlace != -1) + { + const std::string* popped = headerStack->back(); + + while (popped != &ASResource::AS_OPEN_BRACE) + { + headerStack->pop_back(); + popped = headerStack->back(); + } + headerStack->pop_back(); + + if (headerStack->empty()) + g_preprocessorCppExternCBrace = 0; + + // do not indent namespace brace unless namespaces are indented + if (!namespaceIndent && !headerStack->empty() + && ((*headerStack).back() == &ASResource::AS_NAMESPACE + || (*headerStack).back() == &ASResource::AS_MODULE) + && i == 0) // must be the first brace on the line + shouldIndentBracedLine = false; + + if (!tempStacks->empty()) + { + std::vector* temp = tempStacks->back(); + tempStacks->pop_back(); + delete temp; + } + } + + *ch = ' '; // needed due to cases such as '}else{', so that headers ('else' in this case) will be identified... + } // ch == '}' + + /* + * Create a temporary snapshot of the current block's header-list in the + * uppermost inner stack in tempStacks, and clear the headerStack up to + * the beginning of the block. + * Thus, the next future statement will think it comes one indent past + * the block's '{' unless it specifically checks for a companion-header + * (such as a previous 'if' for an 'else' header) within the tempStacks, + * and recreates the temporary snapshot by manipulating the tempStacks. + */ + if (!tempStacks->back()->empty()) + while (!tempStacks->back()->empty()) + tempStacks->back()->pop_back(); + while (!headerStack->empty() && headerStack->back() != &ASResource::AS_OPEN_BRACE) + { + tempStacks->back()->emplace_back(headerStack->back()); + headerStack->pop_back(); + } + + if (parenDepth == 0 && *ch == ';') + { + isContinuation = false; + isInClassInitializer = false; + + if (lambdaDepth <= 1 || (lastLambdaDepth == lambdaDepth && lambdaDepthStack->size() == 0)){ + lambdaIndicator = false; + lambdaDepth = 0; + } + + lastLambdaDepth = lambdaDepth; + } + + if (isInObjCMethodDefinition) + { + objCColonAlignSubsequent = 0; + isImmediatelyPostObjCMethodDefinition = true; + } + + previousLastLineHeader = nullptr; + isInClassHeader = false; // for 'friend' class + isInEnum = false; + isInEnumTypeID = false; + + isInQuestion = false; + isInTemplate = false; + isInObjCInterface = false; + foundPreCommandHeader = false; + foundPreCommandMacro = false; + squareBracketCount = 0; +} + +void ASBeautifier::handleParens(std::string_view line, size_t i, bool tabIncrementIn, bool * isInOperator, char ch) +{ + + // GL28 xx + if (ch == '(' || ch == '[') + { + *isInOperator = false; + // if have a struct header, this is a declaration not a definition + if (ch == '(' + && !headerStack->empty() + && headerStack->back() == &ASResource::AS_STRUCT) + { + headerStack->pop_back(); + isInClassHeader = false; + + if (line.find("struct ", 0) > i) // if not on this line #526, GH #12 + indentCount -= classInitializerIndents; + if (indentCount < 0){ + indentCount = 0; + // logIndent(40); + + } + } + + if (parenDepth == 0) + { + // do not use emplace_back on vector until supported by macOS + parenStatementStack->push_back(isContinuation); + isContinuation = true; + } + parenDepth++; + if (ch == '[') + { + ++squareBracketCount; + // #525 Maybe check for opening brace in the line + if (squareBracketCount == 1 && isObjCStyle() && line.find('{', i + 1 ) == std::string::npos) + { + isInObjCMethodCall = true; + isInObjCMethodCallFirst = true; + } + + // #121 - Improved lambda detection + if (isCStyle() && attemptLambdaIndentation) + { + // Check for lambda capture list patterns + bool isLambdaCapture = false; + + // More robust lambda detection conditions + if (prevNonSpaceCh == '=' || prevNonSpaceCh == '(' || prevNonSpaceCh == ',' + || prevNonSpaceCh == '{' || prevNonSpaceCh == ';' || prevNonSpaceCh == ':') + { + isLambdaCapture = true; + } + // Check for lambda after assignment operators + else if (!isLegalNameChar(prevNonSpaceCh) && prevNonSpaceCh != ']' && prevNonSpaceCh != ')') + { + isLambdaCapture = true; + } + // Check for lambda in return statements + else if (prevNonSpaceCh == 'n' && i >= 6 && line.substr(i-6, 6) == "return") + { + isLambdaCapture = true; + } + + if (isLambdaCapture) + { + lambdaIndicator = true; + lambdaDepth++; + + lambdaDepthStack->push_back(i); + lambdaEndIndent = lambdaStartIndent = i; + } + } + } + + continuationIndentStackSizeStack->emplace_back(continuationIndentStack->size()); + if (currentHeader != nullptr) + registerContinuationIndent(line, i, spaceIndentCount, tabIncrementIn, minConditionalIndent, true); + else if (!isInObjCMethodDefinition + //&& xxxCondition && shouldForceTabIndentation // only count one opening parentheses per line #498 + ) + registerContinuationIndent(line, i, spaceIndentCount, tabIncrementIn, 0, true); + } + else if (ch == ')' || ch == ']') + { + if (ch == ']') + --squareBracketCount; + + if (squareBracketCount <= 0) + { + squareBracketCount = 0; + if (isInObjCMethodCall) + isImmediatelyPostObjCMethodCall = true; + } + foundPreCommandHeader = false; + parenDepth--; + + if (parenDepth == 0) + { + if (!parenStatementStack->empty()) // in case of unmatched closing parens + { + isContinuation = parenStatementStack->back(); + parenStatementStack->pop_back(); + } + isInAsm = false; + isInConditional = false; + } + + if (!continuationIndentStackSizeStack->empty()) + { + popLastContinuationIndent(); + + if (!parenIndentStack->empty()) + { + int poppedIndent = parenIndentStack->back(); + parenIndentStack->pop_back(); + + if (i == 0) + spaceIndentCount = poppedIndent; + } + } + } +} + +void ASBeautifier::handleClosingParen(std::string_view line, size_t i, bool tabIncrementIn) +{ + // first, check if '{' is a block-opener or a static-array opener + + bool isBlockOpener = ((prevNonSpaceCh == '{' && braceBlockStateStack->back()) + || prevNonSpaceCh == '}' + || prevNonSpaceCh == ')' + || prevNonSpaceCh == ';' + || peekNextChar(line, i) == '{' + || isInTrailingReturnType + || foundPreCommandHeader + || foundPreCommandMacro + || isInClassHeader + || (isInClassInitializer && !isLegalNameChar(prevNonSpaceCh)) + || (isNonInStatementArray && !isInClassInitializer) + || isInObjCMethodDefinition + || isInObjCInterface + || isSharpAccessor + || isSharpDelegate + || isInExternC + || isInAsmBlock + //|| getNextWord(line, i) == ASResource::AS_NEW // #487 + || (isInDefine + && (prevNonSpaceCh == '(' || prevNonSpaceCh == '=' + || isLegalNameChar(prevNonSpaceCh)))); + + if (isInObjCMethodDefinition) + { + objCColonAlignSubsequent = 0; + isImmediatelyPostObjCMethodDefinition = true; + if (lineBeginsWithOpenBrace) // for run-in braces + clearObjCMethodDefinitionAlignment(); + } + + // !isInStruct omitted: fix for https://gitlab.com/saalen/astyle/-/issues/3 + if (!isBlockOpener && !isContinuation && !isInClassInitializer && !isInEnum /*&& !isInStruct*/) + { + if (isTopLevel()) + isBlockOpener = true; + } + + // GL28 fix initializer lists like x({a.x=0;}) + + isInInitializerList = isCStyle() && isBlockOpener && (prevNonSpaceCh == '(' || prevNonSpaceCh == '='); + + if (!isBlockOpener && currentHeader != nullptr) + { + for (const std::string* const nonParenHeader : *nonParenHeaders) + if (currentHeader == nonParenHeader) + { + isBlockOpener = true; + break; + } + } + + // do not use emplace_back on vector until supported by macOS + braceBlockStateStack->push_back(isBlockOpener); + + if (!isBlockOpener) + { + continuationIndentStackSizeStack->emplace_back(continuationIndentStack->size()); + registerContinuationIndent(line, i, spaceIndentCount, tabIncrementIn, 0, true); + parenDepth++; + if (i == 0) + shouldIndentBracedLine = false; + isInEnumTypeID = false; + + return ; + } + + if (isBlockOpener && lambdaDepth){ + lambdaDepth++; + } + + // this brace is a block opener... + + ++lineOpeningBlocksNum; + + if (isInClassInitializer || isInEnumTypeID) + { + // decrease tab count if brace is broken + if (lineBeginsWithOpenBrace) + { + indentCount -= classInitializerIndents; + // logIndent(41); + // decrease one more if an empty class + if (!headerStack->empty() + && (*headerStack).back() == &ASResource::AS_CLASS) + { + int nextChar = getNextProgramCharDistance(line, i); + if ((int) line.length() > nextChar && line[nextChar] == '}'){ + --indentCount; + // logIndent(42); + } + + } + } + } + + if (isInObjCInterface) + { + isInObjCInterface = false; + if (lineBeginsWithOpenBrace){ + --indentCount; + // logIndent(43); + } + + } + + if (braceIndent && !namespaceIndent && !headerStack->empty() + && ((*headerStack).back() == &ASResource::AS_NAMESPACE + || (*headerStack).back() == &ASResource::AS_MODULE)) + { + shouldIndentBracedLine = false; + --indentCount; + // logIndent(44); + } + + // an indentable struct is treated like a class in the header stack + if (!headerStack->empty() + && (*headerStack).back() == &ASResource::AS_STRUCT + && isInIndentableStruct) + (*headerStack).back() = &ASResource::AS_CLASS; + + // is a brace inside a paren? + parenDepthStack->emplace_back(parenDepth); + // do not use emplace_back on vector until supported by macOS + blockStatementStack->push_back(isContinuation); + + if (!continuationIndentStack->empty()) + { + // completely purge the continuationIndentStack + while (!continuationIndentStack->empty()) + popLastContinuationIndent(); + if (isInClassInitializer || isInClassHeaderTab) + { + if (lineBeginsWithOpenBrace || lineBeginsWithComma) + spaceIndentCount = 0; + } + else + spaceIndentCount = 0; + } + + blockTabCount += (isContinuation ? 1 : 0); + if (g_preprocessorCppExternCBrace == 3) + ++g_preprocessorCppExternCBrace; + parenDepth = 0; + isInTrailingReturnType = false; + isInClassHeader = false; + isInClassHeaderTab = false; + isInClassInitializer = false; + isInEnumTypeID = false; + isContinuation = false; + isInQuestion = false; + isInLet = false; + foundPreCommandHeader = false; + foundPreCommandMacro = false; + isInExternC = false; + + tempStacks->emplace_back(new std::vector); + headerStack->emplace_back(&ASResource::AS_OPEN_BRACE); + lastLineHeader = &ASResource::AS_OPEN_BRACE; +} + +void ASBeautifier::handlePotentialHeaderSection(std::string_view line, size_t* i, bool tabIncrementIn, bool *isInOperator) +{ + // check for preBlockStatements in C/C++ ONLY if not within parentheses + // (otherwise 'struct XXX' statements would be wrongly interpreted...) + if (!isInTemplate && !(isCStyle() && parenDepth > 0)) + { + const std::string* newHeader = findHeader(line, *i, preBlockStatements); + // CORBA IDL module + if (newHeader == &ASResource::AS_MODULE) + { + char nextChar = peekNextChar(line, *i + newHeader->length() - 1); + if (prevNonSpaceCh == ')' || !isalpha(nextChar)) + newHeader = nullptr; + } + + if (newHeader != nullptr + && !(isCStyle() && newHeader == &ASResource::AS_CLASS && (isInEnum || isInStruct)) // is not 'enum class' + && !(isCStyle() && newHeader == &ASResource::AS_INTERFACE // CORBA IDL interface + && (headerStack->empty() + || headerStack->back() != &ASResource::AS_OPEN_BRACE))) + { + if (!isSharpStyle()) + headerStack->emplace_back(newHeader); + // do not need 'where' in the headerStack + // do not need second 'class' statement in a row + else if (!(newHeader == &ASResource::AS_WHERE + || ((newHeader == &ASResource::AS_CLASS || newHeader == &ASResource::AS_STRUCT) + && !headerStack->empty() + && (headerStack->back() == &ASResource::AS_CLASS + || headerStack->back() == &ASResource::AS_STRUCT)))) + headerStack->emplace_back(newHeader); + + if (!headerStack->empty()) + { + if ((*headerStack).back() == &ASResource::AS_CLASS + || (*headerStack).back() == &ASResource::AS_STRUCT + || (*headerStack).back() == &ASResource::AS_INTERFACE) + { + isInClassHeader = true; + } + else if ((*headerStack).back() == &ASResource::AS_NAMESPACE + || (*headerStack).back() == &ASResource::AS_MODULE) + { + // remove continuationIndent from namespace + if (!continuationIndentStack->empty()) + continuationIndentStack->pop_back(); + isContinuation = false; + } + } + + *i += newHeader->length() - 1; + return; + } + } + const std::string* foundIndentableHeader = findHeader(line, *i, indentableHeaders); + + if (foundIndentableHeader != nullptr) + { + // must bypass the header before registering the in statement + *i += foundIndentableHeader->length() - 1; + if (!*isInOperator && !isInTemplate && !isNonInStatementArray) + { + registerContinuationIndent(line, *i, spaceIndentCount, tabIncrementIn, 0, false); + isContinuation = true; + } + return; + } + + if (isCStyle() && findKeyword(line, *i, ASResource::AS_OPERATOR)) + *isInOperator = true; + + if (g_preprocessorCppExternCBrace == 1 && findKeyword(line, *i, ASResource::AS_EXTERN)) + ++g_preprocessorCppExternCBrace; + + if (g_preprocessorCppExternCBrace == 3) // extern "C" is not followed by a '{' + g_preprocessorCppExternCBrace = 0; + + // "new" operator is a pointer, not a calculation + if (findKeyword(line, *i, ASResource::AS_NEW)) + { + if (isContinuation && !continuationIndentStack->empty() && prevNonSpaceCh == '=') + continuationIndentStack->back() = 0; + } + + if (isCStyle() && findKeyword(line, *i, ASResource::AS_AUTO) && isTopLevel()) + { + isInTrailingReturnType = true; + } + + if (isCStyle()) + { + if (findKeyword(line, *i, ASResource::AS_ASM) + || findKeyword(line, *i, ASResource::AS__ASM__)) + { + isInAsm = true; + } + else if (findKeyword(line, *i, ASResource::AS_MS_ASM) // microsoft specific + || findKeyword(line, *i, ASResource::AS_MS__ASM)) + { + int index = 4; + if (peekNextChar(line, *i) == '_') // check for __asm + index = 5; + + char peekedChar = peekNextChar(line, *i + index); + if (peekedChar == '{' || peekedChar == ' ') + isInAsmBlock = true; + else + isInAsmOneLine = true; + } + } + + // bypass the entire name for all others + std::string_view name = getCurrentWord(line, *i); + *i += name.length() - 1; +} + + +void ASBeautifier::handlePotentialOperatorSection(std::string_view line, size_t* i, bool tabIncrementIn, bool haveAssignmentThisLine, bool isInOperator) +{ + // Check if an operator has been reached. + const std::string* foundAssignmentOp = findOperator(line, *i, assignmentOperators); + const std::string* foundNonAssignmentOp = findOperator(line, *i, nonAssignmentOperators); + + if (foundNonAssignmentOp != nullptr) + { + if (foundNonAssignmentOp == &ASResource::AS_LAMBDA) + foundPreCommandHeader = true; + if (isInTemplate && foundNonAssignmentOp == &ASResource::AS_GR_GR) + foundNonAssignmentOp = nullptr; + } + + // Since findHeader's boundary checking was not used above, it is possible + // that both an assignment op and a non-assignment op where found, + // e.g. '>>' and '>>='. If this is the case, treat the LONGER one as the + // found operator. + if (foundAssignmentOp != nullptr && foundNonAssignmentOp != nullptr) + { + if (foundAssignmentOp->length() < foundNonAssignmentOp->length()) + foundAssignmentOp = nullptr; + else + foundNonAssignmentOp = nullptr; + } + + if (foundNonAssignmentOp != nullptr) + { + if (foundNonAssignmentOp->length() > 1) + *i += foundNonAssignmentOp->length() - 1; + + // For C++ input/output, operator<<, >> and . method calls should be + // aligned, if we are not in a statement already and + // also not in the "operator<<(...)" header line + + // GL28: method calls have to contain only alphanumeric identifier + size_t openParenPos = line.find(ASResource::AS_OPEN_PAREN, *i); + size_t closeParenPos = line.find(ASResource::AS_CLOSE_PAREN, openParenPos); + + std::string methodName = getNextWord(std::string(line), *i); + size_t methodNameEndPos = *i + methodName.length() + 1; + size_t firstCharAfterMethod = line.substr(*i + methodName.length() + 1).find_first_not_of(" \t"); + if (firstCharAfterMethod != std::string::npos) + { + methodNameEndPos += firstCharAfterMethod; + } + + size_t firstCharOfLine = line.find_first_not_of(" \t"); + bool lineStartsWithDot = false; + if (firstCharOfLine != std::string::npos) + { + lineStartsWithDot = line[firstCharOfLine] == '.'; + } + + // GL28: do not mixup template closing ">>" with IO operator + std::string searchTemplatePattern = std::string(line).substr(0, *i); + size_t countLS = std::count_if( searchTemplatePattern.begin(), searchTemplatePattern.end(), []( char c ) {return c == '<';}); + + if (!isInOperator + && continuationIndentStack->empty() + && isCStyle() + && !lineStartsWithDot + && ( (foundNonAssignmentOp == &ASResource::AS_GR_GR && countLS < 2 ) + || foundNonAssignmentOp == &ASResource::AS_LS_LS + || (foundNonAssignmentOp == &ASResource::AS_DOT && openParenPos == methodNameEndPos && closeParenPos != std::string::npos ))) + { + // this will be true if the line begins with the operator + if (*i < foundNonAssignmentOp->length() && spaceIndentCount == 0) + spaceIndentCount += 2 * indentLength; + // align to the beginning column of the operator + registerContinuationIndent(line, *i - foundNonAssignmentOp->length(), spaceIndentCount, tabIncrementIn, 0, false); + } + } + + else if (foundAssignmentOp != nullptr) + { + + isInAssignment = true; + + foundPreCommandHeader = false; // clears this for array assignments + foundPreCommandMacro = false; + + if (foundAssignmentOp->length() > 1) + *i += foundAssignmentOp->length() - 1; + + if (!isInOperator && !isInTemplate && (!isNonInStatementArray || isInEnum || isInStruct)) + { + // if multiple assignments, align on the previous word + if (foundAssignmentOp == &ASResource::AS_ASSIGN + && prevNonSpaceCh != ']' // an array + && statementEndsWithComma(line, *i)) + { + // only one assignment indent per line + GH #10 + if (!haveAssignmentThisLine && line.find(ASResource::AS_SCOPE_RESOLUTION) == std::string::npos) + { + // register indent at previous word + haveAssignmentThisLine = true; + int prevWordIndex = getContinuationIndentAssign(line, *i); + int continuationIndentCount = prevWordIndex + spaceIndentCount + tabIncrementIn; + continuationIndentStack->emplace_back(continuationIndentCount); + isContinuation = true; + } + } + // don't indent an assignment if 'let' + else if (isInLet) + { + isInLet = false; + } + else if (!lineBeginsWithComma && !isInDefine) + { + if (*i == 0 && spaceIndentCount == 0) + spaceIndentCount += indentLength; + + // #SF 97 + if (prevNonLegalCh == '=' && currentNonLegalCh == '=') + spaceIndentCount = 0; + + registerContinuationIndent(line, *i, spaceIndentCount, tabIncrementIn, 0, false); + isContinuation = true; + } + } + } + +} + + +/** + * Parse the current line to update indentCount and spaceIndentCount. + */ +void ASBeautifier::parseCurrentLine(std::string_view line) +{ + bool isInLineComment = false; + bool isInOperator = false; + bool isSpecialChar = false; + bool haveCaseIndent = false; + bool haveAssignmentThisLine = false; + bool closingBraceReached = false; + bool previousLineProbation = (probationHeader != nullptr); + char ch = ' '; + int tabIncrementIn = 0; + + + /*if (isInQuote + && !haveLineContinuationChar + && !isInVerbatimQuote + && !isInAsm + && !isInMultiLineString) + isInQuote = false; // missing closing quote + */ + + haveLineContinuationChar = false; + + for (size_t i = 0; i < line.length(); i++) + { + ch = line[i]; + + if (isInBeautifySQL) + continue; + + bool isTripleQuoteDelimiter = (isJavaStyle() || isSharpStyle() ) && line.length() > i + 2 && line[i + 1] == '"' && line[i + 2 ] == '"'; + + // handle special characters (i.e. backslash+character such as \n, \t, ...) + if (isInQuote && !isInVerbatimQuote) + { + if (isSpecialChar) + { + isSpecialChar = false; + continue; + } + if (line.compare(i, 2, "\\\\") == 0) + { + i++; + continue; + } + if (ch == '\\') + { + if (peekNextChar(line, i) == ' ') // is this '\' at end of line + haveLineContinuationChar = true; + else + isSpecialChar = true; + continue; + } + } + else if (isInDefine && ch == '\\') + continue; + + // bypass whitespace here + if (std::isblank(ch)) + { + size_t wsSpanEnd = line.find_first_not_of(" \t", i + 1); + bool nextTokenIsComment = false; + if (wsSpanEnd != std::string::npos && line.length() > wsSpanEnd + 2) + { + nextTokenIsComment = (line.compare(wsSpanEnd, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0 + || line.compare(wsSpanEnd, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(wsSpanEnd, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0); + } + if (squeezeWhitespace && !isInComment && !isInQuote && !nextTokenIsComment && std::isblank(line[i + 1]) && !std::isblank(line[i - 1])) + { + + std::pair wsSpan(i, wsSpanEnd - i - 1); + squeezeWSStack.emplace_back(wsSpan); + } + + if (ch == '\t') + tabIncrementIn += convertTabToSpaces(i, tabIncrementIn); + continue; + } + + // handle quotes (such as 'x' and "Hello Dolly") + if (!(isInComment || isInLineComment) + && (ch == '"' + || (ch == '\'' && !isDigitSeparator(line, i)))) + { + if (!i && quoteContinuationIndent) + { + spaceIndentCount = quoteContinuationIndent; + } + if (!isInQuote && !isInMultiLineString) + { + quoteChar = ch; + isInQuote = true; + isInMultiLineString = isTripleQuoteDelimiter; + + char prevCh = i > 0 ? line[i - 1] : ' '; + char prevPrevCh = i > 1 ? line[i - 2] : ' '; + + // GL 32 + // https://sourceforge.net/p/astyle/bugs/535/ + // https://gitlab.com/saalen/astyle/-/issues/82 + if (isCStyle() && prevCh == 'R' && !isalpha(prevPrevCh) && !isalpha(prevNonSpaceCh)) + { + int parenPos = line.find('(', i); + + if (parenPos != -1) + { + isInVerbatimQuote = true; + const auto lineSubstr = line.substr(i + 1, parenPos - i - 1); + verbatimDelimiter = std::string(lineSubstr.data(), lineSubstr.length()); + } + } + else if (isSharpStyle() && prevCh == '@') + isInVerbatimQuote = true; + // check for "C" following "extern" + else if (g_preprocessorCppExternCBrace == 2 && line.compare(i, 3, "\"C\"") == 0) + ++g_preprocessorCppExternCBrace; + } + else if (isInVerbatimQuote && ch == '"') + { + if (isCStyle()) + { + std::string delim = ')' + verbatimDelimiter; + int delimStart = i - delim.length(); + + //only stop if no macro is following the end delimiter + size_t firstWord = line.find_first_not_of(" \t", i + 1); + + if (!isalpha(line[firstWord]) && delimStart >= 0 && line.substr(delimStart, delim.length()) == delim) + { + isInQuote = false; + isInVerbatimQuote = false; + } + } + else if (isSharpStyle()) + { + if (line.compare(i, 2, "\"\"") == 0) + { + i++; + } + else + { + isInQuote = false; + isInVerbatimQuote = false; + continue; + } + } + } + else if (isTripleQuoteDelimiter && isInMultiLineString) + { + isInMultiLineString = false; + isInQuote = false; + isContinuation = true; + continue; + } + else if (quoteChar == ch) + { + isInQuote = false; + isContinuation = true; + continue; + } + } + if (isInQuote) + continue; + + // handle comments + + if (!(isInComment || isInLineComment) && line.compare(i, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0) + { + // if there is a 'case' statement after these comments unindent by 1 + if (isCaseHeaderCommentIndent){ + --indentCount; + // logIndent(45, line); + + } + // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested + // if there is an 'else' after these comments a tempStacks indent is required + if (isElseHeaderIndent && lineOpensWithLineComment && !tempStacks->empty()){ + indentCount += adjustIndentCountForBreakElseIfComments(); + // logIndent(46, line); + } + + isInLineComment = true; + i++; + continue; + } + if (!(isInComment || isInLineComment) + && (line.compare(i, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(i, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0)) + { + // if there is a 'case' statement after these comments unindent by 1 + if (isCaseHeaderCommentIndent && lineOpensWithComment){ + --indentCount; + // logIndent(47, line); + } + + // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested + // if there is an 'else' after these comments a tempStacks indent is required + if (isElseHeaderIndent && lineOpensWithComment && !tempStacks->empty()){ + indentCount += adjustIndentCountForBreakElseIfComments(); + // logIndent(48, line); + } + + isInComment = true; + i++; + if (!lineOpensWithComment) // does line start with comment? + blockCommentNoIndent = true; // if no, cannot indent continuation lines + continue; + } + if ((isInComment || isInLineComment) + && (line.compare(i, ASResource::AS_CLOSE_COMMENT.length(), ASResource::AS_CLOSE_COMMENT) == 0 + || line.compare(i, ASResource::AS_GSC_CLOSE_COMMENT.length(), ASResource::AS_GSC_CLOSE_COMMENT) == 0)) + { + size_t firstText = line.find_first_not_of(" \t"); + // if there is a 'case' statement after these comments unindent by 1 + // only if the ending comment is the first entry on the line + if (isCaseHeaderCommentIndent && firstText == i){ + --indentCount; + // logIndent(49, line); + } + + // if this comment close starts the line, must check for else-if indent + // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested + // if there is an 'else' after these comments a tempStacks indent is required + if (firstText == i) + { + if (isElseHeaderIndent && !lineOpensWithComment && !tempStacks->empty()){ + indentCount += adjustIndentCountForBreakElseIfComments(); + // logIndent(50, line); + } + + } + isInComment = false; + i++; + blockCommentNoIndent = false; // ok to indent next comment + continue; + } + // treat indented preprocessor lines as a line comment + if (line[0] == '#' && isIndentedPreprocessor(line, i)) + { + isInLineComment = true; + } + + if (isInLineComment) + { + // bypass rest of the comment up to the comment end + while (i + 1 < line.length()) + i++; + + continue; + } + if (isInComment) + { + // if there is a 'case' statement after these comments unindent by 1 + if (!lineOpensWithComment && isCaseHeaderCommentIndent){ + --indentCount; + // logIndent(51, line); + } + + // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested + // if there is an 'else' after these comments a tempStacks indent is required + if (!lineOpensWithComment && isElseHeaderIndent && !tempStacks->empty()){ + indentCount += adjustIndentCountForBreakElseIfComments(); + // logIndent(52, line); + } + + // bypass rest of the comment up to the comment end + while (i + 1 < line.length() + && line.compare(i + 1, ASResource::AS_CLOSE_COMMENT.length(), ASResource::AS_CLOSE_COMMENT) != 0) + i++; + + continue; + } + + // if we have reached this far then we are NOT in a comment or std::string of special character... + + if (probationHeader != nullptr) + { + if ((probationHeader == &ASResource::AS_STATIC && ch == '{') + || (probationHeader == &ASResource::AS_SYNCHRONIZED && ch == '(')) + { + // insert the probation header as a new header + isInHeader = true; + headerStack->emplace_back(probationHeader); + + // handle the specific probation header + isInConditional = (probationHeader == &ASResource::AS_SYNCHRONIZED); + + isContinuation = false; + // if the probation comes from the previous line, then indent by 1 tab count. + if (previousLineProbation + && ch == '{' + && !(blockIndent && probationHeader == &ASResource::AS_STATIC)) + { + ++indentCount; + // logIndent(53, line); + previousLineProbationTab = true; + } + previousLineProbation = false; + } + + // dismiss the probation header + probationHeader = nullptr; + } + + prevNonSpaceCh = currentNonSpaceCh; + currentNonSpaceCh = ch; + + // #SF 97 + if (!isLegalNameChar(ch) /*&& ch != ',' && ch != ';'*/) + { + prevNonLegalCh = currentNonLegalCh; + currentNonLegalCh = ch; + } + + if (isInHeader) + { + isInHeader = false; + currentHeader = headerStack->back(); + } + else + currentHeader = nullptr; + + if (isCStyle() && isInTemplate + && (ch == '<' || ch == '>') + && !(line.length() > i + 1 && line.compare(i, ASResource::AS_GR_EQUAL.length(), ASResource::AS_GR_EQUAL) == 0)) + { + if (ch == '<') + { + ++templateDepth; + continuationIndentStackSizeStack->emplace_back(continuationIndentStack->size()); + registerContinuationIndent(line, i, spaceIndentCount, tabIncrementIn, 0, true); + } + else if (ch == '>') + { + popLastContinuationIndent(); + if (--templateDepth <= 0) + { + ch = ';'; + isInTemplate = false; + templateDepth = 0; + } + } + } + + size_t eqPos = line.find('=', i); + size_t openParenPos = line.find('(', i + 1); + // GL 62 + if (eqPos != std::string::npos && openParenPos != std::string::npos) + { + isInInitializerList = false; + } + + // handle parentheses + if ((ch == '(' && !isInInitializerList) || ch == '[' || ch == ')' || ch == ']') + { + handleParens(line, i, tabIncrementIn, &isInOperator, ch); + continue; + } + + if (ch == '{') + { + + handleClosingParen(line, i, tabIncrementIn); + continue; + } // end '{' + + //check if a header has been reached + bool isPotentialHeader = isCharPotentialHeader(line, i); + + if (isPotentialHeader && squareBracketCount == 0) + { + + if (!handleHeaderSection(line, &i, closingBraceReached, &haveCaseIndent)) + continue; + + } // isPotentialHeader + + if (ch == '?') + isInQuestion = true; + + // special handling of colons XXX 533 + if (ch == ':') + { + if (!handleColonSection(line, &i, tabIncrementIn, &ch)) + continue; + } + + if ((ch == ';' || (parenDepth > 0 && ch == ',')) && !continuationIndentStackSizeStack->empty()) + { + while ((int) continuationIndentStackSizeStack->back() + (parenDepth > 0 ? 1 : 0) + < (int) continuationIndentStack->size()) + continuationIndentStack->pop_back(); + } + else if (ch == ',' && (isInEnum || isInStruct) && isNonInStatementArray && !continuationIndentStack->empty()) + continuationIndentStack->pop_back(); + + // handle commas + // previous "isInStatement" will be from an assignment operator or class initializer + if (ch == ',' && parenDepth == 0 && !isContinuation && !isNonInStatementArray) + { + // is comma at end of line + size_t nextChar = line.find_first_not_of(" \t", i + 1); + if (nextChar != std::string::npos) + { + if (line.compare(nextChar, ASResource::AS_OPEN_LINE_COMMENT.length(), ASResource::AS_OPEN_LINE_COMMENT) == 0 + || line.compare(nextChar, ASResource::AS_OPEN_COMMENT.length(), ASResource::AS_OPEN_COMMENT) == 0 + || line.compare(nextChar, ASResource::AS_GSC_OPEN_COMMENT.length(), ASResource::AS_GSC_OPEN_COMMENT) == 0) + nextChar = std::string::npos; + } + // register indent + if (nextChar == std::string::npos) + { + // register indent at previous word + if (isJavaStyle() && isInClassHeader) + { + // do nothing for now + } + // register indent at second word on the line + else if (!isInTemplate && !isInClassHeaderTab && !isInClassInitializer) + { + int prevWord = getContinuationIndentComma(line, i); + int continuationIndentCount = prevWord + spaceIndentCount + tabIncrementIn; + continuationIndentStack->emplace_back(continuationIndentCount); + isContinuation = true; + } + } + } + // handle comma first initializers + if (ch == ',' && parenDepth == 0 && lineBeginsWithComma + && (isInClassInitializer || isInClassHeaderTab)) + spaceIndentCount = 0; + + // handle ends of statements + if ((ch == ';' && parenDepth == 0) || ch == '}') + { + handleEndOfStatement(i, &closingBraceReached, &ch); + continue; + } + + if (isPotentialHeader) + { + handlePotentialHeaderSection(line, &i, tabIncrementIn, &isInOperator); + continue; + } + + // Handle Objective-C statements + + if (ch == '@' + && isObjCStyle() + && line.length() > i + 1 + && !std::isblank(line[i + 1]) + && isCharPotentialHeader(line, i + 1)) + { + std::string_view curWord = getCurrentWord(line, i + 1); + if (curWord == ASResource::AS_INTERFACE || curWord == ASResource::AS_AUTORELEASEPOOL) + { + isInObjCInterface = true; + std::string name = '@' + std::string(curWord); + i += name.length() - 1; + continue; + } + + if (isInObjCInterface) + { + --indentCount; + // logIndent(54, line); + isInObjCInterface = false; + } + + if (curWord == ASResource::AS_PUBLIC + || curWord == ASResource::AS_PRIVATE + || curWord == ASResource::AS_PROTECTED) + { + --indentCount; + // logIndent(55, line); + if (modifierIndent) + spaceIndentCount += (indentLength / 2); + std::string name = '@' + std::string(curWord); + i += name.length() - 1; + continue; + } + + if (curWord == ASResource::AS_END) + { + popLastContinuationIndent(); + spaceIndentCount = 0; + isInObjCMethodDefinition = false; + std::string name = '@' + std::string(curWord); + i += name.length() - 1; + continue; + } + } + else if ((ch == '-' || ch == '+') + && (prevNonSpaceCh == ';' || prevNonSpaceCh == '{' + || headerStack->empty() || isInObjCInterface) + && ASBase::peekNextChar(line, i) != '-' + && ASBase::peekNextChar(line, i) != '+' + && isObjCStyle() + && line.find_first_not_of(" \t") == i) + { + if (isInObjCInterface){ + --indentCount; + // logIndent(56, line); + } + + isInObjCInterface = false; + isInObjCMethodDefinition = true; + continue; + } + + // Handle operators + + bool isPotentialOperator = isCharPotentialOperator(ch); + + if (isPotentialOperator) + { + handlePotentialOperatorSection(line, &i, tabIncrementIn, haveAssignmentThisLine, isInOperator); + } + } // end of for loop * end of for loop * end of for loop * end of for loop * end of for loop * +} + + +} // end namespace astyle diff --git a/astyle/AStyle/src/ASEnhancer.cpp b/astyle/AStyle/src/ASEnhancer.cpp new file mode 100644 index 0000000..ae6da90 --- /dev/null +++ b/astyle/AStyle/src/ASEnhancer.cpp @@ -0,0 +1,775 @@ +// ASEnhancer.cpp +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +//----------------------------------------------------------------------------- +// headers +//----------------------------------------------------------------------------- + +#include "astyle.h" +#include + +//----------------------------------------------------------------------------- +// astyle namespace +//----------------------------------------------------------------------------- + +namespace astyle { +// +//----------------------------------------------------------------------------- +// ASEnhancer class +//----------------------------------------------------------------------------- + +/** + * initialize the ASEnhancer. + * + * init() is called each time an ASFormatter object is initialized. + */ +void ASEnhancer::init(int _fileType, + int _indentLength, + int _tabLength, + bool _useTabs, + bool _forceTab, + bool _namespaceIndent, + bool _caseIndent, + bool _preprocBlockIndent, + bool _preprocDefineIndent, + bool _emptyLineFill, + std::vector* >* _indentableMacros) +{ + // formatting variables from ASFormatter and ASBeautifier + ASBase::init(_fileType); + indentLength = _indentLength; + tabLength = _tabLength; + useTabs = _useTabs; + forceTab = _forceTab; + namespaceIndent = _namespaceIndent; + caseIndent = _caseIndent; + preprocBlockIndent = _preprocBlockIndent; + preprocDefineIndent = _preprocDefineIndent; + emptyLineFill = _emptyLineFill; + indentableMacros = _indentableMacros; + quoteChar = '\''; + + // unindent variables + lineNumber = 0; + braceCount = 0; + isInComment = false; + isInQuote = false; + switchDepth = 0; + eventPreprocDepth = 0; + lookingForCaseBrace = false; + unindentNextLine = false; + shouldUnindentLine = false; + shouldUnindentComment = false; + + // switch struct and vector + sw.switchBraceCount = 0; + sw.unindentDepth = 0; + sw.unindentCase = false; + switchStack.clear(); + + // other variables + nextLineIsEventIndent = false; + isInEventTable = false; + nextLineIsDeclareIndent = false; + isInDeclareSection = false; +} + +/** + * additional formatting for line of source code. + * every line of source code in a source code file should be sent + * one after the other to this function. + * indents event tables + * unindents the case blocks + * + * @param line the original formatted line will be updated if necessary. + */ +void ASEnhancer::enhance(std::string& line, bool isInNamespace, bool isInPreprocessor, bool isInSQL) +{ + shouldUnindentLine = true; + shouldUnindentComment = false; + lineNumber++; + + // check for beginning of event table + if (nextLineIsEventIndent) + { + isInEventTable = true; + nextLineIsEventIndent = false; + } + + // check for beginning of SQL declare section + if (nextLineIsDeclareIndent) + { + isInDeclareSection = true; + nextLineIsDeclareIndent = false; + } + + if (line.empty() + && !isInEventTable + && !isInDeclareSection + && !emptyLineFill) + return; + + // test for unindent on attached braces + if (unindentNextLine) + { + sw.unindentDepth++; + sw.unindentCase = true; + unindentNextLine = false; + } + + // parse characters in the current line + parseCurrentLine(line, isInPreprocessor, isInSQL); + + // check for SQL indentable lines + if (isInDeclareSection) + { + size_t firstText = line.find_first_not_of(" \t"); + if (firstText == std::string::npos || line[firstText] != '#') + indentLine(line, 1); + } + + // check for event table indentable lines + if (isInEventTable + && (eventPreprocDepth == 0 + || (namespaceIndent && isInNamespace))) + { + size_t firstText = line.find_first_not_of(" \t"); + if (firstText == std::string::npos || line[firstText] != '#') + indentLine(line, 1); + } + + if (shouldUnindentComment && sw.unindentDepth > 0) + unindentLine(line, sw.unindentDepth - 1); + else if (shouldUnindentLine && sw.unindentDepth > 0) + unindentLine(line, sw.unindentDepth); +} + +/** + * convert a force-tab indent to spaces + * + * @param line a reference to the line that will be converted. + */ +void ASEnhancer::convertForceTabIndentToSpaces(std::string& line) const +{ + // replace tab indents with spaces + for (size_t i = 0; i < line.length(); i++) + { + if (!std::isblank(line[i])) + break; + if (line[i] == '\t') + { + line.erase(i, 1); + line.insert(i, tabLength, ' '); + i += tabLength - 1; + } + } +} + +/** + * convert a space indent to force-tab + * + * @param line a reference to the line that will be converted. + */ +void ASEnhancer::convertSpaceIndentToForceTab(std::string& line) const +{ + assert(tabLength > 0); + + // replace leading spaces with tab indents + size_t newSpaceIndentLength = line.find_first_not_of(" \t"); + size_t tabCount = newSpaceIndentLength / tabLength; // truncate extra spaces + line.replace(0U, tabCount * tabLength, tabCount, '\t'); +} + +/** + * find the colon following a 'case' statement + * + * @param line a reference to the line. + * @param caseIndex the line index of the case statement. + * @return the line index of the colon. + */ +size_t ASEnhancer::findCaseColon(std::string_view line, size_t caseIndex) const +{ + size_t i = caseIndex; + bool isInQuote_ = false; + char quoteChar_ = ' '; + for (; i < line.length(); i++) + { + if (isInQuote_) + { + if (line[i] == '\\') + { + i++; + continue; + } + if (line[i] == quoteChar_) // check ending quote + { + isInQuote_ = false; + quoteChar_ = ' '; + continue; + } + continue; // must close quote before continuing + } + if (line[i] == '"' // check opening quote + || (line[i] == '\'' && !isDigitSeparator(line, i))) + { + isInQuote_ = true; + quoteChar_ = line[i]; + continue; + } + if (line[i] == ':') + { + if ((i + 1 < line.length()) && (line[i + 1] == ':')) + i++; // bypass scope resolution operator + else + break; // found it + } + } + return i; +} + +/** +* indent a line by a given number of tabsets + * by inserting leading whitespace to the line argument. + * + * @param line a reference to the line to indent. + * @param indent the number of tabsets to insert. + * @return the number of characters inserted. + */ +int ASEnhancer::indentLine(std::string& line, int indent) const +{ + if (line.empty() + && !emptyLineFill) + return 0; + + size_t charsToInsert = 0; + + if (forceTab && indentLength != tabLength) + { + // replace tab indents with spaces + convertForceTabIndentToSpaces(line); + // insert the space indents + charsToInsert = indent * indentLength; + line.insert(line.begin(), charsToInsert, ' '); + // replace leading spaces with tab indents + convertSpaceIndentToForceTab(line); + } + else if (useTabs) + { + charsToInsert = indent; + line.insert(line.begin(), charsToInsert, '\t'); + } + else // spaces + { + charsToInsert = indent * indentLength; + line.insert(line.begin(), charsToInsert, ' '); + } + + return charsToInsert; +} + +/** + * check for SQL "BEGIN DECLARE SECTION". + * must compare case insensitive and allow any spacing between words. + * + * @param line a reference to the line to indent. + * @param index the current line index. + * @return true if a hit. + */ +bool ASEnhancer::isBeginDeclareSectionSQL(std::string_view line, size_t index) const +{ + + size_t hits = 0; + size_t i; + for (i = index; i < line.length(); i++) + { + i = line.find_first_not_of(" \t", i); + if (i == std::string::npos) + return false; + if (line[i] == ';') + break; + if (!isCharPotentialHeader(line, i)) + continue; + std::string_view word = getCurrentWord(line, i); + for (char character : word) + character = (char) toupper(character); + if (word == "EXEC" || word == "SQL") + { + i += word.length() - 1; + continue; + } + if (word == "DECLARE" || word == "SECTION") + { + hits++; + i += word.length() - 1; + continue; + } + if (word == "BEGIN") + { + hits++; + i += word.length() - 1; + continue; + } + return false; + } + if (hits == 3) + return true; + return false; +} + +/** + * check for SQL "END DECLARE SECTION". + * must compare case insensitive and allow any spacing between words. + * + * @param line a reference to the line to indent. + * @param index the current line index. + * @return true if a hit. + */ +bool ASEnhancer::isEndDeclareSectionSQL(std::string_view line, size_t index) const +{ + size_t hits = 0; + size_t i; + for (i = index; i < line.length(); i++) + { + i = line.find_first_not_of(" \t", i); + if (i == std::string::npos) + return false; + if (line[i] == ';') + break; + if (!isCharPotentialHeader(line, i)) + continue; + std::string_view word = getCurrentWord(line, i); + for (char character : word) + character = (char) toupper(character); + if (word == "EXEC" || word == "SQL") + { + i += word.length() - 1; + continue; + } + if (word == "DECLARE" || word == "SECTION") + { + hits++; + i += word.length() - 1; + continue; + } + if (word == "END") + { + hits++; + i += word.length() - 1; + continue; + } + return false; + } + if (hits == 3) + return true; + return false; +} + +/** + * check if a one-line brace has been reached, + * i.e. if the currently reached '{' character is closed + * with a complimentary '}' elsewhere on the current line, + *. + * @return false = one-line brace has not been reached. + * true = one-line brace has been reached. + */ +bool ASEnhancer::isOneLineBlockReached(std::string_view line, int startChar) const +{ + assert(line[startChar] == '{'); + + bool isInComment_ = false; + bool isInQuote_ = false; + int _braceCount = 1; + int lineLength = line.length(); + char quoteChar_ = ' '; + char ch = ' '; + + 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 == '\'' && !isDigitSeparator(line, i))) + { + isInQuote_ = true; + quoteChar_ = ch; + continue; + } + + if (line.compare(i, 2, "//") == 0) + break; + + if (line.compare(i, 2, "/*") == 0) + { + isInComment_ = true; + ++i; + continue; + } + + if (ch == '{') + ++_braceCount; + else if (ch == '}') + --_braceCount; + + if (_braceCount == 0) + return true; + } + + return false; +} + +/** + * parse characters in the current line to determine if an indent + * or unindent is needed. + */ +void ASEnhancer::parseCurrentLine(std::string& line, bool isInPreprocessor, bool isInSQL) +{ + bool isSpecialChar = false; // is a backslash escape character + + for (size_t i = 0; i < line.length(); i++) + { + char ch = line[i]; + + // bypass whitespace + if (std::isblank(ch)) + continue; + + // handle special characters (i.e. backslash+character such as \n, \t, ...) + if (isSpecialChar) + { + isSpecialChar = false; + continue; + } + if (!(isInComment) && line.compare(i, 2, "\\\\") == 0) + { + i++; + continue; + } + if (!(isInComment) && ch == '\\') + { + isSpecialChar = true; + continue; + } + + // handle quotes (such as 'x' and "Hello Dolly") + if (!isInComment + && (ch == '"' + || (ch == '\'' && !isDigitSeparator(line, i)))) + { + if (!isInQuote) + { + quoteChar = ch; + isInQuote = true; + } + else if (quoteChar == ch) + { + isInQuote = false; + continue; + } + } + + if (isInQuote) + continue; + + // handle comments + + if (!(isInComment) && line.compare(i, 2, "//") == 0) + { + // check for windows line markers + if (line.compare(i + 2, 1, "\xf0") > 0) + lineNumber--; + // unindent if not in case braces + if (line.find_first_not_of(" \t") == i + && sw.switchBraceCount == 1 + && sw.unindentCase) + shouldUnindentComment = true; + break; // finished with the line + } + if (!(isInComment) && line.compare(i, 2, "/*") == 0) + { + // unindent if not in case braces + if (sw.switchBraceCount == 1 && sw.unindentCase) + shouldUnindentComment = true; + isInComment = true; + size_t commentEnd = line.find("*/", i); + if (commentEnd == std::string::npos) + i = line.length() - 1; + else + i = commentEnd - 1; + continue; + } + if ((isInComment) && line.compare(i, 2, "*/") == 0) + { + // unindent if not in case braces + if (sw.switchBraceCount == 1 && sw.unindentCase) + shouldUnindentComment = true; + isInComment = false; + i++; + continue; + } + if (isInComment) + { + // unindent if not in case braces + if (sw.switchBraceCount == 1 && sw.unindentCase) + shouldUnindentComment = true; + size_t commentEnd = line.find("*/", i); + if (commentEnd == std::string::npos) + i = line.length() - 1; + else + i = commentEnd - 1; + continue; + } + + // if we have reached this far then we are NOT in a comment or string of special characters + + if (line[i] == '{') + braceCount++; + + if (line[i] == '}') + braceCount--; + + // check for preprocessor within an event table + if (isInEventTable && line[i] == '#' && preprocBlockIndent) + { + std::string preproc; + preproc = line.substr(i + 1); + if (preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef) + eventPreprocDepth += 1; + if (preproc.substr(0, 5) == "endif" && eventPreprocDepth > 0) + eventPreprocDepth -= 1; + } + + bool isPotentialKeyword = isCharPotentialHeader(line, i); + + // ---------------- wxWidgets and MFC macros ---------------------------------- + + if (isPotentialKeyword) + { + for (const auto* indentableMacro : *indentableMacros) + { + // 'first' is the beginning macro + if (findKeyword(line, i, indentableMacro->first)) + { + nextLineIsEventIndent = true; + break; + } + // 'second' is the ending macro + if (findKeyword(line, i, indentableMacro->second)) + { + isInEventTable = false; + eventPreprocDepth = 0; + break; + } + } + } + + // ---------------- process SQL ----------------------------------------------- + + if (isInSQL) + { + if (isBeginDeclareSectionSQL(line, i)) + nextLineIsDeclareIndent = true; + if (isEndDeclareSectionSQL(line, i)) + isInDeclareSection = false; + break; + } + + // ---------------- process switch statements --------------------------------- + + if (isPotentialKeyword && findKeyword(line, i, ASResource::AS_SWITCH)) + { + switchDepth++; + switchStack.emplace_back(sw); // save current variables + sw.switchBraceCount = 0; + sw.unindentCase = false; // don't clear case until end of switch + i += 5; // bypass switch statement + continue; + } + + // just want unindented case statements from this point + + if (caseIndent + || switchDepth == 0 + || (isInPreprocessor && !preprocDefineIndent)) + { + // bypass the entire word + if (isPotentialKeyword) + { + std::string_view name = getCurrentWord(line, i); + i += name.length() - 1; + } + continue; + } + + i = processSwitchBlock(line, i); + + } // end of for loop * end of for loop * end of for loop * end of for loop +} + +/** + * process the character at the current index in a switch block. + * + * @param line a reference to the line to indent. + * @param index the current line index. + * @return the new line index. + */ +size_t ASEnhancer::processSwitchBlock(std::string& line, size_t index) +{ + size_t i = index; + bool isPotentialKeyword = isCharPotentialHeader(line, i); + + if (line[i] == '{') + { + sw.switchBraceCount++; + if (lookingForCaseBrace) // if 1st after case statement + { + sw.unindentCase = true; // unindenting this case + sw.unindentDepth++; + lookingForCaseBrace = false; // not looking now + } + return i; + } + lookingForCaseBrace = false; // no opening brace, don't indent + + if (line[i] == '}') + { + sw.switchBraceCount--; + if (sw.switchBraceCount == 0) // if end of switch statement + { + int lineUnindent = sw.unindentDepth; + if (line.find_first_not_of(" \t") == i + && !switchStack.empty()) + lineUnindent = switchStack[switchStack.size() - 1].unindentDepth; + if (shouldUnindentLine) + { + if (lineUnindent > 0) + i -= unindentLine(line, lineUnindent); + shouldUnindentLine = false; + } + switchDepth--; + sw = switchStack.back(); + switchStack.pop_back(); + } + return i; + } + + if (isPotentialKeyword + && (findKeyword(line, i, ASResource::AS_CASE) + || findKeyword(line, i, ASResource::AS_DEFAULT))) + { + if (sw.unindentCase) // if unindented last case + { + sw.unindentCase = false; // stop unindenting previous case + sw.unindentDepth--; + } + + i = findCaseColon(line, i); + + i++; + for (; i < line.length(); i++) // bypass whitespace + { + if (!std::isblank(line[i])) + break; + } + if (i < line.length()) + { + if (line[i] == '{') + { + braceCount++; + sw.switchBraceCount++; + if (!isOneLineBlockReached(line, i)) + unindentNextLine = true; + return i; + } + } + lookingForCaseBrace = true; + i--; // need to process this char + return i; + } + if (isPotentialKeyword) + { + std::string_view name = getCurrentWord(line, i); // bypass the entire name + i += name.length() - 1; + } + return i; +} + +/** + * unindent a line by a given number of tabsets + * by erasing the leading whitespace from the line argument. + * + * @param line a reference to the line to unindent. + * @param unindent the number of tabsets to erase. + * @return the number of characters erased. + */ +int ASEnhancer::unindentLine(std::string& line, int unindent) const +{ + size_t whitespace = line.find_first_not_of(" \t"); + + if (whitespace == std::string::npos) // if line is blank + whitespace = line.length(); // must remove padding, if any + + if (whitespace == 0) + return 0; + + size_t charsToErase = 0; + + if (forceTab && indentLength != tabLength) + { + // replace tab indents with spaces + convertForceTabIndentToSpaces(line); + // remove the space indents + size_t spaceIndentLength = line.find_first_not_of(" \t"); + charsToErase = unindent * indentLength; + if (charsToErase <= spaceIndentLength) + line.erase(0, charsToErase); + else + charsToErase = 0; + // replace leading spaces with tab indents + convertSpaceIndentToForceTab(line); + } + else if (useTabs) + { + charsToErase = unindent; + if (charsToErase <= whitespace) + line.erase(0, charsToErase); + else + charsToErase = 0; + } + else // spaces + { + charsToErase = unindent * indentLength; + if (charsToErase <= whitespace) + line.erase(0, charsToErase); + else + charsToErase = 0; + } + + return charsToErase; +} + +} // end namespace astyle diff --git a/astyle/AStyle/src/ASFormatter.cpp b/astyle/AStyle/src/ASFormatter.cpp new file mode 100644 index 0000000..514919e --- /dev/null +++ b/astyle/AStyle/src/ASFormatter.cpp @@ -0,0 +1,8738 @@ +// ASFormatter.cpp +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +//----------------------------------------------------------------------------- +// headers +//----------------------------------------------------------------------------- + +#include "astyle.h" + +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// astyle namespace +//----------------------------------------------------------------------------- + +namespace astyle { + +// +//----------------------------------------------------------------------------- +// ASFormatter class +//----------------------------------------------------------------------------- + +/** + * Constructor of ASFormatter + */ +ASFormatter::ASFormatter() +{ + sourceIterator = nullptr; + enhancer = new ASEnhancer; + preBraceHeaderStack = nullptr; + braceTypeStack = nullptr; + parenStack = nullptr; + structStack = nullptr; + questionMarkStack = nullptr; + lineCommentNoIndent = false; + formattingStyle = STYLE_NONE; + braceFormatMode = NONE_MODE; + pointerAlignment = PTR_ALIGN_NONE; + referenceAlignment = REF_SAME_AS_PTR; + objCColonPadMode = COLON_PAD_NO_CHANGE; + lineEnd = LINEEND_DEFAULT; + squeezeEmptyLineNum = std::string::npos; + maxCodeLength = std::string::npos; + isInStruct = false; + shouldPadCommas = false; + shouldPadOperators = false; + negationPadMode = NEGATION_PAD_NO_CHANGE; + includeDirectivePaddingMode = INCLUDE_PAD_NO_CHANGE; + shouldPadParensOutside = false; + shouldPadFirstParen = false; + shouldPadEmptyParens = false; + shouldPadParensInside = false; + shouldPadHeader = false; + shouldStripCommentPrefix = false; + shouldUnPadParens = false; + attachClosingBraceMode = false; + shouldBreakOneLineBlocks = true; + shouldBreakOneLineHeaders = false; + shouldBreakOneLineStatements = true; + shouldConvertTabs = false; + shouldIndentCol1Comments = false; + shouldIndentPreprocBlock = false; + shouldCloseTemplates = false; + shouldAttachExternC = false; + shouldAttachNamespace = false; + shouldAttachClass = false; + shouldAttachClosingWhile = false; + shouldAttachInline = false; + shouldBreakBlocks = false; + shouldBreakClosingHeaderBlocks = false; + shouldBreakClosingHeaderBraces = false; + shouldDeleteEmptyLines = false; + shouldBreakReturnType = false; + shouldBreakReturnTypeDecl = false; + shouldAttachReturnType = false; + shouldAttachReturnTypeDecl = false; + shouldBreakElseIfs = false; + shouldBreakLineAfterLogical = false; + shouldAddBraces = false; + shouldAddOneLineBraces = false; + shouldRemoveBraces = false; + shouldPadMethodColon = false; + shouldPadMethodPrefix = false; + shouldUnPadMethodPrefix = false; + shouldPadReturnType = false; + shouldUnPadReturnType = false; + shouldPadParamType = false; + shouldUnPadParamType = false; + shouldPadBracketsOutside = false; + shouldPadBracketsInside = false; + shouldUnPadBrackets = false; + isInMultlineStatement = false; + isInExplicitBlock = 0; + + // initialize ASFormatter member std::vectors + formatterFileType = INVALID_TYPE; // reset to an invalid type + headers = new std::vector; + nonParenHeaders = new std::vector; + preDefinitionHeaders = new std::vector; + preCommandHeaders = new std::vector; + operators = new std::vector; + assignmentOperators = new std::vector; + castOperators = new std::vector; + + // initialize ASEnhancer member std::vectors + indentableMacros = new std::vector* >; +} + +/** + * Destructor of ASFormatter + */ +ASFormatter::~ASFormatter() +{ + // delete ASFormatter stack std::vectors + deleteContainer(preBraceHeaderStack); + deleteContainer(braceTypeStack); + deleteContainer(parenStack); + deleteContainer(structStack); + deleteContainer(questionMarkStack); + + // delete ASFormatter member std::vectors + formatterFileType = INVALID_TYPE; // reset to an invalid type + delete headers; + delete nonParenHeaders; + delete preDefinitionHeaders; + delete preCommandHeaders; + delete operators; + delete assignmentOperators; + delete castOperators; + + // delete ASEnhancer member std::vectors + delete indentableMacros; + + // must be done when the ASFormatter object is deleted (not ASBeautifier) + // delete ASBeautifier member std::vectors + ASBeautifier::deleteBeautifierVectors(); + + delete enhancer; +} + +/** + * initialize the ASFormatter. + * + * init() should be called every time a ASFormatter object is to start + * formatting a NEW source file. + * init() receives a pointer to a ASSourceIterator object that will be + * used to iterate through the source code. + * + * @param si a pointer to the ASSourceIterator or ASStreamIterator object. + */ +void ASFormatter::init(ASSourceIterator* si) +{ + buildLanguageVectors(); + fixOptionVariableConflicts(); + ASBeautifier::init(si); + sourceIterator = si; + + enhancer->init(getFileType(), + getIndentLength(), + getTabLength(), + getIndentString() == "\t", + getForceTabIndentation(), + getNamespaceIndent(), + getCaseIndent(), + shouldIndentPreprocBlock, + getPreprocDefineIndent(), + getEmptyLineFill(), + indentableMacros); + + initContainer(preBraceHeaderStack, new std::vector); + initContainer(parenStack, new std::vector); + initContainer(structStack, new std::vector); + initContainer(questionMarkStack, new std::vector); + parenStack->emplace_back(0); // parenStack must contain this default entry + initContainer(braceTypeStack, new std::vector); + braceTypeStack->emplace_back(NULL_TYPE); // braceTypeStack must contain this default entry + clearFormattedLineSplitPoints(); + + currentHeader = nullptr; + previousHeader = nullptr; + currentLine = ""; + readyFormattedLine = ""; + formattedLine = ""; + verbatimDelimiter = ""; + currentChar = ' '; + previousChar = ' '; + previousCommandChar = ' '; + previousNonWSChar = ','; // not a potential name or operator + quoteChar = '"'; + preprocBlockEnd = 0; + charNum = 0; + checksumIn = 0; + checksumOut = 0; + currentLineFirstBraceNum = std::string::npos; + formattedLineCommentNum = 0; + leadingSpaces = 0; + previousReadyFormattedLineLength = std::string::npos; + preprocBraceTypeStackSize = 0; + spacePadNum = 0; + methodAttachCharNum = std::string::npos; + methodAttachLineNum = 0; + methodBreakCharNum = std::string::npos; + methodBreakLineNum = 0; + nextLineSpacePadNum = 0; + objCColonAlign = 0; + templateDepth = 0; + squareBracketCount = 0; + parenthesesCount = 0; + bracesNestingLevel = 0; + bracesNestingLevelOfStruct = 0; + squeezeEmptyLineCount = 0; + + runInIndentChars = 0; + tabIncrementIn = 0; + previousBraceType = NULL_TYPE; + + isVirgin = true; + isInVirginLine = true; + isInLineComment = false; + isInComment = false; + isInCommentStartLine = false; + noTrimCommentContinuation = false; + isInPreprocessor = false; + isInPreprocessorDefineDef = false; + isInPreprocessorBeautify = false; + doesLineStartComment = false; + lineEndsInCommentOnly = false; + lineIsCommentOnly = false; + lineIsLineCommentOnly = false; + lineIsEmpty = false; + isImmediatelyPostCommentOnly = false; + isImmediatelyPostEmptyLine = false; + isInClassInitializer = false; + isInQuote = false; + isInVerbatimQuote = false; + checkInterpolation = false; + haveLineContinuationChar = false; + isInQuoteContinuation = false; + isHeaderInMultiStatementLine = false; + isSpecialChar = false; + isNonParenHeader = false; + foundNamespaceHeader = false; + foundClassHeader = false; + foundStructHeader = false; + foundInterfaceHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundPreCommandMacro = false; + foundTrailingReturnType = false; + foundCastOperator = false; + foundQuestionMark = false; + isInLineBreak = false; + endOfAsmReached = false; + endOfCodeReached = false; + isFormattingModeOff = false; + isInEnum = false; + isInContinuedPreProc = false; + isInStruct = false; + isInExecSQL = false; + isInAsm = false; + isInAsmOneLine = false; + isInAsmBlock = false; + isLineReady = false; + elseHeaderFollowsComments = false; + caseHeaderFollowsComments = false; + isPreviousBraceBlockRelated = false; + isInPotentialCalculation = false; + needHeaderOpeningBrace = false; + shouldBreakLineAtNextChar = false; + shouldKeepLineUnbroken = false; + shouldReparseCurrentChar = false; + passedSemicolon = false; + passedColon = false; + isImmediatelyPostNonInStmt = false; + isCharImmediatelyPostNonInStmt = false; + isInTemplate = false; + isImmediatelyPostComment = false; + isImmediatelyPostLineComment = false; + isImmediatelyPostEmptyBlock = false; + isImmediatelyPostObjCMethodPrefix = false; + isImmediatelyPostPreprocessor = false; + isImmediatelyPostReturn = false; + isImmediatelyPostThrow = false; + isImmediatelyPostNewDelete = false; + isImmediatelyPostOperator = false; + isImmediatelyPostTemplate = false; + isImmediatelyPostPointerOrReference = false; + isCharImmediatelyPostReturn = false; + isCharImmediatelyPostThrow = false; + isCharImmediatelyPostNewDelete = false; + isCharImmediatelyPostOperator = false; + isCharImmediatelyPostComment = false; + isPreviousCharPostComment = false; + isCharImmediatelyPostLineComment = false; + isCharImmediatelyPostOpenBlock = false; + isCharImmediatelyPostCloseBlock = false; + isCharImmediatelyPostTemplate = false; + isCharImmediatelyPostPointerOrReference = false; + isInObjCInterface = false; + isInObjCMethodDefinition = false; + isInObjCReturnType = false; + isInObjCParam = false; + isInObjCSelector = false; + breakCurrentOneLineBlock = false; + shouldRemoveNextClosingBrace = false; + isInBraceRunIn = false; + returnTypeChecked = false; + currentLineBeginsWithBrace = false; + isPrependPostBlockEmptyLineRequested = false; + isAppendPostBlockEmptyLineRequested = false; + isIndentablePreprocessor = false; + isIndentablePreprocessorBlck = false; + prependEmptyLine = false; + appendOpeningBrace = false; + foundClosingHeader = false; + isImmediatelyPostHeader = false; + isInHeader = false; + isInCase = false; + isInAllocator = false; + isInMultlineStatement = false; + isInExplicitBlock = 0; + + isFirstPreprocConditional = false; + processedFirstConditional = false; + isJavaStaticConstructor = false; +} + +/** + * build std::vectors for each programming language + * depending on the file extension. + */ +void ASFormatter::buildLanguageVectors() +{ + if (getFileType() == formatterFileType) // don't build unless necessary + return; + + formatterFileType = getFileType(); + + headers->clear(); + nonParenHeaders->clear(); + preDefinitionHeaders->clear(); + preCommandHeaders->clear(); + operators->clear(); + assignmentOperators->clear(); + castOperators->clear(); + indentableMacros->clear(); // ASEnhancer + + ASResource::buildHeaders(headers, formatterFileType); + ASResource::buildNonParenHeaders(nonParenHeaders, formatterFileType); + ASResource::buildPreDefinitionHeaders(preDefinitionHeaders, formatterFileType); + ASResource::buildPreCommandHeaders(preCommandHeaders, formatterFileType); + ASResource::buildOperators(operators, formatterFileType); + ASResource::buildAssignmentOperators(assignmentOperators); + ASResource::buildCastOperators(castOperators); + ASResource::buildIndentableMacros(indentableMacros); //ASEnhancer +} + +/** + * set the variables for each predefined style. + * this will override any previous settings. + */ +void ASFormatter::fixOptionVariableConflicts() +{ + if (formattingStyle == STYLE_ALLMAN) + { + setBraceFormatMode(BREAK_MODE); + } + else if (formattingStyle == STYLE_JAVA) + { + setBraceFormatMode(ATTACH_MODE); + } + else if (formattingStyle == STYLE_KR) + { + setBraceFormatMode(LINUX_MODE); + } + else if (formattingStyle == STYLE_STROUSTRUP) + { + setBraceFormatMode(LINUX_MODE); + setBreakClosingHeaderBracesMode(true); + } + else if (formattingStyle == STYLE_WHITESMITH) + { + setBraceFormatMode(BREAK_MODE); + setBraceIndent(true); + setClassIndent(true); // avoid hanging indent with access modifiers + setSwitchIndent(true); // avoid hanging indent with case statements + } + else if (formattingStyle == STYLE_VTK) + { + // the unindented class brace does NOT cause a hanging indent like Whitesmith + setBraceFormatMode(BREAK_MODE); + setBraceIndentVtk(true); // sets both braceIndent and braceIndentVtk + setSwitchIndent(true); // avoid hanging indent with case statements + } + else if (formattingStyle == STYLE_RATLIFF) + { + // attached braces can have hanging indents with the closing brace + setBraceFormatMode(ATTACH_MODE); + setBraceIndent(true); + setClassIndent(true); // avoid hanging indent with access modifiers + setSwitchIndent(true); // avoid hanging indent with case statements + } + else if (formattingStyle == STYLE_GNU) + { + setBraceFormatMode(BREAK_MODE); + setBlockIndent(true); + } + else if (formattingStyle == STYLE_LINUX) + { + setBraceFormatMode(LINUX_MODE); + // always for Linux style + setMinConditionalIndentOption(MINCOND_ONEHALF); + } + else if (formattingStyle == STYLE_HORSTMANN) + { + setBraceFormatMode(RUN_IN_MODE); + setSwitchIndent(true); + } + else if (formattingStyle == STYLE_1TBS) + { + setBraceFormatMode(LINUX_MODE); + setAddBracesMode(1); + setRemoveBracesMode(false); + } + else if (formattingStyle == STYLE_GOOGLE) + { + setBraceFormatMode(ATTACH_MODE); + setModifierIndent(true); + setClassIndent(false); + } + else if (formattingStyle == STYLE_MOZILLA) + { + setBraceFormatMode(LINUX_MODE); + } + else if (formattingStyle == STYLE_WEBKIT) + { + setBraceFormatMode(LINUX_MODE); + } + else if (formattingStyle == STYLE_PICO) + { + setBraceFormatMode(RUN_IN_MODE); + setAttachClosingBraceMode(true); + setSwitchIndent(true); + setBreakOneLineBlocksMode(false); + setBreakOneLineStatementsMode(false); + // add-braces won't work for pico, but it could be fixed if necessary + // both options should be set to true + if (shouldAddBraces) + shouldAddOneLineBraces = true; + } + else if (formattingStyle == STYLE_LISP) + { + setBraceFormatMode(ATTACH_MODE); + setAttachClosingBraceMode(true); + setBreakOneLineStatementsMode(false); + // add-one-line-braces won't work for lisp + // only shouldAddBraces should be set to true + if (shouldAddOneLineBraces) + { + shouldAddBraces = true; + shouldAddOneLineBraces = false; + } + } + setMinConditionalIndentLength(); + // if not set by indent=force-tab-x set equal to indentLength + if (getTabLength() == 0) + setDefaultTabLength(); + // add-one-line-braces implies keep-one-line-blocks + if (shouldAddOneLineBraces) + setBreakOneLineBlocksMode(false); + // don't allow add-braces and remove-braces + if (shouldAddBraces || shouldAddOneLineBraces) + setRemoveBracesMode(false); + // don't allow break-return-type and attach-return-type + if (shouldBreakReturnType) + shouldAttachReturnType = false; + if (shouldBreakReturnTypeDecl) + shouldAttachReturnTypeDecl = false; + // don't allow indent-classes and indent-modifiers + if (getClassIndent()) + setModifierIndent(false); +} + +bool ASFormatter::handleImmediatelyPostHeaderSection() +{ + // should braces be added + if (currentChar != '{' + && shouldAddBraces + && currentChar != '#' // don't add to preprocessor + && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine) + && isOkToBreakBlock(braceTypeStack->back())) + { + bool bracesAdded = addBracesToStatement(); + if (bracesAdded && !shouldAddOneLineBraces) + { + size_t firstText = currentLine.find_first_not_of(" \t"); + assert(firstText != std::string::npos); + if ((int) firstText == charNum || shouldBreakOneLineHeaders) + breakCurrentOneLineBlock = true; + } + } + // should braces be removed + else if (currentChar == '{' && shouldRemoveBraces) + { + bool bracesRemoved = removeBracesFromStatement(); + if (bracesRemoved) + { + shouldRemoveNextClosingBrace = true; + if (isBeforeAnyLineEndComment(charNum)) + spacePadNum--; + else if (shouldBreakOneLineBlocks + || (currentLineBeginsWithBrace + && currentLine.find_first_not_of(" \t") != std::string::npos)) + shouldBreakLineAtNextChar = true; + return false; + } + } + + // break 'else-if' if shouldBreakElseIfs is requested + if (shouldBreakElseIfs + && currentHeader == &ASResource::AS_ELSE + && isOkToBreakBlock(braceTypeStack->back()) + && !isBeforeAnyComment() + && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine)) + { + std::string nextText = peekNextText(currentLine.substr(charNum)); + if (!nextText.empty() + && isCharPotentialHeader(nextText, 0) + && ASBase::findHeader(nextText, 0, headers) == &ASResource::AS_IF) + { + isInLineBreak = true; + } + } + + // break a header (e.g. if, while, else) from the following statement + if (shouldBreakOneLineHeaders + && peekNextChar() != ' ' + && (shouldBreakOneLineStatements + || (!isHeaderInMultiStatementLine + && !isMultiStatementLine())) + && isOkToBreakBlock(braceTypeStack->back()) + && !isBeforeAnyComment()) + { + if (currentChar == '{') + { + if (!currentLineBeginsWithBrace) + { + if (isOneLineBlockReached(currentLine, charNum) == 3) + isInLineBreak = false; + else + breakCurrentOneLineBlock = true; + } + } + else if (currentHeader == &ASResource::AS_ELSE) + { + std::string nextText = peekNextText(currentLine.substr(charNum), true); + if (!nextText.empty() + && ((isCharPotentialHeader(nextText, 0) + && ASBase::findHeader(nextText, 0, headers) != &ASResource::AS_IF) + || nextText[0] == '{')) + isInLineBreak = true; + } + else + { + // GH16 only break if header is present + if (currentHeader) + isInLineBreak = true; + } + } + + isImmediatelyPostHeader = false; + return true; +} + +bool ASFormatter::handlePassedSemicolonSection() +{ + isInAllocator = false; // GH16 + isInMultlineStatement = false; + passedSemicolon = false; + + if (parenStack->back() == 0 && !isCharImmediatelyPostComment && currentChar != ';') // allow ;; + { + // does a one-line block have ending comments? + if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)) + { + size_t blockEnd = currentLine.rfind(ASResource::AS_CLOSE_BRACE); + assert(blockEnd != std::string::npos); + // move ending comments to this formattedLine + if (isBeforeAnyLineEndComment(blockEnd)) + { + size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1); + assert(commentStart != std::string::npos); + assert((currentLine.compare(commentStart, 2, "//") == 0) + || (currentLine.compare(commentStart, 2, "/*") == 0)); + formattedLine.append(getIndentLength() - 1, ' '); + // append comment + int charNumSave = charNum; + charNum = commentStart; + while (charNum < (int) currentLine.length()) + { + currentChar = currentLine[charNum]; + if (currentChar == '\t' && shouldConvertTabs) + convertTabToSpaces(); + formattedLine.append(1, currentChar); + ++charNum; + } + size_t commentLength = currentLine.length() - commentStart; + currentLine.erase(commentStart, commentLength); + charNum = charNumSave; + currentChar = currentLine[charNum]; + testForTimeToSplitFormattedLine(); + } + } + isInExecSQL = false; + shouldReparseCurrentChar = true; + if (formattedLine.find_first_not_of(" \t") != std::string::npos) + isInLineBreak = true; + if (needHeaderOpeningBrace) + { + isCharImmediatelyPostCloseBlock = true; + needHeaderOpeningBrace = false; + } + return false; + } + return true; +} + +void ASFormatter::handleAttachedReturnTypes() +{ + if ((size_t) charNum == methodAttachCharNum) + { + int pa = pointerAlignment; + int ra = referenceAlignment; + int itemAlignment = (previousNonWSChar == '*' || previousNonWSChar == '^') + ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra); + isInLineBreak = false; + if (previousNonWSChar == '*' || previousNonWSChar == '&' || previousNonWSChar == '^') + { + if (itemAlignment == REF_ALIGN_TYPE) + { + if (!formattedLine.empty() + && !std::isblank(formattedLine[formattedLine.length() - 1])) + formattedLine.append(1, ' '); + } + else if (itemAlignment == REF_ALIGN_MIDDLE) + { + if (!formattedLine.empty() + && !std::isblank(formattedLine[formattedLine.length() - 1])) + formattedLine.append(1, ' '); + } + else if (itemAlignment == REF_ALIGN_NAME) + { + if (!formattedLine.empty() + && std::isblank(formattedLine[formattedLine.length() - 1])) + formattedLine.erase(formattedLine.length() - 1); + } + else + { + if (formattedLine.length() > 1 + && !std::isblank(formattedLine[formattedLine.length() - 2])) + formattedLine.append(1, ' '); + } + } + else + formattedLine.append(1, ' '); + } + methodAttachCharNum = std::string::npos; + methodAttachLineNum = 0; +} + +void ASFormatter::handleClosedBracesOrParens() +{ + foundPreCommandHeader = false; + parenStack->back()--; + // this can happen in preprocessor directives + if (parenStack->back() < 0) + parenStack->back() = 0; + if (!questionMarkStack->empty()) + { + foundQuestionMark = questionMarkStack->back(); + questionMarkStack->pop_back(); + } + + if (isInTemplate && currentChar == '>') + { + templateDepth--; + if (templateDepth == 0) + { + isInTemplate = false; + isImmediatelyPostTemplate = true; + } + } + + // check if this parenthesis closes a header, e.g. if (...), while (...) + //GH16 + if ( !(isSharpStyle() && peekNextChar() == ',') && isInHeader && parenStack->back() == 0) + { + isInHeader = false; + isImmediatelyPostHeader = true; + foundQuestionMark = false; + } + if (currentChar == ']') + { + --squareBracketCount; + if (squareBracketCount <= 0) + { + squareBracketCount = 0; + objCColonAlign = 0; + } + } + + // GH16 break + if (currentChar == ')') + { + --parenthesesCount; + foundCastOperator = false; + if (parenStack->back() == 0) + endOfAsmReached = true; + } +} + +void ASFormatter::handleBraces() +{ + // if appendOpeningBrace this was already done for the original brace + if (currentChar == '{' && !appendOpeningBrace) + { + BraceType newBraceType = getBraceType(); + breakCurrentOneLineBlock = false; + foundNamespaceHeader = false; + foundClassHeader = false; + foundStructHeader = false; + foundInterfaceHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundPreCommandMacro = false; + foundTrailingReturnType = false; + isInPotentialCalculation = false; + isInObjCMethodDefinition = false; + isImmediatelyPostObjCMethodPrefix = false; + isInObjCInterface = false; + isInEnum = false; + + isJavaStaticConstructor = false; + isCharImmediatelyPostNonInStmt = false; + needHeaderOpeningBrace = false; + shouldKeepLineUnbroken = false; + returnTypeChecked = false; + + isInExplicitBlock++; + + objCColonAlign = 0; + + methodBreakCharNum = std::string::npos; + methodBreakLineNum = 0; + methodAttachCharNum = std::string::npos; + methodAttachLineNum = 0; + + isPreviousBraceBlockRelated = !isBraceType(newBraceType, ARRAY_TYPE); + braceTypeStack->emplace_back(newBraceType); + preBraceHeaderStack->emplace_back(currentHeader); + currentHeader = nullptr; + // do not use emplace_back on std::vector until supported by macOS + structStack->push_back(isInIndentableStruct); + if (isBraceType(newBraceType, STRUCT_TYPE) && isCStyle()) + isInIndentableStruct = isStructAccessModified(currentLine, charNum); + else + isInIndentableStruct = false; + + bracesNestingLevel++; + } + + // this must be done before the braceTypeStack is popped + BraceType braceType = braceTypeStack->back(); + bool isOpeningArrayBrace = (isBraceType(braceType, ARRAY_TYPE) + && braceTypeStack->size() >= 2 + && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2], ARRAY_TYPE) + ); + + if (currentChar == '}') + { + // if a request has been made to append a post block empty line, + // but the block exists immediately before a closing brace, + // then there is no need for the post block empty line. + isAppendPostBlockEmptyLineRequested = false; + if (isInAsm) + endOfAsmReached = true; + isInAsmOneLine = isInQuote = false; + shouldKeepLineUnbroken = false; + squareBracketCount = 0; + isInAllocator = false; + isInMultlineStatement = false; + isInExplicitBlock--; + + if (braceTypeStack->size() > 1) + { + previousBraceType = braceTypeStack->back(); + braceTypeStack->pop_back(); + isPreviousBraceBlockRelated = !isBraceType(braceType, ARRAY_TYPE); + } + else + { + previousBraceType = NULL_TYPE; + isPreviousBraceBlockRelated = false; + } + + if (!preBraceHeaderStack->empty()) + { + previousHeader = currentHeader; + currentHeader = preBraceHeaderStack->back(); + preBraceHeaderStack->pop_back(); + } + else + currentHeader = nullptr; + + if (!structStack->empty()) + { + isInIndentableStruct = structStack->back(); + structStack->pop_back(); + } + else + isInIndentableStruct = false; + + if (isNonInStatementArray + && (!isBraceType(braceTypeStack->back(), ARRAY_TYPE) // check previous brace + || peekNextChar() == ';')) // check for "};" added V2.01 + isImmediatelyPostNonInStmt = true; + + if (!shouldBreakOneLineStatements + && ASBeautifier::getNextWord(currentLine, charNum) == ASResource::AS_ELSE) + { + // handle special case of "else" at the end of line + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ') + shouldBreakLineAtNextChar = true; + } + bracesNestingLevel--; + } + + // format braces + appendOpeningBrace = false; + if (isBraceType(braceType, ARRAY_TYPE)) + { + formatArrayBraces(braceType, isOpeningArrayBrace); + } + else + { + if (currentChar == '{') + formatOpeningBrace(braceType); + else + formatClosingBrace(braceType); + } +} + + +void ASFormatter::handleBreakLine() +{ + isCharImmediatelyPostOpenBlock = (previousCommandChar == '{'); + isCharImmediatelyPostCloseBlock = (previousCommandChar == '}'); + + if (isCharImmediatelyPostOpenBlock + && !isCharImmediatelyPostComment + && !isCharImmediatelyPostLineComment) + { + previousCommandChar = ' '; + + if (braceFormatMode == NONE_MODE) + { + if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE) + && (isBraceType(braceTypeStack->back(), BREAK_BLOCK_TYPE) + || shouldBreakOneLineBlocks)) + isInLineBreak = true; + else if (currentLineBeginsWithBrace) + formatRunIn(); + else + breakLine(); + } + else if (braceFormatMode == RUN_IN_MODE + && currentChar != '#') + formatRunIn(); + else + isInLineBreak = true; + } + else if (isCharImmediatelyPostCloseBlock + && shouldBreakOneLineStatements + && !isCharImmediatelyPostComment + && ((isLegalNameChar(currentChar) && currentChar != '.') + || currentChar == '+' + || currentChar == '-' + || currentChar == '*' + || currentChar == '&' + || currentChar == '(')) + { + previousCommandChar = ' '; + isInLineBreak = true; + } +} + +bool ASFormatter::handlePotentialHeader(const std::string *&newHeader) +{ + isNonParenHeader = false; + foundClosingHeader = false; + + newHeader = findHeader(headers); + + // java can have a 'default' not in a switch + if (newHeader == &ASResource::AS_DEFAULT + && ASBeautifier::peekNextChar( + currentLine, charNum + (*newHeader).length() - 1) != ':') + newHeader = nullptr; + // Qt headers may be variables in C++ + if (isCStyle() + && (newHeader == &ASResource::AS_FOREVER || newHeader == &ASResource::AS_FOREACH)) + { + if (currentLine.find_first_of("=;", charNum) != std::string::npos) + newHeader = nullptr; + } + if (isJavaStyle() + && (newHeader == &ASResource::AS_SYNCHRONIZED)) + { + // want synchronized statements not synchronized methods + if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE)) + newHeader = nullptr; + } + else if (newHeader == &ASResource::AS_USING + && ASBeautifier::peekNextChar( + currentLine, charNum + (*newHeader).length() - 1) != '(') + newHeader = nullptr; + + if (newHeader != nullptr) + { + foundClosingHeader = isClosingHeader(newHeader); + + if (!foundClosingHeader) + { + // these are closing headers + if ((newHeader == &ASResource::AS_WHILE && currentHeader == &ASResource::AS_DO) + || (newHeader == &ASResource::_AS_FINALLY && currentHeader == &ASResource::_AS_TRY) + || (newHeader == &ASResource::_AS_EXCEPT && currentHeader == &ASResource::_AS_TRY)) + foundClosingHeader = true; + // don't append empty block for these related headers + else if (isSharpStyle() + && previousNonWSChar == '}' + && ((newHeader == &ASResource::AS_SET && currentHeader == &ASResource::AS_GET) + || (newHeader == &ASResource::AS_REMOVE && currentHeader == &ASResource::AS_ADD)) + && isOkToBreakBlock(braceTypeStack->back())) + isAppendPostBlockEmptyLineRequested = false; + } + + previousHeader = currentHeader; + currentHeader = newHeader; + needHeaderOpeningBrace = true; + + // is the previous statement on the same line? + if ((previousNonWSChar == ';' || previousNonWSChar == ':') + && !isInLineBreak + && isOkToBreakBlock(braceTypeStack->back())) + { + // if breaking lines, break the line at the header + // except for multiple 'case' statements on a line + if (maxCodeLength != std::string::npos + && previousHeader != &ASResource::AS_CASE) + isInLineBreak = true; + else + isHeaderInMultiStatementLine = true; + } + + if (foundClosingHeader && previousNonWSChar == '}') + { + if (isOkToBreakBlock(braceTypeStack->back())) + isLineBreakBeforeClosingHeader(); + + // get the adjustment for a comment following the closing header + if (isInLineBreak) + nextLineSpacePadNum = getNextLineCommentAdjustment(); + else + spacePadNum = getCurrentLineCommentAdjustment(); + } + + // check if the found header is non-paren header + isNonParenHeader = findHeader(nonParenHeaders) != nullptr; + + if (isNonParenHeader + && (currentHeader == &ASResource::AS_CATCH + || currentHeader == &ASResource::AS_CASE)) + { + int startChar = charNum + currentHeader->length() - 1; + if (ASBeautifier::peekNextChar(currentLine, startChar) == '(') + isNonParenHeader = false; + } + + // join 'else if' statements + if (currentHeader == &ASResource::AS_IF + && previousHeader == &ASResource::AS_ELSE + && isInLineBreak + && !shouldBreakElseIfs + && !isCharImmediatelyPostLineComment + && !isImmediatelyPostPreprocessor) + { + // 'else' must be last thing on the line + size_t start = formattedLine.length() >= 6 ? formattedLine.length() - 6 : 0; + if (formattedLine.find(ASResource::AS_ELSE, start) != std::string::npos) + { + appendSpacePad(); + isInLineBreak = false; + } + } + + appendSequence(*currentHeader); + goForward(currentHeader->length() - 1); + // if a paren-header is found add a space after it, if needed + // this checks currentLine, appendSpacePad() checks formattedLine + // in 'case' and C# 'catch' can be either a paren or non-paren header + if (shouldPadHeader + && !isNonParenHeader + && charNum < (int) currentLine.length() - 1 && !std::isblank(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 != &ASResource::AS_CASE && currentHeader != &ASResource::AS_DEFAULT + && !(foundClosingHeader && currentHeader == &ASResource::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; + } + } + + // #569 + if (shouldBreakBlocks + && isOkToBreakBlock(braceTypeStack->back()) + && !isHeaderInMultiStatementLine) + { + if (previousHeader == nullptr + && !foundClosingHeader + && !isCharImmediatelyPostOpenBlock + && !isImmediatelyPostCommentOnly) + { + isPrependPostBlockEmptyLineRequested = true; + } + + if (isClosingHeader(currentHeader) + || foundClosingHeader) + { + isPrependPostBlockEmptyLineRequested = false; + } + + if (shouldBreakClosingHeaderBlocks + && isCharImmediatelyPostCloseBlock + && !isImmediatelyPostCommentOnly + && !(currentHeader == &ASResource::AS_WHILE // do-while + && foundClosingHeader)) + { + isPrependPostBlockEmptyLineRequested = true; + } + } + + if (currentHeader == &ASResource::AS_CASE + || currentHeader == &ASResource::AS_DEFAULT) + isInCase = true; + + return false; + } + if ((newHeader = findHeader(preDefinitionHeaders)) != nullptr + && parenStack->back() == 0 + && !isInEnum) // not C++11 enum class + { + if (newHeader == &ASResource::AS_NAMESPACE || newHeader == &ASResource::AS_MODULE) + foundNamespaceHeader = true; + if (newHeader == &ASResource::AS_CLASS) + foundClassHeader = true; + if (newHeader == &ASResource::AS_STRUCT) + foundStructHeader = true; + if (newHeader == &ASResource::AS_INTERFACE && !foundNamespaceHeader && !foundClassHeader) + foundInterfaceHeader = true; + foundPreDefinitionHeader = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + return false; + } + if ((newHeader = findHeader(preCommandHeaders)) != nullptr) + { + // must be after function arguments + if (previousNonWSChar == ')') + foundPreCommandHeader = true; + } + else if ((newHeader = findHeader(castOperators)) != nullptr) + { + foundCastOperator = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + return false; + } + return true; +} + + +void ASFormatter::handleEndOfBlock() +{ + if (currentChar == ';' && !isInAsmBlock) + { + squareBracketCount = 0; + + methodBreakCharNum = std::string::npos; + methodBreakLineNum = 0; + methodAttachCharNum = std::string::npos; + methodAttachLineNum = 0; + + if (((shouldBreakOneLineStatements + || isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)) + && isOkToBreakBlock(braceTypeStack->back())) + && !(attachClosingBraceMode && peekNextChar() == '}')) + { + passedSemicolon = true; + } + else if (!shouldBreakOneLineStatements + && ASBeautifier::getNextWord(currentLine, charNum) == ASResource::AS_ELSE) + { + // handle special case of "else" at the end of line + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ') + passedSemicolon = true; + } + +//is set in struct case? #518 #569 + if (shouldBreakBlocks + && currentHeader != nullptr + && currentHeader != &ASResource::AS_CASE + && currentHeader != &ASResource::AS_DEFAULT + && !isHeaderInMultiStatementLine + && parenStack->back() == 0 + ) + { + isAppendPostBlockEmptyLineRequested = true; + } + } + if (currentChar != ';' + || foundStructHeader // #518 + || (needHeaderOpeningBrace && parenStack->back() == 0)) + currentHeader = nullptr; + + resetEndOfStatement(); +} + +void ASFormatter::handleColonSection() +{ + if (isInCase) + { + isInCase = false; + if (shouldBreakOneLineStatements) + passedColon = true; + } + else if (isCStyle() // for C/C++ only + && isOkToBreakBlock(braceTypeStack->back()) + && shouldBreakOneLineStatements + && !foundQuestionMark // not in a ?: sequence + && !foundPreDefinitionHeader // not in a definition block + && previousCommandChar != ')' // not after closing paren of a method header + && !foundPreCommandHeader // not after a 'noexcept' + && squareBracketCount == 0 // not in objC method call + && !isInObjCMethodDefinition // not objC '-' or '+' method + && !isInObjCInterface // not objC @interface + && !isInObjCSelector // not objC @selector + && !isDigit(peekNextChar()) && !lineStartsWithNumericType(currentLine) // not a bit field xxxx + && !isInEnum // not an enum with a base type + && !isInStruct // not an struct + && !isInContinuedPreProc // not in preprocessor line + && !isInAsm // not in extended assembler + && !isInAsmOneLine // not in extended assembler + && !isInAsmBlock) // not in extended assembler + { + passedColon = true; + } + + if (isObjCStyle() + && (squareBracketCount > 0 || isInObjCMethodDefinition || isInObjCSelector) + && !foundQuestionMark) // not in a ?: sequence + { + isImmediatelyPostObjCMethodPrefix = false; + isInObjCReturnType = false; + isInObjCParam = true; + if (shouldPadMethodColon) + padObjCMethodColon(); + } + + if (isInObjCInterface) + { + appendSpacePad(); + if ((int) currentLine.length() > charNum + 1 + && !std::isblank(currentLine[charNum + 1])) + currentLine.insert(charNum + 1, " "); + } + + if (isClassInitializer()) + { + isInClassInitializer = true; + } +} + +void ASFormatter::handlePotentialHeaderPart2() +{ + //GL30 + if (!isGSCStyle() && (findKeyword(currentLine, charNum, ASResource::AS_NEW) + || findKeyword(currentLine, charNum, ASResource::AS_DELETE))) + { + isInPotentialCalculation = false; + isImmediatelyPostNewDelete = true; + } + + //https://sourceforge.net/p/astyle/bugs/464/ + GH16 + if (isSharpStyle() && findKeyword(currentLine, charNum, ASResource::AS_NEW) + && currentHeader != &ASResource::AS_FOREACH + && currentHeader != &ASResource::AS_FOR + && currentHeader != &ASResource::AS_USING + && currentHeader != &ASResource::AS_WHILE + && currentHeader != &ASResource::AS_IF + && currentLine.find(ASResource::AS_PUBLIC) == std::string::npos + && currentLine.find(ASResource::AS_PROTECTED) == std::string::npos + && currentLine.find(ASResource::AS_PRIVATE) == std::string::npos + ) + { + isInAllocator = true; + } + + if (findKeyword(currentLine, charNum, ASResource::AS_RETURN)) + { + isInPotentialCalculation = true; + isImmediatelyPostReturn = true; // return is the same as an = sign + } + + if (findKeyword(currentLine, charNum, ASResource::AS_OPERATOR)) + isImmediatelyPostOperator = true; + + if (findKeyword(currentLine, charNum, ASResource::AS_ENUM)) + { + size_t firstNum = currentLine.find_first_of("(){},/"); + if (firstNum == std::string::npos + || currentLine[firstNum] == '{' + || currentLine[firstNum] == '/') + isInEnum = true; + } + + if (findKeyword(currentLine, charNum, ASResource::AS_TYPEDEF_STRUCT) || findKeyword(currentLine, charNum, ASResource::AS_STRUCT)) + { + size_t firstNum = currentLine.find_first_of("(){},/"); + + if (firstNum == std::string::npos + || currentLine[firstNum] == '{' + || currentLine[firstNum] == '/') + { + isInStruct = true; + } + } + + if (isCStyle() + && findKeyword(currentLine, charNum, ASResource::AS_THROW) + && previousCommandChar != ')' + && !foundPreCommandHeader) // 'const' throw() + isImmediatelyPostThrow = true; + + if (isCStyle() && findKeyword(currentLine, charNum, ASResource::AS_EXTERN) && isExternC()) + isInExternC = true; + + if (isCStyle() && findKeyword(currentLine, charNum, ASResource::AS_AUTO) + && (isBraceType(braceTypeStack->back(), NULL_TYPE) + || isBraceType(braceTypeStack->back(), DEFINITION_TYPE)) + && (currentLine.find('(') != std::string::npos)) // #516 auto array initializer with braces should not be blocks + foundTrailingReturnType = true; + + // check for break/attach return type + if (shouldBreakReturnType || shouldBreakReturnTypeDecl + || shouldAttachReturnType || shouldAttachReturnTypeDecl) + { + if ((isBraceType(braceTypeStack->back(), NULL_TYPE) + || isBraceType(braceTypeStack->back(), DEFINITION_TYPE)) + && !returnTypeChecked + && !foundNamespaceHeader + && !foundClassHeader + && !isInObjCMethodDefinition + // bypass objective-C and java @ character + && charNum == (int) currentLine.find_first_not_of(" \t") + + // possibly related to #504 + && !(isCStyle() && isCharPotentialHeader(currentLine, charNum) + && (findKeyword(currentLine, charNum, ASResource::AS_PUBLIC) + || findKeyword(currentLine, charNum, ASResource::AS_PRIVATE) + || findKeyword(currentLine, charNum, ASResource::AS_PROTECTED))) + ) + { + findReturnTypeSplitPoint(currentLine); + returnTypeChecked = true; + } + } + + // Objective-C NSException macros are preCommandHeaders + if (isCStyle() && findKeyword(currentLine, charNum, ASResource::AS_NS_DURING)) + foundPreCommandMacro = true; + if (isCStyle() && findKeyword(currentLine, charNum, ASResource::AS_NS_HANDLER)) + foundPreCommandMacro = true; + + if (isCStyle() && isExecSQL(currentLine, charNum)) + isInExecSQL = true; + + if (isCStyle()) + { + if (findKeyword(currentLine, charNum, ASResource::AS_ASM) + || findKeyword(currentLine, charNum, ASResource::AS__ASM__)) + { + isInAsm = true; + } + else if (findKeyword(currentLine, charNum, ASResource::AS_MS_ASM) // microsoft specific + || findKeyword(currentLine, charNum, ASResource::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, ASResource::AS_STATIC) + && isNextCharOpeningBrace(charNum + 6))) + isJavaStaticConstructor = true; + + if (isSharpStyle() + && (findKeyword(currentLine, charNum, ASResource::AS_DELEGATE) + || findKeyword(currentLine, charNum, ASResource::AS_UNCHECKED))) + isSharpDelegate = true; + + // append the entire name + std::string_view name = getCurrentWord(currentLine, charNum); + // must pad the 'and' and 'or' operators if required + if (name == "and" || name == "or") + { + if (shouldPadOperators && previousNonWSChar != ':') + { + appendSpacePad(); + appendOperator(std::string(name)); + goForward(name.length() - 1); + if (!isBeforeAnyComment() + && !(currentLine.compare(charNum + 1, 1, ASResource::AS_SEMICOLON) == 0) + && !(currentLine.compare(charNum + 1, 2, ASResource::AS_SCOPE_RESOLUTION) == 0)) + appendSpaceAfter(); + } + else + { + appendOperator(std::string(name)); + goForward(name.length() - 1); + } + } + else + { + appendSequence(std::string(name)); + goForward(name.length() - 1); + } +} + +void ASFormatter::handlePotentialOperator(const std::string *&newHeader) +{ + + // check for Java ? wildcard + if (newHeader != nullptr + && newHeader == &ASResource::AS_GCC_MIN_ASSIGN + && isJavaStyle() + && isInTemplate) + newHeader = nullptr; + + if (newHeader != nullptr) + { + if (newHeader == &ASResource::AS_LAMBDA) + foundPreCommandHeader = true; + + // correct mistake of two >> closing a template + if (isInTemplate && (newHeader == &ASResource::AS_GR_GR || newHeader == &ASResource::AS_GR_GR_GR)) + newHeader = &ASResource::AS_GR; + + if (!isInPotentialCalculation) + { + // must determine if newHeader is an assignment operator + // do NOT use findOperator - the length must be exact!!! + if (find(begin(*assignmentOperators), end(*assignmentOperators), newHeader) + != end(*assignmentOperators)) + { + foundPreCommandHeader = false; + char peekedChar = peekNextChar(); + isInPotentialCalculation = !(newHeader == &ASResource::AS_EQUAL && peekedChar == '*') + && !(newHeader == &ASResource::AS_EQUAL && peekedChar == '&') + && !isCharImmediatelyPostOperator; + } + } + } +} + +void ASFormatter::handleParens() +{ + if (currentChar == '(') + { + if (shouldPadHeader + && (isCharImmediatelyPostReturn + || isCharImmediatelyPostThrow + || isCharImmediatelyPostNewDelete)) + appendSpacePad(); + } + + if (shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen) + padParensOrBrackets('(', ')', shouldPadFirstParen); + else + appendCurrentChar(); + + if (isInObjCMethodDefinition) + { + if (currentChar == '(' && isImmediatelyPostObjCMethodPrefix) + { + if (shouldPadMethodPrefix || shouldUnPadMethodPrefix) + padObjCMethodPrefix(); + isImmediatelyPostObjCMethodPrefix = false; + isInObjCReturnType = true; + } + else if (currentChar == ')' && isInObjCReturnType) + { + if (shouldPadReturnType || shouldUnPadReturnType) + padObjCReturnType(); + isInObjCReturnType = false; + } + else if (isInObjCParam + && (shouldPadParamType || shouldUnPadParamType)) + padObjCParamType(); + } +} + + +void ASFormatter::handleOpenParens() +{ + questionMarkStack->push_back(foundQuestionMark); + foundQuestionMark = false; + parenStack->back()++; + if (currentChar == '[') + { + ++squareBracketCount; + if (getAlignMethodColon() && squareBracketCount == 1 && + isCStyle()) + objCColonAlign = findObjCColonAlignment(); + } + if (currentChar == '(') + { + ++parenthesesCount; + } +} + +void ASFormatter::formatFirstOpenBrace(BraceType braceType) +{ + if (braceFormatMode == ATTACH_MODE || braceFormatMode == LINUX_MODE) + { + // break an enum if mozilla + if (isBraceType(braceType, ENUM_TYPE) + && formattingStyle == STYLE_MOZILLA + && !(!shouldBreakOneLineBlocks && formattedLine.find('}' != std::string::npos) ) // GL38 + ) + { + isInLineBreak = true; + appendCurrentChar(); // don't attach + } + // don't attach to a preprocessor directive or '\' line + else if ((isImmediatelyPostPreprocessor + || (!formattedLine.empty() + && formattedLine[formattedLine.length() - 1] == '\\')) + && currentLineBeginsWithBrace) + { + isInLineBreak = true; + appendCurrentChar(); // don't attach + } + else if (isCharImmediatelyPostComment) + { + // TODO: attach brace to line-end comment + appendCurrentChar(); // don't attach + } + else if (isCharImmediatelyPostLineComment && !isBraceType(braceType, SINGLE_LINE_TYPE)) + { + appendCharInsideComments(); + } + else + { + // if a blank line precedes this don't attach + if (isEmptyLine(formattedLine)) + appendCurrentChar(); // don't attach + else + { + // if brace is broken or not an assignment + if (currentLineBeginsWithBrace + && !isBraceType(braceType, SINGLE_LINE_TYPE)) + { + appendSpacePad(); + appendCurrentChar(false); // OK to attach + // TODO: debug the following line + testForTimeToSplitFormattedLine(); // line length will have changed + + if (currentLineBeginsWithBrace && currentLineFirstBraceNum == (size_t) charNum) + shouldBreakLineAtNextChar = true; + } + else + { + if (previousNonWSChar != '(') + { + // don't space pad C++11 uniform initialization + if (!isBraceType(braceType, INIT_TYPE)) + appendSpacePad(); + } + appendCurrentChar(); + } + } + } + } + else if (braceFormatMode == BREAK_MODE) + { + if (std::isblank(peekNextChar()) && !isInVirginLine) + breakLine(); + else if (isBeforeAnyComment() && sourceIterator->hasMoreLines()) + { + // do not break unless comment is at line end + if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace) + { + currentChar = ' '; // remove brace from current line + appendOpeningBrace = true; // append brace to following line + } + } + if (!isInLineBreak && previousNonWSChar != '(') + { + // don't space pad C++11 uniform initialization + if (!isBraceType(braceType, INIT_TYPE)) + appendSpacePad(); + } + appendCurrentChar(); + + if (currentLineBeginsWithBrace + && currentLineFirstBraceNum == (size_t) charNum + && !isBraceType(braceType, SINGLE_LINE_TYPE)) + shouldBreakLineAtNextChar = true; + } + else if (braceFormatMode == RUN_IN_MODE) + { + if (std::isblank(peekNextChar()) && !isInVirginLine) + breakLine(); + else if (isBeforeAnyComment() && sourceIterator->hasMoreLines()) + { + // do not break unless comment is at line end + if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace) + { + currentChar = ' '; // remove brace from current line + appendOpeningBrace = true; // append brace to following line + } + } + if (!isInLineBreak && previousNonWSChar != '(') + { + // don't space pad C++11 uniform initialization + if (!isBraceType(braceType, INIT_TYPE)) + appendSpacePad(); + } + appendCurrentChar(); + } + else if (braceFormatMode == NONE_MODE) + { + if (currentLineBeginsWithBrace + && (size_t) charNum == currentLineFirstBraceNum) + { + appendCurrentChar(); // don't attach + } + else + { + if (previousNonWSChar != '(') + { + // don't space pad C++11 uniform initialization + if (!isBraceType(braceType, INIT_TYPE)) + appendSpacePad(); + } + appendCurrentChar(false); // OK to attach + } + } +} + +void ASFormatter::formatOpenBrace() +{ + if (braceFormatMode == RUN_IN_MODE) + { + if (previousNonWSChar == '{' + && braceTypeStack->size() > 2 + && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2], + SINGLE_LINE_TYPE)) + formatArrayRunIn(); + } + else if (!isInLineBreak + && !std::isblank(peekNextChar()) + && previousNonWSChar == '{' + && braceTypeStack->size() > 2 + && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2], + SINGLE_LINE_TYPE)) + formatArrayRunIn(); + + appendCurrentChar(); +} + +void ASFormatter::formatCloseBrace(BraceType braceType) +{ + if (attachClosingBraceMode) + { + if (isEmptyLine(formattedLine) // if a blank line precedes this + || isImmediatelyPostPreprocessor + || isCharImmediatelyPostLineComment + || isCharImmediatelyPostComment) + appendCurrentChar(); // don't attach + else + { + appendSpacePad(); + appendCurrentChar(false); // attach + } + } + else + { + // does this close the first opening brace in the array? + // must check if the block is still a single line because of anonymous statements + if (!isBraceType(braceType, INIT_TYPE) + && (!isBraceType(braceType, SINGLE_LINE_TYPE) + || formattedLine.find('{') == std::string::npos)) + breakLine(); + appendCurrentChar(); + } + + // if a declaration follows an enum definition, space pad + char peekedChar = peekNextChar(); + if ((isLegalNameChar(peekedChar) && peekedChar != '.') + || peekedChar == '[') + appendSpaceAfter(); +} + + + +std::string ASFormatter::nextLine() +{ + const std::string* newHeader = nullptr; + isInVirginLine = isVirgin; + isCharImmediatelyPostComment = false; + isPreviousCharPostComment = false; + isCharImmediatelyPostLineComment = false; + isCharImmediatelyPostOpenBlock = false; + isCharImmediatelyPostCloseBlock = false; + isCharImmediatelyPostTemplate = false; + + while (!isLineReady) + { + if (shouldReparseCurrentChar) + shouldReparseCurrentChar = false; + else if (!getNextChar()) + { + breakLine(); + continue; + } + else // stuff to do when reading a new character... + { + // make sure that a virgin '{' at the beginning of the file will be treated as a block... + if (isInVirginLine && currentChar == '{' + && currentLineBeginsWithBrace + && previousCommandChar == ' ') + previousCommandChar = '{'; + if (isInClassInitializer + && isBraceType(braceTypeStack->back(), COMMAND_TYPE)) + isInClassInitializer = false; + if (isInBraceRunIn) + isInLineBreak = false; + if (!std::isblank(currentChar)) + isInBraceRunIn = false; + isPreviousCharPostComment = isCharImmediatelyPostComment; + isCharImmediatelyPostComment = false; + isCharImmediatelyPostTemplate = false; + isCharImmediatelyPostReturn = false; + isCharImmediatelyPostThrow = false; + isCharImmediatelyPostNewDelete = false; + isCharImmediatelyPostOperator = false; + isCharImmediatelyPostPointerOrReference = false; + isCharImmediatelyPostOpenBlock = false; + isCharImmediatelyPostCloseBlock = false; + } + + if ((lineIsLineCommentOnly || lineIsCommentOnly) + && currentLine.find("*INDENT-ON*", charNum) != std::string::npos + && isFormattingModeOff) + { + isFormattingModeOff = false; + breakLine(); + formattedLine = currentLine; + charNum = (int) currentLine.length() - 1; + continue; + } + if (isFormattingModeOff) + { + breakLine(); + formattedLine = currentLine; + charNum = (int) currentLine.length() - 1; + continue; + } + + if ((lineIsLineCommentOnly || lineIsCommentOnly) + && currentLine.find("*INDENT-OFF*", charNum) != std::string::npos) + { + isFormattingModeOff = true; + if (isInLineBreak) // is true if not the first line + breakLine(); + formattedLine = currentLine; + charNum = (int) currentLine.length() - 1; + continue; + } + + if (shouldBreakLineAtNextChar) + { + if (std::isblank(currentChar) && !lineIsEmpty) + continue; + isInLineBreak = true; + shouldBreakLineAtNextChar = false; + } + + if (isInExecSQL && !passedSemicolon) + { + if (currentChar == ';') + passedSemicolon = true; + appendCurrentChar(); + continue; + } + + if (isInLineComment) + { + formatLineCommentBody(); + continue; + } + + if (isInComment) + { + formatCommentBody(); + continue; + } + + if (isInQuote) + { + formatQuoteBody(); + continue; + } + + // not in quote or comment or line comment + + if (isSequenceReached(ASResource::AS_OPEN_LINE_COMMENT)) + { + formatLineCommentOpener(); + testForTimeToSplitFormattedLine(); + continue; + } + if (isSequenceReached(ASResource::AS_OPEN_COMMENT) || (isGSCStyle() && isSequenceReached(ASResource::AS_GSC_OPEN_COMMENT))) + { + formatCommentOpener(); + testForTimeToSplitFormattedLine(); + continue; + } + if (currentChar == '"' + || (currentChar == '\'' && !isDigitSeparator(currentLine, charNum))) + { + formatQuoteOpener(); + testForTimeToSplitFormattedLine(); + continue; + } + // treat these preprocessor statements as a line comment + + if ((currentChar == '#') + && currentLine.find_first_not_of(" \t") == (size_t) charNum) + { + isInContinuedPreProc = currentLine[currentLine.size() - 1] == '\\'; + } + + if (isInPreprocessor) + { + appendCurrentChar(); + continue; + } + + if (isInTemplate && shouldCloseTemplates) + { + if (previousNonWSChar == '>' && std::isblank(currentChar) && peekNextChar() == '>') + continue; + } + + if (shouldRemoveNextClosingBrace && currentChar == '}') + { + currentLine[charNum] = currentChar = ' '; + shouldRemoveNextClosingBrace = false; + assert(adjustChecksumIn(-'}')); + if (isEmptyLine(currentLine)) + continue; + } + + // handle white space - needed to simplify the rest. + if (std::isblank(currentChar)) + { + appendCurrentChar(); + continue; + } + + /* not in MIDDLE of quote or comment or SQL or white-space of any type ... */ + + // check if in preprocessor + // ** isInPreprocessor will be automatically reset at the beginning + // of a new line in getnextChar() + if (currentChar == '#' && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)) + { + isInPreprocessor = true; + // check for run-in + if (!formattedLine.empty() && formattedLine[0] == '{') + { + isInLineBreak = true; + isInBraceRunIn = false; + } + processPreprocessor(); + } + + /* not in preprocessor ... */ + + if (isImmediatelyPostComment) + { + caseHeaderFollowsComments = false; + isImmediatelyPostComment = false; + isCharImmediatelyPostComment = true; + } + + if (isImmediatelyPostLineComment) + { + caseHeaderFollowsComments = false; + isImmediatelyPostLineComment = false; + isCharImmediatelyPostLineComment = true; + } + + if (isImmediatelyPostReturn) + { + isImmediatelyPostReturn = false; + isCharImmediatelyPostReturn = true; + } + + if (isImmediatelyPostThrow) + { + isImmediatelyPostThrow = false; + isCharImmediatelyPostThrow = true; + } + + if (isImmediatelyPostNewDelete) + { + isImmediatelyPostNewDelete = false; + isCharImmediatelyPostNewDelete = true; + } + + if (isImmediatelyPostOperator) + { + isImmediatelyPostOperator = false; + isCharImmediatelyPostOperator = true; + } + if (isImmediatelyPostTemplate) + { + isImmediatelyPostTemplate = false; + isCharImmediatelyPostTemplate = true; + } + if (isImmediatelyPostPointerOrReference) + { + isImmediatelyPostPointerOrReference = false; + isCharImmediatelyPostPointerOrReference = true; + } + + // reset isImmediatelyPostHeader information + if (isImmediatelyPostHeader) + { + if (!handleImmediatelyPostHeaderSection()) + continue; + } + + if (passedSemicolon) // need to break the formattedLine + { + if (!handlePassedSemicolonSection()) + continue; + } + + if (passedColon) + { + passedColon = false; + if (parenStack->back() == 0 + && !isBeforeAnyComment() + && (formattedLine.find_first_not_of(" \t") != std::string::npos)) + { + shouldReparseCurrentChar = true; + isInLineBreak = true; + continue; + } + } + + // Check if in template declaration, e.g. foo or foo + if (!isInTemplate && currentChar == '<') + { + checkIfTemplateOpener(); + } + + // Check for break return type + if ((size_t) charNum >= methodBreakCharNum && methodBreakLineNum == 0) + { + if ((size_t) charNum == methodBreakCharNum) + isInLineBreak = true; + methodBreakCharNum = std::string::npos; + methodBreakLineNum = 0; + } + // Check for attach return type + if ((size_t) charNum >= methodAttachCharNum && methodAttachLineNum == 0) + { + handleAttachedReturnTypes(); + } + + // handle parens + if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<')) + { + handleOpenParens(); + } + + else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>')) + { + handleClosedBracesOrParens(); + } + + // handle braces + if (currentChar == '{' || currentChar == '}') + { + handleBraces(); + continue; + } + + // #126 + if ( currentChar == '*' && shouldPadOperators && + pointerAlignment != PTR_ALIGN_TYPE && // SF 557 + peekNextChar() != '=' && // GH 81 + ( currentHeader == &ASResource::AS_IF || currentHeader == &ASResource::AS_WHILE || + currentHeader == &ASResource::AS_DO || currentHeader == &ASResource::AS_FOR) + && ( previousChar == ')' || std::isalpha(previousChar) ) + && !isOperatorPaddingDisabled() ) + { + appendSpacePad(); + appendOperator(ASResource::AS_MULT); + goForward(0); + appendSpaceAfter(); + continue; + } + + if ((((previousCommandChar == '{' && isPreviousBraceBlockRelated) + || ((previousCommandChar == '}' + && !isImmediatelyPostEmptyBlock + && isPreviousBraceBlockRelated + && !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments + && peekNextChar() != ' ' + && !isBraceType(previousBraceType, DEFINITION_TYPE)) + && !isBraceType(braceTypeStack->back(), DEFINITION_TYPE))) + && isOkToBreakBlock(braceTypeStack->back())) + // check for array + || (previousCommandChar == '{' // added 9/30/2010 + && isBraceType(braceTypeStack->back(), ARRAY_TYPE) + && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE) + && isNonInStatementArray) + // check for pico one line braces + || (formattingStyle == STYLE_PICO + && (previousCommandChar == '{' && isPreviousBraceBlockRelated) + && isBraceType(braceTypeStack->back(), COMMAND_TYPE) + && isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE) + && braceFormatMode == RUN_IN_MODE) + ) + { + handleBreakLine(); + } + + // reset block handling flags + isImmediatelyPostEmptyBlock = false; + + // Objective-C method prefix with no return type + if (isImmediatelyPostObjCMethodPrefix && currentChar != '(') + { + if (shouldPadMethodPrefix || shouldUnPadMethodPrefix) + padObjCMethodPrefix(); + isImmediatelyPostObjCMethodPrefix = false; + } + + // look for headers + bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum); + + if (isPotentialHeader && !isInTemplate && squareBracketCount == 0) + { + if (!handlePotentialHeader(newHeader)) + continue; + } // (isPotentialHeader && !isInTemplate) + + if (isInLineBreak) // OK to break line here + { + breakLine(); + if (isInVirginLine) // adjust for the first line + { + lineCommentNoBeautify = lineCommentNoIndent; + lineCommentNoIndent = false; + if (isImmediatelyPostPreprocessor) + { + isInIndentablePreproc = isIndentablePreprocessor; + isIndentablePreprocessor = false; + } + } + } + + if (previousNonWSChar == '}' || currentChar == ';') + { + handleEndOfBlock(); + } + + if (currentChar == ':' + && previousChar != ':' // not part of '::' + && peekNextChar() != ':') // not part of '::' + { + handleColonSection(); + } + + if (currentChar == '?') + foundQuestionMark = true; + + if (isPotentialHeader && !isInTemplate) + { + handlePotentialHeaderPart2(); + continue; + } // (isPotentialHeader && !isInTemplate) + + // determine if this is an Objective-C statement + + if (currentChar == '@' + && isCStyle() + && (int) currentLine.length() > charNum + 1 + && !std::isblank(currentLine[charNum + 1]) + && isCharPotentialHeader(currentLine, charNum + 1) + && findKeyword(currentLine, charNum + 1, ASResource::AS_INTERFACE) + && isBraceType(braceTypeStack->back(), NULL_TYPE)) + { + isInObjCInterface = true; + std::string name = '@' + ASResource::AS_INTERFACE; + appendSequence(name); + goForward(name.length() - 1); + continue; + } + if (currentChar == '@' + && isCStyle() + && (int) currentLine.length() > charNum + 1 + && !std::isblank(currentLine[charNum + 1]) + && isCharPotentialHeader(currentLine, charNum + 1) + && findKeyword(currentLine, charNum + 1, ASResource::AS_SELECTOR)) + { + isInObjCSelector = true; + std::string name = '@' + ASResource::AS_SELECTOR; + appendSequence(name); + goForward(name.length() - 1); + continue; + } + if ((currentChar == '-' || currentChar == '+') + && isCStyle() + && (int) currentLine.find_first_not_of(" \t") == charNum + && !isInPotentialCalculation + && !isInObjCMethodDefinition + && (isBraceType(braceTypeStack->back(), NULL_TYPE) + || (isBraceType(braceTypeStack->back(), EXTERN_TYPE)))) + { + isInObjCMethodDefinition = true; + isImmediatelyPostObjCMethodPrefix = true; + isInObjCParam = false; + isInObjCInterface = false; + if (getAlignMethodColon()) + objCColonAlign = findObjCColonAlignment(); + appendCurrentChar(); + continue; + } + + // determine if this is a potential calculation + + bool isPotentialOperator = isCharPotentialOperator(currentChar); + newHeader = nullptr; + + // Skip operator detection for git conflict markers + if (isPotentialOperator && !isGitConflictMarker(currentLine)) + { + newHeader = findOperator(operators); + + handlePotentialOperator(newHeader); + } + + // TODO check add flag to preserve space + size_t lastNonWsChar = currentLine.find_last_not_of(" \t", charNum - 1); + if (lastNonWsChar != std::string::npos && pointerAlignment == PTR_ALIGN_TYPE && !isGSCStyle() && !preserveWhitespace) + { + char lastChar = currentLine[lastNonWsChar]; + + //if (lastChar != '(' && !isalpha(lastChar)) { + // formattedLine = rtrim(formattedLine); + //} + + if (lastChar == ',') + { + formattedLine = rtrim(formattedLine); + formattedLine += ' '; + } + + } + + // process pointers and references + // check newHeader to eliminate things like '&&' sequence + if (newHeader != nullptr && !isJavaStyle() + && (newHeader == &ASResource::AS_MULT + || newHeader == &ASResource::AS_BIT_AND + || newHeader == &ASResource::AS_BIT_XOR + || newHeader == &ASResource::AS_AND) + && isPointerOrReference()) + { + + if (!isDereferenceOrAddressOf() && !isOperatorPaddingDisabled()) + { + formatPointerOrReference(); + } + else + { + appendOperator(*newHeader); + goForward(newHeader->length() - 1); + } + isImmediatelyPostPointerOrReference = true; + continue; + } + + if ((shouldPadOperators || negationPadMode != NEGATION_PAD_NO_CHANGE) && newHeader != nullptr && !isOperatorPaddingDisabled()) + { + padOperators(newHeader); + continue; + } + + // remove spaces before commas + if (currentChar == ',') + { + const size_t len = formattedLine.length(); + size_t lastText = formattedLine.find_last_not_of(' '); + if (lastText != std::string::npos && lastText < len - 1) + { + formattedLine.resize(lastText + 1); + int size_diff = len - (lastText + 1); + spacePadNum -= size_diff; + } + } + + // pad commas and semi-colons + if (currentChar == ';' + || (currentChar == ',' && (shouldPadOperators || shouldPadCommas))) + { + char nextChar = ' '; + if (charNum + 1 < (int) currentLine.length()) + nextChar = currentLine[charNum + 1]; + if (!std::isblank(nextChar) + && nextChar != '}' + && nextChar != ')' + && nextChar != ']' + && nextChar != '>' + && nextChar != ';' + && !isBeforeAnyComment() + ) + { + appendCurrentChar(); + appendSpaceAfter(); + continue; + } + } + + // pad parens + if (currentChar == '(' || currentChar == ')') + { + handleParens(); + continue; + } + + //GL31 + bool isDoubleOpenBrackets = isGSCStyle() && currentChar == '[' && peekNextChar() == '['; + + if ((currentChar == '[' || currentChar == ']' ) && (shouldPadBracketsOutside || shouldPadBracketsInside || shouldUnPadBrackets) && !isDoubleOpenBrackets) + { + padParensOrBrackets('[', ']', false); + continue; + } + + // bypass the entire operator + if (newHeader != nullptr) + { + appendOperator(*newHeader); + goForward(newHeader->length() - 1); + continue; + } + + appendCurrentChar(); + + } // end of while loop * end of while loop * end of while loop * end of while loop + + // return a beautified (i.e. correctly indented) line. + + std::string beautifiedLine; + size_t readyFormattedLineLength = trim(readyFormattedLine).length(); + bool isInNamespace = isBraceType(braceTypeStack->back(), NAMESPACE_TYPE); + + if (prependEmptyLine // prepend a blank line before this formatted line + && readyFormattedLineLength > 0 + && previousReadyFormattedLineLength > 0) + { + isLineReady = true; // signal a waiting readyFormattedLine + beautifiedLine = beautify(""); + previousReadyFormattedLineLength = 0; + // call the enhancer for new empty lines + enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL); + } + else // format the current formatted line + { + isLineReady = false; + runInIndentContinuation = runInIndentChars; + beautifiedLine = beautify(readyFormattedLine); + previousReadyFormattedLineLength = readyFormattedLineLength; + // the enhancer is not called for no-indent line comments + if (!lineCommentNoBeautify && !isFormattingModeOff) + enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL); + runInIndentChars = 0; + lineCommentNoBeautify = lineCommentNoIndent; + lineCommentNoIndent = false; + isInIndentablePreproc = isIndentablePreprocessor; + isIndentablePreprocessor = false; + isElseHeaderIndent = elseHeaderFollowsComments; + isCaseHeaderCommentIndent = caseHeaderFollowsComments; + objCColonAlignSubsequent = objCColonAlign; + if (isCharImmediatelyPostNonInStmt) + { + isNonInStatementArray = false; + isCharImmediatelyPostNonInStmt = false; + } + isInPreprocessorBeautify = isInPreprocessor; // used by ASEnhancer + isInBeautifySQL = isInExecSQL; // used by ASEnhancer + } + + prependEmptyLine = false; + assert(computeChecksumOut(beautifiedLine)); + return beautifiedLine; +} + +/** + * check if there are any indented lines ready to be read by nextLine() + * + * @return are there any indented lines ready? + */ +bool ASFormatter::hasMoreLines() const +{ + return !endOfCodeReached; +} + +/** + * comparison function for BraceType enum + */ +bool ASFormatter::isBraceType(BraceType a, BraceType b) const +{ + if (a == NULL_TYPE || b == NULL_TYPE) + return (a == b); + return ((a & b) == b); +} + +/** + * set the formatting style. + * + * @param style the formatting style. + */ +void ASFormatter::setFormattingStyle(FormatStyle style) +{ + formattingStyle = style; +} + +/** + * set the add braces mode. + * options: + + * true braces added to headers for single line statements. + * false braces NOT added to headers for single line statements. + * + * @param state the add braces state. + */ +void ASFormatter::setAddBracesMode(bool state) +{ + shouldAddBraces = state; +} + +/** + * set the add one line braces mode. + * options: + * true one line braces added to headers for single line statements. + * false one line braces NOT added to headers for single line statements. + * + * @param state the add one line braces state. + */ +void ASFormatter::setAddOneLineBracesMode(bool state) +{ + shouldAddBraces = state ? 1 : 0; + shouldAddOneLineBraces = state; +} + +/** + * set the remove braces mode. + * options: + * true braces removed from headers for single line statements. + * false braces NOT removed from headers for single line statements. + * + * @param state the remove braces state. + */ +void ASFormatter::setRemoveBracesMode(bool state) +{ + shouldRemoveBraces = state; +} + +// retained for compatibility with release 2.06 +// "Brackets" have been changed to "Braces" in 3.0 +// it is referenced only by the old "bracket" options +void ASFormatter::setAddBracketsMode(bool state) +{ + setAddBracesMode(state); +} + +// retained for compatibility with release 2.06 +// "Brackets" have been changed to "Braces" in 3.0 +// it is referenced only by the old "bracket" options +void ASFormatter::setAddOneLineBracketsMode(bool state) +{ + setAddOneLineBracesMode(state); +} + +// retained for compatibility with release 2.06 +// "Brackets" have been changed to "Braces" in 3.0 +// it is referenced only by the old "bracket" options +void ASFormatter::setRemoveBracketsMode(bool state) +{ + setRemoveBracesMode(state); +} + +// retained for compatibility with release 2.06 +// "Brackets" have been changed to "Braces" in 3.0 +// it is referenced only by the old "bracket" options +void ASFormatter::setBreakClosingHeaderBracketsMode(bool state) +{ + setBreakClosingHeaderBracesMode(state); +} + +/** + * set the brace formatting mode. + * options: + * + * @param mode the brace formatting mode. + */ +void ASFormatter::setBraceFormatMode(BraceMode mode) +{ + braceFormatMode = mode; +} + +/** + * set 'break after' mode for maximum code length + * + * @param state the 'break after' mode. + */ +void ASFormatter::setBreakAfterMode(bool state) +{ + shouldBreakLineAfterLogical = state; +} + +/** + * set closing header brace breaking mode + * options: + * true braces just before closing headers (e.g. 'else', 'catch') + * will be broken, even if standard braces are attached. + * false closing header braces will be treated as standard braces. + * + * @param state the closing header brace breaking mode. + */ +void ASFormatter::setBreakClosingHeaderBracesMode(bool state) +{ + shouldBreakClosingHeaderBraces = state; +} + +/** + * set 'else if()' breaking mode + * options: + * true 'else' headers will be broken from their succeeding 'if' headers. + * false 'else' headers will be attached to their succeeding 'if' headers. + * + * @param state the 'else if()' breaking mode. + */ +void ASFormatter::setBreakElseIfsMode(bool state) +{ + shouldBreakElseIfs = state; +} + +/** +* set comma padding mode. +* options: +* true statement commas and semicolons will be padded with spaces around them. +* false statement commas and semicolons will not be padded. +* +* @param state the padding mode. +*/ +void ASFormatter::setCommaPaddingMode(bool state) +{ + shouldPadCommas = state; +} + +/** + * set maximum code length + * + * @param max the maximum code length. + */ +void ASFormatter::setMaxCodeLength(int max) +{ + maxCodeLength = max; +} + +/** + * set operator padding mode. + * options: + * true statement operators will be padded with spaces around them. + * false statement operators will not be padded. + * + * @param state the padding mode. + */ +void ASFormatter::setOperatorPaddingMode(bool state) +{ + shouldPadOperators = state; +} + +/** + * set negation padding mode. + * @param state the padding mode. + */ +void ASFormatter::setNegationPaddingMode(NegationPaddingMode mode) +{ + negationPadMode = mode; +} + +/** + * set include directive padding mode. + * @param state the padding mode. + */ +void ASFormatter::setIncludeDirectivePaddingMode(IncludeDirectivePaddingMode mode) +{ + includeDirectivePaddingMode = mode; +} + +/** + * set parenthesis outside padding mode. + * options: + * true statement parentheses will be padded with spaces around them. + * false statement parentheses will not be padded. + * + * @param state the padding mode. + */ +void ASFormatter::setParensOutsidePaddingMode(bool state) +{ + shouldPadParensOutside = state; +} + +/** + * set parenthesis inside padding mode. + * options: + * true statement parenthesis will be padded with spaces around them. + * false statement parenthesis will not be padded. + * + * @param state the padding mode. + */ +void ASFormatter::setParensInsidePaddingMode(bool state) +{ + shouldPadParensInside = state; +} + +/** + * set square brackets outside padding mode. + * options: + * true square brackets will be padded with spaces around them. + * false square brackets will not be padded. + * + * @param state the padding mode. + */ +void ASFormatter::setBracketsOutsidePaddingMode(bool state) +{ + shouldPadBracketsOutside = state; +} + +/** + * set square brackets inside padding mode. + * options: + * true square brackets will be padded with spaces around them. + * false square brackets will not be padded. + * + * @param state the padding mode. + */ +void ASFormatter::setBracketsInsidePaddingMode(bool state) +{ + shouldPadBracketsInside = 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 padding mode for empty parentheses. + * options: + * true padding will be applied + * false no padding (default) + * + * @param state the padding mode. + */ +void ASFormatter::setEmptyParensPaddingMode(bool state) +{ + shouldPadEmptyParens = 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 square brackets unpadding mode. + * options: + * true square brackets will be unpadded with spaces removed around them. + * false square brackets will not be unpadded. + * + * @param state the padding mode. + */ +void ASFormatter::setBracketsUnPaddingMode(bool state) +{ + shouldUnPadBrackets = state; +} + +/** +* set the state of the preprocessor indentation option. +* If true, #ifdef blocks at level 0 will be indented. +* +* @param state state of option. +*/ +void ASFormatter::setPreprocBlockIndent(bool state) +{ + shouldIndentPreprocBlock = state; +} + +/** + * Set strip comment prefix mode. + * options: + * true strip leading '*' in a comment. + * false leading '*' in a comment will be left unchanged. + * + * @param state the strip comment prefix mode. + */ +void ASFormatter::setStripCommentPrefix(bool state) +{ + shouldStripCommentPrefix = state; +} + +/** + * set objective-c '-' or '+' class prefix padding mode. + * options: + * true class prefix will be padded a spaces after them. + * false class prefix will be left unchanged. + * + * @param state the padding mode. + */ +void ASFormatter::setMethodPrefixPaddingMode(bool state) +{ + shouldPadMethodPrefix = state; +} + +/** + * set objective-c '-' or '+' class prefix unpadding mode. + * options: + * true class prefix will be unpadded with spaces after them removed. + * false class prefix will left unchanged. + * + * @param state the unpadding mode. + */ +void ASFormatter::setMethodPrefixUnPaddingMode(bool state) +{ + shouldUnPadMethodPrefix = state; +} + +// set objective-c '-' or '+' return type padding mode. +void ASFormatter::setReturnTypePaddingMode(bool state) +{ + shouldPadReturnType = state; +} + +// set objective-c '-' or '+' return type unpadding mode. +void ASFormatter::setReturnTypeUnPaddingMode(bool state) +{ + shouldUnPadReturnType = state; +} + +// set objective-c method parameter type padding mode. +void ASFormatter::setParamTypePaddingMode(bool state) +{ + shouldPadParamType = state; +} + +// set objective-c method parameter type unpadding mode. +void ASFormatter::setParamTypeUnPaddingMode(bool state) +{ + shouldUnPadParamType = state; +} + +/** + * set objective-c method colon padding mode. + * + * @param mode objective-c colon padding mode. + */ +void ASFormatter::setObjCColonPaddingMode(ObjCColonPad mode) +{ + shouldPadMethodColon = true; + objCColonPadMode = mode; +} + +/** + * set option to attach closing braces + * + * @param state true = attach, false = don't attach. + */ +void ASFormatter::setAttachClosingBraceMode(bool state) +{ + attachClosingBraceMode = state; +} + +/** + * set option to attach class braces + * + * @param state true = attach, false = use style default. + */ +void ASFormatter::setAttachClass(bool state) +{ + shouldAttachClass = state; +} + +/** + * set option to attach extern "C" braces + * + * @param state true = attach, false = use style default. + */ +void ASFormatter::setAttachExternC(bool state) +{ + shouldAttachExternC = state; +} + +/** + * set option to attach namespace braces + * + * @param state true = attach, false = use style default. + */ +void ASFormatter::setAttachNamespace(bool state) +{ + shouldAttachNamespace = state; +} + +/** + * set option to attach inline braces + * + * @param state true = attach, false = use style default. + */ +void ASFormatter::setAttachInline(bool state) +{ + shouldAttachInline = state; +} + +void ASFormatter::setAttachClosingWhile(bool state) +{ + shouldAttachClosingWhile = state; +} + +/** + * set option to break/not break one-line blocks + * + * @param state true = break, false = don't break. + */ +void ASFormatter::setBreakOneLineBlocksMode(bool state) +{ + shouldBreakOneLineBlocks = state; +} + +/** +* set one line headers breaking mode +*/ +void ASFormatter::setBreakOneLineHeadersMode(bool state) +{ + shouldBreakOneLineHeaders = state; +} + +/** +* set option to break/not break lines consisting of multiple statements. +* +* @param state true = break, false = don't break. +*/ +void ASFormatter::setBreakOneLineStatementsMode(bool state) +{ + shouldBreakOneLineStatements = state; +} + +void ASFormatter::setCloseTemplatesMode(bool state) +{ + shouldCloseTemplates = state; +} + +/** + * set option to convert tabs to spaces. + * + * @param state true = convert, false = don't convert. + */ +void ASFormatter::setTabSpaceConversionMode(bool state) +{ + shouldConvertTabs = state; +} + +/** + * set option to indent comments in column 1. + * + * @param state true = indent, false = don't indent. + */ +void ASFormatter::setIndentCol1CommentsMode(bool state) +{ + shouldIndentCol1Comments = state; +} + +/** + * set option to force all line ends to a particular style. + * + * @param fmt format enum value + */ +void ASFormatter::setLineEndFormat(LineEndFormat fmt) +{ + lineEnd = fmt; +} + +/** + * set option to break unrelated blocks of code with empty lines. + * + * @param state true = convert, false = don't convert. + */ +void ASFormatter::setBreakBlocksMode(bool state) +{ + shouldBreakBlocks = state; +} + +/** + * set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines. + * + * @param state true = convert, false = don't convert. + */ +void ASFormatter::setBreakClosingHeaderBlocksMode(bool state) +{ + shouldBreakClosingHeaderBlocks = state; +} + +/** + * set option to delete empty lines. + * + * @param state true = delete, false = don't delete. + */ +void ASFormatter::setDeleteEmptyLinesMode(bool state) +{ + shouldDeleteEmptyLines = state; +} + +void ASFormatter::setBreakReturnType(bool state) +{ + shouldBreakReturnType = state; +} + +void ASFormatter::setBreakReturnTypeDecl(bool state) +{ + shouldBreakReturnTypeDecl = state; +} + +void ASFormatter::setAttachReturnType(bool state) +{ + shouldAttachReturnType = state; +} + +void ASFormatter::setAttachReturnTypeDecl(bool state) +{ + shouldAttachReturnTypeDecl = state; +} + +void ASFormatter::setSqueezeEmptyLinesNumber(int num) +{ + squeezeEmptyLineNum = num; +} + + +/** + * set the pointer alignment. + * + * @param alignment the pointer alignment. + */ +void ASFormatter::setPointerAlignment(PointerAlign alignment) +{ + pointerAlignment = alignment; +} + +void ASFormatter::setReferenceAlignment(ReferenceAlign alignment) +{ + referenceAlignment = alignment; +} + +/** + * jump over several characters. + * + * @param i the number of characters to jump over. + */ +void ASFormatter::goForward(int i) +{ + while (--i >= 0) + getNextChar(); +} + +/** + * peek at the next unread character. + * + * @return the next unread character. + */ +char ASFormatter::peekNextChar() const +{ + char ch = ' '; + size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); + + if (peekNum == std::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 == std::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 == std::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 != std::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 != std::string::npos) + { + size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2); + if (nextChar == std::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 != std::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 != std::string::npos) + { + size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2); + if (nextChar != std::string::npos + && currentLine.compare(nextChar, 2, "//") == 0) + foundMultipleLineEndComment = true; + } + } + } + return foundMultipleLineEndComment; +} + +/** + * get the next character, increasing the current placement in the process. + * the new character is inserted into the variable currentChar. + * + * @return whether succeeded to receive the new character. + */ +bool ASFormatter::getNextChar() +{ + isInLineBreak = false; + previousChar = currentChar; + + if (!std::isblank(currentChar)) + { + previousNonWSChar = currentChar; + if (!isInComment && !isInLineComment && !isInQuote + && !isImmediatelyPostComment + && !isImmediatelyPostLineComment + && !isInPreprocessor + && !isSequenceReached(ASResource::AS_OPEN_COMMENT) + && !(isGSCStyle() && isSequenceReached(ASResource::AS_GSC_OPEN_COMMENT)) + && !isSequenceReached(ASResource::AS_OPEN_LINE_COMMENT)) + previousCommandChar = currentChar; + } + + if (charNum + 1 < (int) currentLine.length() + && (!std::isblank(peekNextChar()) || isInComment || isInLineComment)) + { + currentChar = currentLine[++charNum]; + if (currentChar == '\t' && shouldConvertTabs) + convertTabToSpaces(); + + return true; + } + + // end of line has been reached + return getNextLine(); +} + +/** + * get the next line of input, increasing the current placement in the process. + * + * @param emptyLineWasDeleted an empty line was deleted. + * @return whether succeeded in reading the next line. + */ +bool ASFormatter::getNextLine(bool emptyLineWasDeleted /*false*/) +{ + if (!sourceIterator->hasMoreLines()) + { + endOfCodeReached = true; + return false; + } + if (appendOpeningBrace) + currentLine = "{"; // append brace that was removed from the previous line + else + { + currentLine = sourceIterator->nextLine(emptyLineWasDeleted); + assert(computeChecksumIn(currentLine)); + } + + // reset variables for new line + inLineNumber++; + if (endOfAsmReached) + endOfAsmReached = isInAsmBlock = isInAsm = false; + shouldKeepLineUnbroken = false; + isInCommentStartLine = false; + isInCase = false; + isInAsmOneLine = false; + isHeaderInMultiStatementLine = false; + isInQuoteContinuation = isInVerbatimQuote || haveLineContinuationChar; + haveLineContinuationChar = false; + isImmediatelyPostEmptyLine = lineIsEmpty; + previousChar = ' '; + + if (currentLine.empty()) + { + //#574 avoid deletion of empty lines after continuation + if (!isInComment && previousNonWSChar == '\\') + { + isInPreprocessor = true; + return false; + } + + isInContinuedPreProc = false; + currentLine = std::string(" "); // a null is inserted if this is not done + } + + if (methodBreakLineNum > 0) + --methodBreakLineNum; + if (methodAttachLineNum > 0) + --methodAttachLineNum; + + // unless reading in the first line of the file, break a new line. + if (!isVirgin) + isInLineBreak = true; + else + isVirgin = false; + + if (isImmediatelyPostNonInStmt) + { + isCharImmediatelyPostNonInStmt = true; + isImmediatelyPostNonInStmt = false; + } + + // check if is in preprocessor before line trimming + // a blank line after a \ will remove the flag + isImmediatelyPostPreprocessor = isInPreprocessor; + + if (!isInComment + && (previousNonWSChar != '\\' + || isEmptyLine(currentLine))) + { + isInPreprocessor = false; + isInPreprocessorDefineDef = false; + } + + if (passedSemicolon) + isInExecSQL = false; + initNewLine(); + + currentChar = currentLine[charNum]; + if (isInBraceRunIn && previousNonWSChar == '{' && !isInComment) + isInLineBreak = false; + isInBraceRunIn = false; + + if (currentChar == '\t' && shouldConvertTabs) + convertTabToSpaces(); + + // check for an empty line inside a command brace. + // if yes then read the next line (calls getNextLine recursively). + // must be after initNewLine. + if (shouldDeleteEmptyLines + && lineIsEmpty + && isBraceType((*braceTypeStack)[braceTypeStack->size() - 1], COMMAND_TYPE)) + { + if (!shouldBreakBlocks || previousNonWSChar == '{' || !commentAndHeaderFollows()) + { + isInPreprocessor = isImmediatelyPostPreprocessor; // restore + lineIsEmpty = false; + return getNextLine(true); + } + } + + if ( ++squeezeEmptyLineCount > squeezeEmptyLineNum && lineIsEmpty && isImmediatelyPostEmptyLine) + { + isInPreprocessor = isImmediatelyPostPreprocessor; // restore + return getNextLine(true); + } + + return true; +} + +/** + * jump over the leading white space in the current line, + * IF the line does not begin a comment or is in a preprocessor definition. + */ +void ASFormatter::initNewLine() +{ + size_t len = currentLine.length(); + size_t tabSize = getTabLength(); + charNum = 0; + + // don't trim these + if (isInQuoteContinuation + || (isInPreprocessor && !getPreprocDefineIndent())) + return; + + // SQL continuation lines must be adjusted so the leading spaces + // is equivalent to the opening EXEC SQL + if (isInExecSQL) + { + // replace leading tabs with spaces + // so that continuation indent will be spaces + size_t tabCount_ = 0; + size_t i; + for (i = 0; i < currentLine.length(); i++) + { + if (!std::isblank(currentLine[i])) // stop at first text + break; + if (currentLine[i] == '\t') + { + size_t numSpaces = tabSize - ((tabCount_ + i) % tabSize); + currentLine.replace(i, 1, numSpaces, ' '); + tabCount_++; + i += tabSize - 1; + } + } + // this will correct the format if EXEC SQL is not a hanging indent + trimContinuationLine(); + return; + } + + // comment continuation lines must be adjusted so the leading spaces + // is equivalent to the opening comment + if (isInComment) + { + if (noTrimCommentContinuation) + leadingSpaces = tabIncrementIn = 0; + trimContinuationLine(); + return; + } + + // compute leading spaces + isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly; + lineIsCommentOnly = false; + lineIsLineCommentOnly = false; + lineEndsInCommentOnly = false; + doesLineStartComment = false; + currentLineBeginsWithBrace = false; + lineIsEmpty = false; + currentLineFirstBraceNum = std::string::npos; + tabIncrementIn = 0; + + // bypass whitespace at the start of a line + // preprocessor tabs are replaced later in the program + for (charNum = 0; std::isblank(currentLine[charNum]) && charNum + 1 < (int) len; charNum++) + { + if (currentLine[charNum] == '\t' + && (!isInPreprocessor || isInPreprocessorDefineDef)) + tabIncrementIn += tabSize - 1 - ((tabIncrementIn + charNum) % tabSize); + } + leadingSpaces = charNum + tabIncrementIn; + + if (isSequenceReached(ASResource::AS_OPEN_COMMENT) || (isGSCStyle() && isSequenceReached(ASResource::AS_GSC_OPEN_COMMENT))) + { + doesLineStartComment = true; + if ((int) currentLine.length() > charNum + 2 + && currentLine.find("*/", charNum + 2) != std::string::npos) + lineIsCommentOnly = true; + } + else if (isSequenceReached(ASResource::AS_OPEN_LINE_COMMENT)) + { + lineIsLineCommentOnly = true; + } + else if (isSequenceReached("{")) + { + currentLineBeginsWithBrace = true; + currentLineFirstBraceNum = charNum; + size_t firstText = currentLine.find_first_not_of(" \t", charNum + 1); + if (firstText != std::string::npos) + { + if (currentLine.compare(firstText, 2, "//") == 0) + lineIsLineCommentOnly = true; + else if (currentLine.compare(firstText, 2, "/*") == 0 + || isExecSQL(currentLine, firstText)) + { + // get the extra adjustment + size_t j; + for (j = charNum + 1; j < firstText && std::isblank(currentLine[j]); j++) + { + if (currentLine[j] == '\t') + tabIncrementIn += tabSize - 1 - ((tabIncrementIn + j) % tabSize); + } + leadingSpaces = j + tabIncrementIn; + if (currentLine.compare(firstText, 2, "/*") == 0) + doesLineStartComment = true; + } + } + } + else if (std::isblank(currentLine[charNum]) && !(charNum + 1 < (int) currentLine.length())) + { + lineIsEmpty = true; + if (!isImmediatelyPostEmptyLine ) + { + squeezeEmptyLineCount = 0; + } + } + + // do not trim indented preprocessor define (except for comment continuation lines) + if (isInPreprocessor) + { + if (!doesLineStartComment) + leadingSpaces = 0; + charNum = 0; + } +} + +/** + * Append a character to the current formatted line. + * The formattedLine split points are updated. + * + * @param ch the character to append. + * @param canBreakLine if true, a registered line-break + */ +void ASFormatter::appendChar(char ch, bool canBreakLine) +{ + if (canBreakLine && isInLineBreak) + breakLine(); + + formattedLine.append(1, ch); + isImmediatelyPostCommentOnly = false; + if (maxCodeLength != std::string::npos) + { + // These compares reduce the frequency of function calls. + if (isOkToSplitFormattedLine()) + updateFormattedLineSplitPoints(ch); + if (formattedLine.length() > maxCodeLength) + testForTimeToSplitFormattedLine(); + } +} + +/** + * Append a std::string sequence to the current formatted line. + * The formattedLine split points are NOT updated. + * But the formattedLine is checked for time to split. + * + * @param sequence the sequence to append. + * @param canBreakLine if true, a registered line-break + */ +void ASFormatter::appendSequence(std::string_view sequence, bool canBreakLine) +{ + if (canBreakLine && isInLineBreak) + breakLine(); + formattedLine.append(sequence.data()); + if (formattedLine.length() > maxCodeLength) + testForTimeToSplitFormattedLine(); +} + +/** + * Append an operator sequence to the current formatted line. + * The formattedLine split points are updated. + * + * @param sequence the sequence to append. + * @param canBreakLine if true, a registered line-break + */ +void ASFormatter::appendOperator(std::string_view sequence, bool canBreakLine) +{ + if (canBreakLine && isInLineBreak) + breakLine(); + formattedLine.append(sequence.data()); + if (maxCodeLength != std::string::npos) + { + // These compares reduce the frequency of function calls. + if (isOkToSplitFormattedLine()) + updateFormattedLineSplitPointsOperator(sequence); + if (formattedLine.length() > maxCodeLength) + testForTimeToSplitFormattedLine(); + } +} + +/** + * append a space to the current formattedline, UNLESS the + * last character is already a white-space character. + */ +void ASFormatter::appendSpacePad() +{ + int len = formattedLine.length(); + if (len > 0 && !std::isblank(formattedLine[len - 1])) + { + formattedLine.append(1, ' '); + spacePadNum++; + if (maxCodeLength != std::string::npos) + { + // These compares reduce the frequency of function calls. + if (isOkToSplitFormattedLine()) + updateFormattedLineSplitPoints(' '); + if (formattedLine.length() > maxCodeLength) + testForTimeToSplitFormattedLine(); + } + } +} + +/** + * append a space to the current formattedline, UNLESS the + * next character is already a white-space character. + */ +void ASFormatter::appendSpaceAfter() +{ + int len = currentLine.length(); + if (charNum + 1 < len && !std::isblank(currentLine[charNum + 1])) + { + formattedLine.append(1, ' '); + spacePadNum++; + if (maxCodeLength != std::string::npos) + { + // These compares reduce the frequency of function calls. + if (isOkToSplitFormattedLine()) + updateFormattedLineSplitPoints(' '); + if (formattedLine.length() > maxCodeLength) + testForTimeToSplitFormattedLine(); + } + } +} + +/** + * register a line break for the formatted line. + */ +void ASFormatter::breakLine(bool isSplitLine /*false*/) +{ + isLineReady = true; + isInLineBreak = false; + spacePadNum = nextLineSpacePadNum; + nextLineSpacePadNum = 0; + readyFormattedLine = formattedLine; + formattedLine.erase(); + // queue an empty line prepend request if one exists + prependEmptyLine = isPrependPostBlockEmptyLineRequested; + + if (!isSplitLine) + { + formattedLineCommentNum = std::string::npos; + clearFormattedLineSplitPoints(); + + if (isAppendPostBlockEmptyLineRequested) + { + isAppendPostBlockEmptyLineRequested = false; + isPrependPostBlockEmptyLineRequested = true; + } + else + isPrependPostBlockEmptyLineRequested = false; + } +} + +/** + * check if the currently reached open-brace (i.e. '{') + * opens a: + * - a definition type block (such as a class or namespace), + * - a command block (such as a method block) + * - a static array + * this method takes for granted that the current character + * is an opening brace. + * + * @return the type of the opened block. + */ +BraceType ASFormatter::getBraceType() +{ + assert(currentChar == '{'); + + BraceType returnVal = NULL_TYPE; + + if ((previousNonWSChar == '=' + || isBraceType(braceTypeStack->back(), ARRAY_TYPE)) + && previousCommandChar != ')' + && !isNonParenHeader) + returnVal = ARRAY_TYPE; + else if (foundPreDefinitionHeader && previousCommandChar != ')') + { + returnVal = DEFINITION_TYPE; + if (foundNamespaceHeader) + returnVal = (BraceType)(returnVal | NAMESPACE_TYPE); + else if (foundClassHeader) + returnVal = (BraceType)(returnVal | CLASS_TYPE); + else if (foundStructHeader) + returnVal = (BraceType)(returnVal | STRUCT_TYPE); + else if (foundInterfaceHeader) + returnVal = (BraceType)(returnVal | INTERFACE_TYPE); + } + else if (isInEnum) + { + returnVal = (BraceType)(ARRAY_TYPE | ENUM_TYPE); + } + else if (isSharpStyle() && + !isOneLineBlockReached(currentLine, charNum) && + (currentHeader == &ASResource::AS_IF || currentHeader == &ASResource::AS_WHILE + || currentHeader == &ASResource::AS_USING || currentHeader == &ASResource::AS_WHILE + || currentHeader == &ASResource::AS_FOR || currentHeader == &ASResource::AS_FOREACH) ) // GH16 + { + returnVal = (BraceType) COMMAND_TYPE; + } + else + { + bool isCommandType = (foundPreCommandHeader + || foundPreCommandMacro + || (currentHeader != nullptr && isNonParenHeader) + || (previousCommandChar == ')' && !isInAllocator) + || (previousCommandChar == ':' && !foundQuestionMark) + || (previousCommandChar == ';') + || ((previousCommandChar == '{' || previousCommandChar == '}') + && isPreviousBraceBlockRelated) + || (isInClassInitializer + && ((!isLegalNameChar(previousNonWSChar) && previousNonWSChar != '(') + || foundPreCommandHeader)) + || foundTrailingReturnType + || isInObjCMethodDefinition + || isInObjCInterface + || isJavaStaticConstructor + || isSharpDelegate); + // C# methods containing 'get', 'set', 'add', and 'remove' do NOT end with parens + if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1)) + { + isCommandType = true; + isSharpAccessor = true; + } + + if (isInExternC) + returnVal = (isCommandType ? COMMAND_TYPE : EXTERN_TYPE); + else + returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE); + } + + int foundOneLineBlock = isOneLineBlockReached(currentLine, charNum); + + if (foundOneLineBlock == 2 && returnVal == COMMAND_TYPE) + returnVal = ARRAY_TYPE; + + if (foundOneLineBlock > 0) + { + returnVal = (BraceType) (returnVal | SINGLE_LINE_TYPE); + if (breakCurrentOneLineBlock) + returnVal = (BraceType) (returnVal | BREAK_BLOCK_TYPE); + if (foundOneLineBlock == 3) + returnVal = (BraceType)(returnVal | EMPTY_BLOCK_TYPE); + } + + if (isBraceType(returnVal, ARRAY_TYPE)) + { + if (isNonInStatementArrayBrace()) + { + returnVal = (BraceType)(returnVal | ARRAY_NIS_TYPE); + isNonInStatementArray = true; + isImmediatelyPostNonInStmt = false; // in case of "},{" + nonInStatementBrace = formattedLine.length() - 1; + } + if (isUniformInitializerBrace()) + returnVal = (BraceType)(returnVal | INIT_TYPE); + } + + + return returnVal; +} + +/** +* check if a colon is a class initializer separator +* +* @return whether it is a class initializer separator +*/ +bool ASFormatter::isClassInitializer() const +{ + assert(currentChar == ':'); + assert(previousChar != ':' && peekNextChar() != ':'); // not part of '::' + + // Early exit if conditions that prevent class initializer detection are met + if (foundQuestionMark || parenStack->back() > 0 || isInEnum) + { + return false; + } + + // Check if we are in a C-style class constructor initializer + bool isCStyleInitializer = isCStyle() && + !isInCase && + (previousCommandChar == ')' || foundPreCommandHeader); + + return isCStyleInitializer; +} + + +/** + * check if a line is empty + * + * @return whether line is empty + */ +bool ASFormatter::isEmptyLine(std::string_view line) const +{ + return line.find_first_not_of(" \t") == std::string::npos; +} + +/** + * Check if the following text is "C" as in extern "C". + * + * @return whether the statement is extern "C" + */ +bool ASFormatter::isExternC() const +{ + // charNum should be at 'extern' + assert(!std::isblank(currentLine[charNum])); + size_t startQuote = currentLine.find_first_of(" \t\"", charNum); + if (startQuote == std::string::npos) + return false; + startQuote = currentLine.find_first_not_of(" \t", startQuote); + if (startQuote == std::string::npos) + return false; + if (currentLine.compare(startQuote, 3, "\"C\"") != 0) + return false; + return true; +} + +/** + * Check if the currently reached '*', '&' or '^' character is + * a pointer-or-reference symbol, or another operator. + * A pointer dereference (*) or an "address of" character (&) + * counts as a pointer or reference because it is not an + * arithmetic operator. + * + * @return whether current character is a reference-or-pointer + */ +bool ASFormatter::isPointerOrReference() const +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + + if (isJavaStyle()) + return false; + + if (isCharImmediatelyPostOperator) + return false; + + // get the last legal word (may be a number) + std::string lastWord = getPreviousWord(currentLine, charNum); + if (lastWord.empty()) + lastWord = " "; + + // check for preceding or following numeric values + std::string nextText = peekNextText(currentLine.substr(charNum + 1)); + if (nextText.empty()) + nextText = " "; + if (isDigit(lastWord[0]) + || isDigit(nextText[0]) + || nextText[0] == '!' + || nextText[0] == '~') + return false; + + // check for multiply then a dereference (a * *b) + char nextChar = peekNextChar(); + if (currentChar == '*' + && nextChar == '*' + && !isPointerToPointer(currentLine, charNum)) + return false; + + if ((foundCastOperator && nextChar == '>') + || isPointerOrReferenceVariable(lastWord)) + { + return true; + } + + if (pointerAlignment == PTR_ALIGN_TYPE + && !shouldPadOperators //TODO 578 + && !isPointerOrReferenceVariable(lastWord)) + { + return false; + } + + if (isInClassInitializer + && previousNonWSChar != '(' + && previousNonWSChar != '{' + && previousCommandChar != ',' + && nextChar != ')' + && nextChar != '}') + return false; + + //check for rvalue reference + if (currentChar == '&' && nextChar == '&') + { + if (lastWord == ASResource::AS_AUTO) + return true; + if (previousNonWSChar == '>') + return true; + std::string followingText; + if ((int) currentLine.length() > charNum + 2) + followingText = peekNextText(currentLine.substr(charNum + 2)); + if (!followingText.empty() && followingText[0] == ')') + return true; + if (currentHeader != nullptr || isInPotentialCalculation) + return false; + if (parenStack->back() > 0 && isBraceType(braceTypeStack->back(), COMMAND_TYPE)) + return false; + return true; + } + + if (nextChar == '*' + || previousNonWSChar == '=' + || previousNonWSChar == '(' + || previousNonWSChar == '[' + || isCharImmediatelyPostReturn + || isInTemplate + || isCharImmediatelyPostTemplate + || currentHeader == &ASResource::AS_CATCH + || currentHeader == &ASResource::AS_FOREACH + || currentHeader == &ASResource::AS_QFOREACH) + { + return true; + } + + + if (isBraceType(braceTypeStack->back(), ARRAY_TYPE) + && isLegalNameChar(lastWord[0]) + && isLegalNameChar(nextChar) + && previousNonWSChar != ')') + { + if (isArrayOperator()) + return false; + } + + // checks on operators in parens + if (parenStack->back() > 0 + && isLegalNameChar(lastWord[0]) + && isLegalNameChar(nextChar)) + { + // if followed by an assignment it is a pointer or reference + // if followed by semicolon it is a pointer or reference in range-based for + const std::string* followingOperator = getFollowingOperator(); + if (followingOperator != nullptr + && followingOperator != &ASResource::AS_MULT + && followingOperator != &ASResource::AS_BIT_AND) + { + if (followingOperator == &ASResource::AS_ASSIGN || followingOperator == &ASResource::AS_COLON) + { + return true; + } + + return false; + } + + if (isBraceType(braceTypeStack->back(), COMMAND_TYPE) + || squareBracketCount > 0) + return false; + return true; + } + + // checks on operators in parens with following '(' + std::set disallowedChars = {',', '(', '!', '&', '*', '|'}; + + if (parenStack->back() > 0 + && nextChar == '(' + && disallowedChars.find(previousNonWSChar) == disallowedChars.end()) + return false; + + if (nextChar == '-' + || nextChar == '+') + { + size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextNum != std::string::npos) + { + if (currentLine.compare(nextNum, 2, "++") != 0 + && currentLine.compare(nextNum, 2, "--") != 0) + return false; + } + } + + bool isPR = (!isInPotentialCalculation + || (!isLegalNameChar(previousNonWSChar) + && !(previousNonWSChar == ')' && nextChar == '(') + && !(previousNonWSChar == ')' && currentChar == '*' && !isImmediatelyPostCast()) + && previousNonWSChar != ']') + || (!std::isblank(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; + + // https://sourceforge.net/p/astyle/bugs/537/ + // https://sourceforge.net/p/astyle/bugs/552/ + if ( previousNonWSChar == ',' && parenthesesCount <= 0 && currentChar != '&') + { + return false; + } + + if ( currentChar == '*' && pointerAlignment == PTR_ALIGN_NAME ) + { + size_t openParen = currentLine.rfind('(', charNum); + if (openParen != std::string::npos) + { + return true; + } + } + + std::set allowedChars = {'=', '.', '{', '>', '<', '?'}; + + if ( allowedChars.find(previousNonWSChar) != allowedChars.end() + || (previousNonWSChar == ',' && currentChar == '&') // #537, #552 + || isCharImmediatelyPostLineComment + || isCharImmediatelyPostComment + || isCharImmediatelyPostReturn) + return true; + + char nextChar = peekNextChar(); + if (currentChar == '*' && nextChar == '*') + { + if (previousNonWSChar == '(') + return true; + if ((int) currentLine.length() < charNum + 2) + return true; + return false; + } + + if (currentChar == '&' && nextChar == '&') + { + if (previousNonWSChar == '(' || isInTemplate) + return true; + if ((int) currentLine.length() < charNum + 2) + return true; + return false; + } + + if (previousNonWSChar == '(' && currentChar == '&' && pointerAlignment == PTR_ALIGN_TYPE) + { + return true; + } + + // check first char on the line + if (charNum == (int) currentLine.find_first_not_of(" \t") + && (isBraceType(braceTypeStack->back(), COMMAND_TYPE) + || parenStack->back() != 0)) + return true; + + std::string nextText = peekNextText(currentLine.substr(charNum + 1)); + if (!nextText.empty()) + { + if (nextText[0] == ')' || nextText[0] == '>' + || nextText[0] == ',' || nextText[0] == '=') + return false; + if (nextText[0] == ';') + return true; + } + // check for reference to a pointer *& + if ((currentChar == '*' && nextChar == '&') + || (previousNonWSChar == '*' && currentChar == '&')) + return false; + + if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE) + && parenStack->back() == 0) + return false; + std::string lastWord = getPreviousWord(currentLine, charNum); + if (lastWord == "else" || lastWord == "delete") + return true; + + bool isDA = (!(isLegalNameChar(previousNonWSChar) || previousNonWSChar == '>') // TODO GH14 + || (!nextText.empty() && !isLegalNameChar(nextText[0]) && nextText[0] != '/') + || (ispunct((unsigned char)previousNonWSChar) && previousNonWSChar != '.') // TODO GH14 + || isCharImmediatelyPostReturn + || !isPointerOrReferenceVariable(lastWord)); + + return isDA; +} + +/** + * Check if the currently reached '*' or '&' character is + * centered with one space on each side. + * Only spaces are checked, not tabs. + * If true then a space will be deleted on the output. + * + * @return whether current character is centered. + */ +bool ASFormatter::isPointerOrReferenceCentered() const +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + + int prNum = charNum; + int lineLength = (int) currentLine.length(); + + // check for end of line + if (peekNextChar() == ' ') + return false; + + // check space before + if (prNum < 1 + || currentLine[prNum - 1] != ' ') + return false; + + // check no space before that + if (prNum < 2 + || currentLine[prNum - 2] == ' ') + return false; + + // check for ** or && + if (prNum + 1 < lineLength + && (currentLine[prNum + 1] == '*' || currentLine[prNum + 1] == '&')) + prNum++; + + // check space after + if (prNum + 1 <= lineLength + && currentLine[prNum + 1] != ' ') + return false; + + // check no space after that + if (prNum + 2 < lineLength + && currentLine[prNum + 2] == ' ') + return false; + + return true; +} + +/** + * Check if a word is a pointer or reference variable type. + * + * @return whether word is a pointer or reference variable. + */ +bool ASFormatter::isPointerOrReferenceVariable(std::string_view word) const +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + + // to avoid problem with multiplications - we need LSP + + bool retval = false; + if (word == "char" + || word == "std::string" + || word == "String" + || word == "NSString" + || word == "int" + || word == "void" + || word == "short" + || word == "long" + || word == "double" + || word == "float" + || word == "sizeof" + || (word.length() >= 6 // check end of word for _t + && word.compare(word.length() - 2, 2, "_t") == 0) + ) + retval = true; + + // check for C# object type "x is std::string" + if (retval && isSharpStyle()) + { + // find the word previous to the 'word' parameter + std::string prevWord; + size_t wordStart = currentLine.rfind(word.data(), charNum); + if (wordStart != std::string::npos) + prevWord = getPreviousWord(currentLine, wordStart); + if (prevWord == "is") + retval = false; + } + + return retval; +} + +/** + * Check if * * is a pointer to a pointer or a multiply then a dereference. + * + * @return true if a pointer *. + */ +bool ASFormatter::isPointerToPointer(std::string_view line, int currPos) const +{ + assert(line[currPos] == '*' && peekNextChar() == '*'); + if ((int) line.length() > currPos + 1 && line[currPos + 1] == '*') + return true; + size_t nextText = line.find_first_not_of(" \t", currPos + 1); + if (nextText == std::string::npos || line[nextText] != '*') + return false; + size_t nextText2 = line.find_first_not_of(" \t", nextText + 1); + if (nextText == std::string::npos) + return false; + if (line[nextText2] == ')' || line[nextText2] == '*') + return true; + return false; +} + +/** + * 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 == '-'); + + // does a digit follow a c-style cast + if (previousCommandChar == ')') + { + if (!isdigit(peekNextChar())) + return false; + size_t end = currentLine.rfind(')', charNum); + if (end == std::string::npos) + return false; + size_t lastChar = currentLine.find_last_not_of(" \t", end - 1); + if (lastChar == std::string::npos) + return false; + if (currentLine[lastChar] == '*') + end = lastChar; + std::string prevWord = getPreviousWord(currentLine, end); + if (prevWord.empty()) + return false; + + // a cast can be a user defined type + //if (!isNumericVariable(prevWord)) + // return false; + + return true; + } + + return ((isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar)) + && previousCommandChar != '.' + && previousCommandChar != '\"' + && previousCommandChar != '\'' + && previousCommandChar != ']'); +} + +/** + * check if the currently reached comment is in a 'switch' statement + * + * @return whether the current '+' or '-' is in an exponent. + */ +bool ASFormatter::isInSwitchStatement() const +{ + assert(isInLineComment || isInComment); + if (!preBraceHeaderStack->empty()) + for (size_t i = 1; i < preBraceHeaderStack->size(); i++) + if (preBraceHeaderStack->at(i) == &ASResource::AS_SWITCH) + return true; + return false; +} + +/** + * check if the currently reached '+' or '-' character is + * part of an exponent, i.e. 0.2E-5. + * + * @return whether the current '+' or '-' is in an exponent. + */ +bool ASFormatter::isInExponent() const +{ + assert(currentChar == '+' || currentChar == '-'); + std::string prevWord = getPreviousWord(currentLine, charNum, true); + + if (charNum && isDigit(prevWord[0])) + { + return prevWord.find_first_not_of("0123456789.") != std::string::npos; + } + + if (charNum > 2 && prevWord.size() >= 2 && prevWord[0] == '0' && (prevWord[1] == 'x' || prevWord[1] == 'X')) + { + char prevPrevFormattedChar = currentLine[charNum - 2]; + char prevFormattedChar = currentLine[charNum - 1]; + // double x = 0x1.23ffp-11; + return ((prevFormattedChar == 'e' || prevFormattedChar == 'E' || prevFormattedChar == 'p' || prevFormattedChar == 'P') + && (prevPrevFormattedChar == '.' || std::isxdigit(prevPrevFormattedChar))); + } + return false; +} + +/** + * check if an array brace should NOT have an in-statement indent + * + * @return the array is non in-statement + */ +bool ASFormatter::isNonInStatementArrayBrace() const +{ + bool returnVal = false; + char nextChar = peekNextChar(); + // if this opening brace begins the line there will be no inStatement indent + if (currentLineBeginsWithBrace + && (size_t) charNum == currentLineFirstBraceNum + && nextChar != '}') + returnVal = true; + // if an opening brace ends the line there will be no inStatement indent + if (std::isblank(nextChar) + || isBeforeAnyLineEndComment(charNum) + || nextChar == '{') + returnVal = true; + + // Java "new Type [] {...}" IS an inStatement indent + if (isJavaStyle() && previousNonWSChar == ']') + returnVal = false; + + return returnVal; +} + +/** + * check if a one-line block has been reached, + * i.e. if the currently reached '{' character is closed + * with a complimentary '}' elsewhere on the current line, + *. + * @return 0 = one-line block has not been reached. + * 1 = one-line block has been reached. + * 2 = one-line block has been reached and is followed by a comma. + * 3 = one-line block has been reached and is an empty block. + */ +int ASFormatter::isOneLineBlockReached(std::string_view line, int startChar) const +{ + assert(line[startChar] == '{'); + + bool isInComment_ = false; + bool isInQuote_ = false; + bool hasText = false; + int braceCount = 0; + int lineLength = line.length(); + char quoteChar_ = ' '; + char ch = ' '; + char prevCh = ' '; + + for (int i = startChar; i < lineLength; ++i) + { + ch = line[i]; + + if (isInComment_) + { + if (line.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + + if (isInQuote_) + { + if (ch == '\\') + ++i; + else if (ch == quoteChar_) + isInQuote_ = false; + continue; + } + + if (ch == '"' + || (ch == '\'' && !isDigitSeparator(line, i))) + { + isInQuote_ = true; + quoteChar_ = ch; + continue; + } + + if (line.compare(i, 2, "//") == 0) + break; + + if (line.compare(i, 2, "/*") == 0) + { + isInComment_ = true; + ++i; + continue; + } + + if (ch == '{') + { + ++braceCount; + continue; + } + if (ch == '}') + { + --braceCount; + if (braceCount == 0) + { + // is this an array? + if (parenStack->back() == 0 && prevCh != '}') + { + size_t peekNum = line.find_first_not_of(" \t", i + 1); + if (peekNum != std::string::npos && line[peekNum] == ',') + return 2; + } + if (!hasText) + return 3; // is an empty block + return 1; + } + } + if (ch == ';') + continue; + if (!std::isblank(ch)) + { + hasText = true; + prevCh = ch; + } + } + + return 0; +} + +/** + * peek at the next word to determine if it is a C# non-paren header. + * will look ahead in the input file if necessary. + * + * @param startChar position on currentLine to start the search + * @return true if the next word is get or set. + */ +bool ASFormatter::isNextWordSharpNonParenHeader(int startChar) const +{ + // look ahead to find the next non-comment text + std::string nextText = peekNextText(currentLine.substr(startChar)); + if (nextText.empty()) + return false; + if (nextText[0] == '[') + return true; + if (!isCharPotentialHeader(nextText, 0)) + return false; + if (findKeyword(nextText, 0, ASResource::AS_GET) || findKeyword(nextText, 0, ASResource::AS_SET) + || findKeyword(nextText, 0, ASResource::AS_ADD) || findKeyword(nextText, 0, ASResource::AS_REMOVE)) + return true; + return false; +} + +/** + * peek at the next char to determine if it is an opening brace. + * will look ahead in the input file if necessary. + * this determines a java static constructor. + * + * @param startChar position on currentLine to start the search + * @return true if the next word is an opening brace. + */ +bool ASFormatter::isNextCharOpeningBrace(int startChar) const +{ + bool retVal = false; + std::string nextText = peekNextText(currentLine.substr(startChar)); + if (!nextText.empty() + && nextText.compare(0, 1, "{") == 0) + retVal = true; + return retVal; +} + +/** +* Check if operator and, pointer, and reference padding is disabled. +* Disabling is done thru a NOPAD tag in an ending comment. +* +* @return true if the formatting on this line is disabled. +*/ +bool ASFormatter::isOperatorPaddingDisabled() const +{ + size_t commentStart = currentLine.find("//", charNum); + if (commentStart == std::string::npos) + { + commentStart = currentLine.find("/*", charNum); + // comment must end on this line + if (commentStart != std::string::npos) + { + size_t commentEnd = currentLine.find("*/", commentStart + 2); + if (commentEnd == std::string::npos) + commentStart = std::string::npos; + } + } + if (commentStart == std::string::npos) + return false; + size_t noPadStart = currentLine.find("*NOPAD*", commentStart); + if (noPadStart == std::string::npos) + return false; + return true; +} + +/** +* Determine if an opening array-type brace should have a leading space pad. +* This is to identify C++11 uniform initializers. +*/ +bool ASFormatter::isUniformInitializerBrace() const +{ + if (isCStyle() && !isInEnum && !isImmediatelyPostPreprocessor) + { + if (isInClassInitializer + || isLegalNameChar(previousNonWSChar) + || previousNonWSChar == '(') + return true; + } + return false; +} + +/** +* Determine if there is a following statement on the current line. +*/ +bool ASFormatter::isMultiStatementLine() const +{ + assert((isImmediatelyPostHeader || foundClosingHeader)); + bool isInComment_ = false; + bool isInQuote_ = false; + int semiCount_ = 0; + int parenCount_ = 0; + int braceCount_ = 0; + + for (size_t i = 0; i < currentLine.length(); i++) + { + if (isInComment_) + { + if (currentLine.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + continue; + } + } + if (currentLine.compare(i, 2, "/*") == 0) + { + isInComment_ = true; + continue; + } + if (currentLine.compare(i, 2, "//") == 0) + return false; + if (isInQuote_) + { + if (currentLine[i] == '"' || currentLine[i] == '\'') + isInQuote_ = false; + continue; + } + if (currentLine[i] == '"' || currentLine[i] == '\'') + { + isInQuote_ = true; + continue; + } + if (currentLine[i] == '(') + { + ++parenCount_; + continue; + } + if (currentLine[i] == ')') + { + --parenCount_; + continue; + } + if (parenCount_ > 0) + continue; + if (currentLine[i] == '{') + { + ++braceCount_; + } + if (currentLine[i] == '}') + { + --braceCount_; + } + if (braceCount_ > 0) + continue; + if (currentLine[i] == ';') + { + ++semiCount_; + if (semiCount_ > 1) + return true; + continue; + } + } + return false; +} + +/** + * get the next non-whitespace substd::string on following lines, bypassing all comments. + * + * @param firstLine the first line to check + * @return the next non-whitespace substd::string. + */ +std::string ASFormatter::peekNextText(std::string_view firstLine, + bool endOnEmptyLine /*false*/, + const std::shared_ptr& streamArg /*nullptr*/) const +{ + assert(sourceIterator->getPeekStart() == 0 || streamArg != nullptr); // Borland may need != 0 + bool isFirstLine = true; + std::string nextLine_(firstLine); + size_t firstChar = std::string::npos; + std::shared_ptr stream = streamArg; + if (stream == nullptr) // Borland may need == 0 + stream = std::make_shared(sourceIterator); + + // find the first non-blank text, bypassing all comments. + bool isInComment_ = false; + while (stream->hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + nextLine_ = stream->peekNextLine(); + + firstChar = nextLine_.find_first_not_of(" \t"); + if (firstChar == std::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 == std::string::npos) + continue; + firstChar += 2; + isInComment_ = false; + firstChar = nextLine_.find_first_not_of(" \t", firstChar); + if (firstChar == std::string::npos) + continue; + } + + if (nextLine_.compare(firstChar, 2, "//") == 0) + continue; + + // found the next text + break; + } + + if (firstChar == std::string::npos) + nextLine_ = ""; + else + nextLine_ = nextLine_.substr(firstChar); + return nextLine_; +} + +/** + * adjust comment position because of adding or deleting spaces + * the spaces are added or deleted to formattedLine + * spacePadNum contains the adjustment + */ +void ASFormatter::adjustComments() +{ + assert(spacePadNum != 0); + assert(isSequenceReached(ASResource::AS_OPEN_LINE_COMMENT) || isSequenceReached(ASResource::AS_OPEN_COMMENT) || isSequenceReached(ASResource::AS_GSC_OPEN_COMMENT)); + + // block comment must be closed on this line with nothing after it + bool isCppComment = isSequenceReached(ASResource::AS_OPEN_COMMENT); + bool isGSCComment = isSequenceReached(ASResource::AS_GSC_OPEN_COMMENT); + + if (isCppComment || isGSCComment) + { + size_t endNum = currentLine.find(isCppComment ? ASResource::AS_CLOSE_COMMENT : ASResource::AS_GSC_CLOSE_COMMENT, charNum + 2); + if (endNum == std::string::npos) + return; + // following line comments may be a tag from AStyleWx //[[)> + size_t nextNum = currentLine.find_first_not_of(" \t", endNum + 2); + if (nextNum != std::string::npos + && currentLine.compare(nextNum, 2, ASResource::AS_OPEN_LINE_COMMENT) != 0) + 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 != std::string::npos + && lastText < len - adjust - 1) + formattedLine.resize(len - adjust); + else if (len > lastText + 2) + formattedLine.resize(lastText + 2); + else if (len < lastText + 2) + formattedLine.append(len - lastText, ' '); + } +} + +/** + * append the current brace inside the end of line comments + * currentChar contains the brace, it will be appended to formattedLine + * formattedLineCommentNum is the comment location on formattedLine + */ +void ASFormatter::appendCharInsideComments() +{ + if (formattedLineCommentNum == std::string::npos // does the comment start on the previous line? + || formattedLineCommentNum == 0) + { + appendCurrentChar(); // don't attach + return; + } + assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0 + || formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0); + + // find the previous non space char + size_t end = formattedLineCommentNum; + size_t beg = formattedLine.find_last_not_of(" \t", end - 1); + if (beg == std::string::npos) + { + appendCurrentChar(); // don't attach + return; + } + beg++; + + // insert the brace + if (end - beg < 3) // is there room to insert? + formattedLine.insert(beg, 3 - end + beg, ' '); + if (formattedLine[beg] == '\t') // don't pad with a tab + formattedLine.insert(beg, 1, ' '); + formattedLine[beg + 1] = currentChar; + testForTimeToSplitFormattedLine(); + + if (isBeforeComment()) + breakLine(); + else if (isCharImmediatelyPostLineComment) + shouldBreakLineAtNextChar = true; +} + +/** + * add or remove space padding to operators + * the operators and necessary padding will be appended to formattedLine + * the calling function should have a continue statement after calling this method + * + * @param newOperator the operator to be padded + */ +void ASFormatter::padOperators(const std::string* newOperator) +{ + assert(shouldPadOperators || negationPadMode != NEGATION_PAD_NO_CHANGE); + assert(newOperator != nullptr); + + char nextNonWSChar = ASBase::peekNextChar(currentLine, charNum); + std::set allowedChars = {'(', '[', '=', ',', ':', '{'}; + + bool isUnaryOrModOperator = (newOperator == &ASResource::AS_PLUS || + newOperator == &ASResource::AS_MINUS || + (newOperator == &ASResource::AS_MOD && isGSCStyle())); + + bool isExponentOperator = (newOperator == &ASResource::AS_MINUS && isInExponent()) || + (newOperator == &ASResource::AS_PLUS && isInExponent()); + + bool isSpecialColon = (newOperator == &ASResource::AS_COLON && !foundQuestionMark && + (isInObjCMethodDefinition || isInObjCInterface || isInObjCSelector || squareBracketCount != 0)); + + bool isJavaWildcard = (newOperator == &ASResource::AS_QUESTION && isJavaStyle() && + (previousNonWSChar == '<' || nextNonWSChar == '>' || nextNonWSChar == '.')); + + bool isSharpNullConditional = (newOperator == &ASResource::AS_QUESTION && isSharpStyle() && + (nextNonWSChar == '.' || nextNonWSChar == '[')); + + bool isSpecialTemplateOperator = (isInTemplate || isImmediatelyPostTemplate) && + (newOperator == &ASResource::AS_LS || newOperator == &ASResource::AS_GR); + + std::string sBegin = currentLine.substr(0, charNum); + std::string sEnd = currentLine.substr(charNum, currentLine.find_first_not_of(">", charNum + 1)); + + auto numOfOpeningBrackets = std::count(sBegin.begin(), sBegin.end(), '<'); + auto numOfClosingBrackets = std::count(sEnd.begin(), sEnd.end(), '>'); + + bool isClosingTemplateDefinition = numOfClosingBrackets >= numOfOpeningBrackets && numOfOpeningBrackets >= 2; + + bool shouldPad = (newOperator != &ASResource::AS_SCOPE_RESOLUTION && + newOperator != &ASResource::AS_PLUS_PLUS && + newOperator != &ASResource::AS_MINUS_MINUS && + (newOperator != &ASResource::AS_NOT || negationPadMode != NEGATION_PAD_NO_CHANGE) && + newOperator != &ASResource::AS_BIT_NOT && + newOperator != &ASResource::AS_ARROW && + !isSpecialColon && + !isExponentOperator && + !isClosingTemplateDefinition && + !(newOperator == &ASResource::AS_GR && previousChar == '-') && + !(isUnaryOrModOperator && (allowedChars.find(previousNonWSChar) != allowedChars.end())) && + !(newOperator == &ASResource::AS_MULT && + (previousNonWSChar == '.' || previousNonWSChar == '>')) && + !(newOperator == &ASResource::AS_MULT && peekNextChar() == '>') && + !isSpecialTemplateOperator && + !(newOperator == &ASResource::AS_GCC_MIN_ASSIGN && + ASBase::peekNextChar(currentLine, charNum + 1) == '>') && + !(newOperator == &ASResource::AS_GR && previousNonWSChar == '?') && + !isJavaWildcard && + !isSharpNullConditional && + !isCharImmediatelyPostOperator && + !isInCase && + !isInAsm && + !isInAsmOneLine && + !isInAsmBlock); + + // pad before operator + if (shouldPad + && (newOperator != &ASResource::AS_NOT || (newOperator == &ASResource::AS_NOT && negationPadMode == NEGATION_PAD_BEFORE ) ) + && !(newOperator == &ASResource::AS_COLON + && (!foundQuestionMark && !isInEnum) && currentHeader != &ASResource::AS_FOR) + && !(newOperator == &ASResource::AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?) + && currentLine.find(':', charNum + 1) == std::string::npos) + ) + { + appendSpacePad(); + } + + appendOperator(*newOperator); + goForward(newOperator->length() - 1); + + currentChar = (*newOperator)[newOperator->length() - 1]; + // pad after operator + // but do not pad after a '-' that is a unary-minus. + if (shouldPad + && !isBeforeAnyComment() + && !(newOperator == &ASResource::AS_PLUS && isUnaryOperator()) + && !(newOperator == &ASResource::AS_MINUS && isUnaryOperator()) + && !(currentLine.compare(charNum + 1, 1, ASResource::AS_SEMICOLON) == 0) + && !(currentLine.compare(charNum + 1, 2, ASResource::AS_SCOPE_RESOLUTION) == 0) + && !(peekNextChar() == ',') + && !(newOperator == &ASResource::AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?) + && peekNextChar() == '[') + ) + { + appendSpaceAfter(); + } +} + +/** + * format pointer or reference + * currentChar contains the pointer or reference + * the symbol and necessary padding will be appended to formattedLine + * the calling function should have a continue statement after calling this method + * + * NOTE: Do NOT use appendCurrentChar() in this method. The line should not be + * broken once the calculation starts. + */ +void ASFormatter::formatPointerOrReference() +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + assert(!isJavaStyle()); + + int pa = pointerAlignment; + int ra = referenceAlignment; + int itemAlignment = (currentChar == '*' || currentChar == '^') + ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra); + + // handle operator char*() {}; + if (currentChar == '*' && isCStyle() && currentLine.find("operator") != std::string::npos) + { + formattedLine.append("*"); + return; + } + + // check for ** and && + int ptrLength = 1; + char peekedChar = peekNextChar(); + if ((currentChar == '*' && peekedChar == '*') + || (currentChar == '&' && peekedChar == '&')) + { + ptrLength = 2; + + //TODO check + size_t nextChar = currentLine.find_first_not_of(" \t", charNum + 2); + if (nextChar == std::string::npos) + peekedChar = ' '; + else + peekedChar = currentLine[nextChar]; + + //https://sourceforge.net/p/astyle/bugs/543/ + if (currentChar == '&' /*&& itemAlignment == PTR_ALIGN_NAME*/) + { + itemAlignment = PTR_ALIGN_NONE; + } + } + // check for cast + if (peekedChar == ')' || peekedChar == '>' || peekedChar == ',') + { + formatPointerOrReferenceCast(); + return; + } + + // check for a padded space and remove it + if (charNum > 0 + && !std::isblank(currentLine[charNum - 1]) + && !formattedLine.empty() + && std::isblank(formattedLine[formattedLine.length() - 1])) + { + formattedLine.erase(formattedLine.length() - 1); + spacePadNum--; + } + + if (itemAlignment == PTR_ALIGN_TYPE) + { + formatPointerOrReferenceToType(); + } + else if (itemAlignment == PTR_ALIGN_MIDDLE) + { + formatPointerOrReferenceToMiddle(); + } + else if (itemAlignment == PTR_ALIGN_NAME) + { + formatPointerOrReferenceToName(); + } + else // pointerAlignment == PTR_ALIGN_NONE + { + formattedLine.append(currentLine.substr(charNum, ptrLength)); + if (ptrLength > 1) + goForward(ptrLength - 1); + } +} + +/** + * format pointer or reference with align to type + */ +void ASFormatter::formatPointerOrReferenceToType() +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + assert(!isJavaStyle()); + + // do this before bumping charNum + bool isOldPRCentered = isPointerOrReferenceCentered(); + std::string sequenceToInsert(1, currentChar); + // get the sequence + if (currentChar == peekNextChar()) + { + for (size_t i = charNum + 1; currentLine.length() > i; i++) + { + if (currentLine[i] == sequenceToInsert[0]) + { + sequenceToInsert.append(1, currentLine[i]); + goForward(1); + continue; + } + break; + } + } + // append the sequence + std::string charSave; + size_t prevCh = formattedLine.find_last_not_of(" \t"); + if (prevCh < formattedLine.length()) + { + charSave = formattedLine.substr(prevCh + 1); + formattedLine.resize(prevCh + 1); + } + + // https://sourceforge.net/p/astyle/bugs/537/ + // TODO check + if ((previousNonWSChar == ',' || previousNonWSChar == '[') && currentChar != ' ') + appendSpacePad(); + + formattedLine.append(sequenceToInsert); + if (peekNextChar() != ')') + formattedLine.append(charSave); + else + spacePadNum -= charSave.length(); + // if no space after then add one + if (charNum < (int) currentLine.length() - 1 + && !std::isblank(currentLine[charNum + 1]) + && currentLine[charNum + 1] != ')' + && peekNextChar() != '&') + appendSpacePad(); + + // if old pointer or reference is centered, remove a space + if (isOldPRCentered + && std::isblank(formattedLine[formattedLine.length() - 1])) + { + formattedLine.erase(formattedLine.length() - 1, 1); + spacePadNum--; + } + // update the formattedLine split point + if (maxCodeLength != std::string::npos && !formattedLine.empty()) + { + size_t index = formattedLine.length() - 1; + if (std::isblank(formattedLine[index])) + { + updateFormattedLineSplitPointsPointerOrReference(index); + testForTimeToSplitFormattedLine(); + } + } +} + +/** + * format pointer or reference with align in the middle + */ +void ASFormatter::formatPointerOrReferenceToMiddle() +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + assert(!isJavaStyle()); + + if (currentLine.size() > (size_t)charNum + 1 && + std::isblank(currentLine[charNum - 1]) && + std::isblank(currentLine[charNum + 1])) + { + + std::string seq = {currentChar, currentLine[charNum + 1]}; + appendSequence(seq); + goForward(1); + return; + } + + // compute current whitespace before + size_t wsBefore = currentLine.find_last_not_of(" \t", charNum - 1); + if (wsBefore == std::string::npos) + wsBefore = 0; + else + wsBefore = charNum - wsBefore - 1; + std::string sequenceToInsert(1, currentChar); + + if (currentChar == peekNextChar()) + { + for (size_t i = charNum + 1; currentLine.length() > i; i++) + { + if (currentLine[i] == sequenceToInsert[0]) + { + sequenceToInsert.append(1, currentLine[i]); + goForward(1); + continue; + } + break; + } + } + // if reference to a pointer check for conflicting alignment + else if (currentChar == '*' && peekNextChar() == '&' + && ASBeautifier::peekNextChar(currentLine, charNum + 1) != '&' + && (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 && std::isblank(currentLine[i]); i++) + goForward(1); + } + // if a comment follows don't align, just space pad + if (isBeforeAnyComment()) + { + appendSpacePad(); + formattedLine.append(sequenceToInsert); + appendSpaceAfter(); + return; + } + // do this before goForward() + bool isAfterScopeResolution = previousNonWSChar == ':'; + size_t charNumSave = charNum; + // if this is the last thing on the line + if (currentLine.find_first_not_of(" \t", charNum + 1) == std::string::npos) + { + if (wsBefore == 0 && !isAfterScopeResolution) + formattedLine.append(1, ' '); + formattedLine.append(sequenceToInsert); + return; + } + // goForward() to convert tabs to spaces, if necessary, + // and move following characters to preceding characters + // this may not work every time with tab characters + for (size_t i = charNum + 1; i < currentLine.length() && std::isblank(currentLine[i]); i++) + { + goForward(1); + if (!formattedLine.empty()) + formattedLine.append(1, currentLine[i]); + else + spacePadNum--; + } + // find space padding after + size_t wsAfter = currentLine.find_first_not_of(" \t", charNumSave + 1); + if (wsAfter == std::string::npos || isBeforeAnyComment()) + wsAfter = 0; + else + wsAfter = wsAfter - charNumSave - 1; + // don't pad before scope resolution operator, but pad after + if (isAfterScopeResolution) + { + size_t lastText = formattedLine.find_last_not_of(" \t"); + formattedLine.insert(lastText + 1, sequenceToInsert); + appendSpacePad(); + } + else if (!formattedLine.empty()) + { + // whitespace should be at least 2 chars to center + if (wsBefore + wsAfter < 2) + { + size_t charsToAppend = (2 - (wsBefore + wsAfter)); + formattedLine.append(charsToAppend, ' '); + spacePadNum += charsToAppend; + if (wsBefore == 0) + wsBefore++; + if (wsAfter == 0) + wsAfter++; + } + // insert the pointer or reference char + size_t padAfter = (wsBefore + wsAfter) / 2; + size_t index = formattedLine.length() - padAfter; + if (index < formattedLine.length()) + formattedLine.insert(index, sequenceToInsert); + else + formattedLine.append(sequenceToInsert); + } + else // formattedLine.length() == 0 + { + formattedLine.append(sequenceToInsert); + if (wsAfter == 0) + wsAfter++; + formattedLine.append(wsAfter, ' '); + spacePadNum += wsAfter; + } + // update the formattedLine split point after the pointer + if (maxCodeLength != std::string::npos && !formattedLine.empty()) + { + size_t index = formattedLine.find_last_not_of(" \t"); + if (index != std::string::npos && (index < formattedLine.length() - 1)) + { + index++; + updateFormattedLineSplitPointsPointerOrReference(index); + testForTimeToSplitFormattedLine(); + } + } +} + +/** + * format pointer or reference with align to name + */ +void ASFormatter::formatPointerOrReferenceToName() +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + assert(!isJavaStyle()); + + // do this before bumping charNum + bool isOldPRCentered = isPointerOrReferenceCentered(); + size_t startNum = formattedLine.find_last_not_of(" \t"); + if (startNum == std::string::npos) + startNum = 0; + std::string sequenceToInsert(1, currentChar); + if (currentChar == peekNextChar()) + { + for (size_t i = charNum + 1; currentLine.length() > i; i++) + { + if (currentLine[i] == sequenceToInsert[0]) + { + sequenceToInsert.append(1, currentLine[i]); + goForward(1); + continue; + } + break; + } + } + + // if reference to a pointer align both to name + + else if (currentChar == '*' && peekNextChar() == '&' && ASBeautifier::peekNextChar(currentLine, charNum + 1) != '&') + { + sequenceToInsert = "*&"; + goForward(1); + for (size_t i = charNum; i < currentLine.length() - 1 && std::isblank(currentLine[i]); i++) + goForward(1); + } + + + char peekedChar = peekNextChar(); + bool isAfterScopeResolution = previousNonWSChar == ':'; // check for :: + // if this is not the last thing on the line + if ((isLegalNameChar(peekedChar) || peekedChar == '(' || peekedChar == '[' || peekedChar == '=') + && (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() && std::isblank(currentLine[i]); i++) + { + // if a padded paren follows don't move + if (shouldPadParensOutside && peekedChar == '(' && !isOldPRCentered) + { + // empty parens don't count + size_t start = currentLine.find_first_not_of("( \t", i); + if (start != std::string::npos && currentLine[start] != ')') + break; + } + goForward(1); + if (!formattedLine.empty()) + formattedLine.append(1, currentLine[charNum]); + else + spacePadNum--; + } + } + // don't pad before scope resolution operator + if (isAfterScopeResolution) + { + size_t lastText = formattedLine.find_last_not_of(" \t"); + if (lastText != std::string::npos && lastText + 1 < formattedLine.length()) + formattedLine.erase(lastText + 1); + } + // if no space before * then add one + else if (!formattedLine.empty() + && (currentLine[startNum + 1] != '&') + && (formattedLine.length() <= startNum + 1 + || !std::isblank(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 + && std::isblank(formattedLine[startNum + 1]) + && peekedChar != '*' // check for '* *' + && !isBeforeAnyComment() + && ((isLegalNameChar(peekedChar) || peekedChar == '(') && pointerAlignment == PTR_ALIGN_NAME) //https://sourceforge.net/p/astyle/bugs/546/ + #527 + ) + { + formattedLine.erase(startNum + 1, 1); + spacePadNum--; + } + // don't convert to *= or &= + if (peekedChar == '=') + { + appendSpaceAfter(); + // if more than one space before, delete one + if (formattedLine.length() > startNum + && std::isblank(formattedLine[startNum + 1]) + && std::isblank(formattedLine[startNum + 2])) + { + formattedLine.erase(startNum + 1, 1); + spacePadNum--; + } + } + // update the formattedLine split point + if (maxCodeLength != std::string::npos) + { + size_t index = formattedLine.find_last_of(" \t"); + if (index != std::string::npos + && index < formattedLine.length() - 1 + && (formattedLine[index + 1] == '*' + || formattedLine[index + 1] == '&' + || formattedLine[index + 1] == '^')) + { + updateFormattedLineSplitPointsPointerOrReference(index); + testForTimeToSplitFormattedLine(); + } + } +} + +/** + * format pointer or reference cast + * currentChar contains the pointer or reference + * NOTE: the pointers and references in function definitions + * are processed as a cast (e.g. void foo(void*, void*)) + * is processed here. + */ +void ASFormatter::formatPointerOrReferenceCast() +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + assert(!isJavaStyle()); + + int pa = pointerAlignment; + int ra = referenceAlignment; + int itemAlignment = (currentChar == '*' || currentChar == '^') + ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra); + + std::string sequenceToInsert(1, currentChar); + if (isSequenceReached("**") || isSequenceReached(ASResource::AS_AND)) + { + goForward(1); + sequenceToInsert.append(1, currentLine[charNum]); + } + if (itemAlignment == PTR_ALIGN_NONE) + { + appendSequence(sequenceToInsert, false); + return; + } + // remove preceding whitespace + char prevCh = ' '; + size_t prevNum = formattedLine.find_last_not_of(" \t"); + if (prevNum != std::string::npos) + { + prevCh = formattedLine[prevNum]; + if (itemAlignment == PTR_ALIGN_TYPE && currentChar == '*' && prevCh == '*') + { + // '* *' may be a multiply followed by a dereference + if (prevNum + 2 < formattedLine.length() + && std::isblank(formattedLine[prevNum + 2])) + { + spacePadNum -= (formattedLine.length() - 2 - prevNum); + formattedLine.erase(prevNum + 2); + } + } + else if (prevNum + 1 < formattedLine.length() + && std::isblank(formattedLine[prevNum + 1]) + && prevCh != '(') + { + spacePadNum -= (formattedLine.length() - 1 - prevNum); + formattedLine.erase(prevNum + 1); + } + } + + bool isAfterScopeResolution = previousNonWSChar == ':'; + if ((itemAlignment == PTR_ALIGN_MIDDLE || itemAlignment == PTR_ALIGN_NAME) + && !isAfterScopeResolution && prevCh != '(') + { + appendSpacePad(); + // in this case appendSpacePad may or may not update the split point + if (maxCodeLength != std::string::npos && !formattedLine.empty()) + updateFormattedLineSplitPointsPointerOrReference(formattedLine.length() - 1); + appendSequence(sequenceToInsert, false); + } + else + appendSequence(sequenceToInsert, false); +} + +/** + * add or remove space padding to parens + * currentChar contains the paren + * the parens and necessary padding will be appended to formattedLine + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::padParensOrBrackets(char openDelim, char closeDelim, bool padFirstParen) +{ + assert(currentChar == openDelim || currentChar == closeDelim); + + int spacesOutsideToDelete = 0; + int spacesInsideToDelete = 0; + + bool shouldPadOutside = shouldPadParensOutside || shouldPadBracketsOutside; + bool shouldPadInside = shouldPadParensInside || shouldPadBracketsInside; + bool shouldUnPad = shouldUnPadParens || shouldUnPadBrackets; + + if (currentChar == openDelim) + { + spacesOutsideToDelete = formattedLine.length() - 1; + spacesInsideToDelete = 0; + + // compute spaces outside the opening paren to delete + if (shouldUnPad && !isInStruct) + { + char lastChar = ' '; + bool prevIsParenHeader = false; + size_t i = formattedLine.find_last_not_of(" \t"); + if (i != std::string::npos) + { + // if last char is a brace the previous whitespace is an indent + if (formattedLine[i] == '{') + spacesOutsideToDelete = 0; + else if (isCharImmediatelyPostPointerOrReference) + spacesOutsideToDelete = 0; + else + { + spacesOutsideToDelete -= i; + lastChar = formattedLine[i]; + // if previous word is a header, it will be a paren header + std::string prevWord = getPreviousWord(formattedLine, formattedLine.length()); + const std::string* prevWordH = nullptr; + if (shouldPadHeader + && !prevWord.empty() + && isCharPotentialHeader(prevWord, 0)) + prevWordH = ASBase::findHeader(prevWord, 0, headers); + + if (prevWordH != nullptr) + prevIsParenHeader = true; // don't unpad + else if (prevWord == ASResource::AS_RETURN) + prevIsParenHeader = true; // don't unpad + else if ((prevWord == ASResource::AS_NEW || prevWord == ASResource::AS_DELETE) + && shouldPadHeader) + prevIsParenHeader = true; // don't unpad + else if (isCStyle() && prevWord == ASResource::AS_THROW && shouldPadHeader) + prevIsParenHeader = true; // don't unpad + else if (prevWord == "and" || prevWord == "or" || prevWord == "in") + prevIsParenHeader = true; // don't unpad + // don't unpad variables + else if (isNumericVariable(prevWord)) + prevIsParenHeader = true; // don't unpad + } + } + // do not unpad operators, but leave them if already padded + if (shouldPadOutside || prevIsParenHeader) + { + spacesOutsideToDelete--; + } + else + { + static const std::string operatorList = "|&<>,?:;=+-*/%^"; + if (operatorList.find(lastChar) != std::string::npos || + (lastChar == openDelim && shouldPadInside) || + (lastChar == '>' && !foundCastOperator)) + { + spacesOutsideToDelete--; + } + } + + if (spacesOutsideToDelete > 0) + { + formattedLine.erase(i + 1, spacesOutsideToDelete); + spacePadNum -= spacesOutsideToDelete; + } + } + + // pad open paren outside + char peekedCharOutside = peekNextChar(); + if (padFirstParen && ( (previousChar != openDelim && peekedCharOutside != closeDelim) || shouldPadEmptyParens ) ) + appendSpacePad(); + else if (shouldPadOutside) + { + // GH19 + if (!(currentChar == openDelim && peekedCharOutside == closeDelim) || shouldPadEmptyParens) + appendSpacePad(); + } + + appendCurrentChar(); + + // unpad open paren inside + if (shouldUnPad) + { + size_t j = currentLine.find_first_not_of(" \t", charNum + 1); + if (j != std::string::npos) + spacesInsideToDelete = j - charNum - 1; + if (shouldPadInside) + 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 (shouldPadInside) + if (!(currentChar == openDelim && peekedCharInside == closeDelim)) + appendSpaceAfter(); + } + else if (currentChar == closeDelim) + { + // unpad close paren inside + if (shouldUnPad) + { + spacesInsideToDelete = formattedLine.length(); + size_t i = formattedLine.find_last_not_of(" \t"); + if (i != std::string::npos) + spacesInsideToDelete = formattedLine.length() - 1 - i; + if (shouldPadInside) + spacesInsideToDelete--; + if (spacesInsideToDelete > 0) + { + formattedLine.erase(i + 1, spacesInsideToDelete); + spacePadNum -= spacesInsideToDelete; + } + } + + // pad close paren inside + if (shouldPadInside) + if (!(previousChar == openDelim && currentChar == closeDelim)) + appendSpacePad(); + + appendCurrentChar(); + + // pad close paren outside + char peekedCharOutside = peekNextChar(); + if (shouldPadOutside) + if (peekedCharOutside != ';' + && peekedCharOutside != ',' + && peekedCharOutside != '.' + && peekedCharOutside != '+' // check for ++ + && peekedCharOutside != '-' // check for -- + && peekedCharOutside != ']') + { + appendSpaceAfter(); + } + } +} + +/** +* add or remove space padding to objective-c method prefix (- or +) +* if this is a '(' it begins a return type +* these options have precedence over the padParensOrBrackets methods +* the padParensOrBrackets method has already been called, this method adjusts +*/ +void ASFormatter::padObjCMethodPrefix() +{ + assert(isInObjCMethodDefinition && isImmediatelyPostObjCMethodPrefix); + assert(shouldPadMethodPrefix || shouldUnPadMethodPrefix); + + size_t prefix = formattedLine.find_first_of("+-"); + if (prefix == std::string::npos) + return; + size_t firstChar = formattedLine.find_first_not_of(" \t", prefix + 1); + if (firstChar == std::string::npos) + firstChar = formattedLine.length(); + int spaces = firstChar - prefix - 1; + + if (shouldPadMethodPrefix) + { + if (spaces == 0) + { + formattedLine.insert(prefix + 1, 1, ' '); + spacePadNum += 1; + } + else if (spaces > 1) + { + formattedLine.erase(prefix + 1, spaces - 1); + formattedLine[prefix + 1] = ' '; // convert any tab to space + spacePadNum -= spaces - 1; + } + } + // this option will be ignored if used with pad-method-prefix + else if (shouldUnPadMethodPrefix) + { + if (spaces > 0) + { + formattedLine.erase(prefix + 1, spaces); + spacePadNum -= spaces; + } + } +} + +/** +* add or remove space padding to objective-c parens +* these options have precedence over the padParensOrBrackets methods +* the padParensOrBrackets method has already been called, this method adjusts +*/ +void ASFormatter::padObjCReturnType() +{ + assert(currentChar == ')' && isInObjCReturnType); + assert(shouldPadReturnType || shouldUnPadReturnType); + + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextText == std::string::npos) + return; + int spaces = nextText - charNum - 1; + + if (shouldPadReturnType) + { + if (spaces == 0) + { + // this will already be padded if pad-paren is used + if (formattedLine[formattedLine.length() - 1] != ' ') + { + formattedLine.append(" "); + spacePadNum += 1; + } + } + else if (spaces > 1) + { + // do not use goForward here + currentLine.erase(charNum + 1, spaces - 1); + currentLine[charNum + 1] = ' '; // convert any tab to space + spacePadNum -= spaces - 1; + } + } + // this option will be ignored if used with pad-return-type + else if (shouldUnPadReturnType) + { + // this will already be padded if pad-paren is used + if (formattedLine[formattedLine.length() - 1] == ' ') + { + int lastText = formattedLine.find_last_not_of(" \t"); + spacePadNum -= formattedLine.length() - lastText - 1; + formattedLine.resize(lastText + 1); + } + // do not use goForward here + currentLine.erase(charNum + 1, spaces); + spacePadNum -= spaces; + } +} + +/** +* add or remove space padding to objective-c parens +* these options have precedence over the padParensOrBrackets methods +* the padParensOrBrackets method has already been called, this method adjusts +*/ +void ASFormatter::padObjCParamType() +{ + assert((currentChar == '(' || currentChar == ')') && isInObjCMethodDefinition); + assert(!isImmediatelyPostObjCMethodPrefix && !isInObjCReturnType); + assert(shouldPadParamType || shouldUnPadParamType); + + if (currentChar == '(') + { + // open paren has already been attached to formattedLine by padParen + size_t paramOpen = formattedLine.rfind('('); + assert(paramOpen != std::string::npos); + size_t prevText = formattedLine.find_last_not_of(" \t", paramOpen - 1); + if (prevText == std::string::npos) + return; + int spaces = paramOpen - prevText - 1; + + if (shouldPadParamType + || objCColonPadMode == COLON_PAD_ALL + || objCColonPadMode == COLON_PAD_AFTER) + { + if (spaces == 0) + { + formattedLine.insert(paramOpen, 1, ' '); + spacePadNum += 1; + } + if (spaces > 1) + { + formattedLine.erase(prevText + 1, spaces - 1); + formattedLine[prevText + 1] = ' '; // convert any tab to space + spacePadNum -= spaces - 1; + } + } + // this option will be ignored if used with pad-param-type + else if (shouldUnPadParamType + || objCColonPadMode == COLON_PAD_NONE + || objCColonPadMode == COLON_PAD_BEFORE) + { + if (spaces > 0) + { + formattedLine.erase(prevText + 1, spaces); + spacePadNum -= spaces; + } + } + } + else if (currentChar == ')') + { + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextText == std::string::npos) + return; + int spaces = nextText - charNum - 1; + + if (shouldPadParamType) + { + if (spaces == 0) + { + // this will already be padded if pad-paren is used + if (formattedLine[formattedLine.length() - 1] != ' ') + { + formattedLine.append(" "); + spacePadNum += 1; + } + } + else if (spaces > 1) + { + // do not use goForward here + currentLine.erase(charNum + 1, spaces - 1); + currentLine[charNum + 1] = ' '; // convert any tab to space + spacePadNum -= spaces - 1; + } + } + // this option will be ignored if used with pad-param-type + else if (shouldUnPadParamType) + { + // this will already be padded if pad-paren is used + if (formattedLine[formattedLine.length() - 1] == ' ') + { + spacePadNum -= 1; + int lastText = formattedLine.find_last_not_of(" \t"); + formattedLine.resize(lastText + 1); + } + if (spaces > 0) + { + // do not use goForward here + currentLine.erase(charNum + 1, spaces); + spacePadNum -= spaces; + } + } + } +} + +/** + * format opening brace as attached or broken + * currentChar contains the brace + * the braces will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + * + * @param braceType the type of brace to be formatted. + */ +void ASFormatter::formatOpeningBrace(BraceType braceType) +{ + assert(!isBraceType(braceType, ARRAY_TYPE)); + assert(currentChar == '{'); + + parenStack->emplace_back(0); + + bool breakBrace = isCurrentBraceBroken(); + + //478 + + if (breakBrace) + { + if (isBeforeAnyComment() && isOkToBreakBlock(braceType) && sourceIterator->hasMoreLines()) + { + // if comment is at line end leave the comment on this line + if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace) + { + currentChar = ' '; // remove brace from current line + if (parenStack->size() > 1) + parenStack->pop_back(); + currentLine[charNum] = currentChar; + appendOpeningBrace = true; // append brace to following line + } + // else put comment after the brace + else if (!isBeforeMultipleLineEndComments(charNum)) + breakLine(); + } + else if (!isBraceType(braceType, SINGLE_LINE_TYPE)) + { + formattedLine = rtrim(formattedLine); + breakLine(); + } + else if ((shouldBreakOneLineBlocks || isBraceType(braceType, BREAK_BLOCK_TYPE)) + && !isBraceType(braceType, EMPTY_BLOCK_TYPE)) + breakLine(); + else if (!isInLineBreak) + appendSpacePad(); + + appendCurrentChar(); + + // should a following comment break from the brace? + // must break the line AFTER the brace + if (isBeforeComment() + && !formattedLine.empty() + && formattedLine[0] == '{' + && isOkToBreakBlock(braceType) + && (braceFormatMode == BREAK_MODE + || braceFormatMode == LINUX_MODE)) + { + shouldBreakLineAtNextChar = true; + } + } + else // attach brace + { + // are there comments before the brace? + if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment) + { + if (isOkToBreakBlock(braceType) + && !(isCharImmediatelyPostComment && isCharImmediatelyPostLineComment) // don't attach if two comments on the line + && !isImmediatelyPostPreprocessor + && previousCommandChar != '{' // don't attach { { + && previousCommandChar != '}' // don't attach } { + && previousCommandChar != ';') // don't attach ; { + { + appendCharInsideComments(); + } + else + { + appendCurrentChar(); // don't attach + } + } + else if (previousCommandChar == '{' + || (previousCommandChar == '}' && !isInClassInitializer) + || previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';' + { + appendCurrentChar(); // don't attach + } + else + { + // if a blank line precedes this don't attach + if (isEmptyLine(formattedLine)) + appendCurrentChar(); // don't attach + else if (isOkToBreakBlock(braceType) + && !(isImmediatelyPostPreprocessor + && currentLineBeginsWithBrace)) + { + if (!isBraceType(braceType, EMPTY_BLOCK_TYPE)) + { + appendSpacePad(); + appendCurrentChar(false); // OK to attach + testForTimeToSplitFormattedLine(); // line length will have changed + // should a following comment attach with the brace? + // insert spaces to reposition the comment + if (isBeforeComment() + && !isBeforeMultipleLineEndComments(charNum) + && (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace)) + { + shouldBreakLineAtNextChar = true; + currentLine.insert(charNum + 1, charNum + 1, ' '); + } + else if (!isBeforeAnyComment()) // added in release 2.03 + { + shouldBreakLineAtNextChar = true; + } + } + else + { + if (currentLineBeginsWithBrace && (size_t) charNum == currentLineFirstBraceNum) + { + appendSpacePad(); + appendCurrentChar(false); // attach + shouldBreakLineAtNextChar = true; + } + else + { + appendSpacePad(); + appendCurrentChar(); // don't attach + } + } + } + else + { + if (!isInLineBreak) + appendSpacePad(); + appendCurrentChar(); // don't attach + } + } + } +} + +/** + * format closing brace + * currentChar contains the brace + * the calling function should have a continue statement after calling this method + * + * @param braceType the type of the opening brace for this closing brace. + */ +void ASFormatter::formatClosingBrace(BraceType braceType) +{ + assert(!isBraceType(braceType, ARRAY_TYPE)); + assert(currentChar == '}'); + + // parenStack must contain one entry + if (parenStack->size() > 1) + parenStack->pop_back(); + + // mark state of immediately after empty block + // this state will be used for locating braces that appear immediately AFTER an empty block (e.g. '{} \n}'). + if (previousCommandChar == '{') + isImmediatelyPostEmptyBlock = true; + + if (attachClosingBraceMode) + { + // for now, namespaces and classes will be attached. Uncomment the lines below to break. + if ((isEmptyLine(formattedLine) // if a blank line precedes this + || isCharImmediatelyPostLineComment + || isCharImmediatelyPostComment + || (isImmediatelyPostPreprocessor && (int) currentLine.find_first_not_of(" \t") == charNum) + ) + && (!isBraceType(braceType, SINGLE_LINE_TYPE) || isOkToBreakBlock(braceType))) + { + breakLine(); + appendCurrentChar(); // don't attach + } + else + { + if (previousNonWSChar != '{' + && (!isBraceType(braceType, SINGLE_LINE_TYPE) + || isOkToBreakBlock(braceType))) + appendSpacePad(); + appendCurrentChar(false); // attach + } + } + else if (!isBraceType(braceType, EMPTY_BLOCK_TYPE) + && (isBraceType(braceType, BREAK_BLOCK_TYPE) + || isOkToBreakBlock(braceType))) + { + breakLine(); + appendCurrentChar(); + } + else + { + appendCurrentChar(); + } + + // if a declaration follows a definition, space pad + if (isLegalNameChar(peekNextChar())) + appendSpaceAfter(); + + if (shouldBreakBlocks + && currentHeader != nullptr + && !isHeaderInMultiStatementLine + && parenStack->back() == 0) + { + if (currentHeader == &ASResource::AS_CASE || currentHeader == &ASResource::AS_DEFAULT) + { + // do not yet insert a line if "break" statement is outside the braces + std::string nextText = peekNextText(currentLine.substr(charNum + 1)); + if (!nextText.empty() + && nextText.substr(0, 5) != "break") + isAppendPostBlockEmptyLineRequested = true; + } + else + { + // GH18 + // #569 + isAppendPostBlockEmptyLineRequested = !(shouldBreakBlocks && shouldAttachClosingWhile) + || currentHeader != &ASResource::AS_DO; + } + + } + // new option for break-blocks for classes and fcts? GL83 + else if (shouldBreakClosingHeaderBlocks) + { + isAppendPostBlockEmptyLineRequested = !currentHeader && shouldBreakBlocks; + } +} + +/** + * format array braces as attached or broken + * determine if the braces can have an inStatement indent + * currentChar contains the brace + * the braces will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + * + * @param braceType the type of brace to be formatted, must be an ARRAY_TYPE. + * @param isOpeningArrayBrace indicates if this is the opening brace for the array block. + */ +void ASFormatter::formatArrayBraces(BraceType braceType, bool isOpeningArrayBrace) +{ + assert(isBraceType(braceType, ARRAY_TYPE)); + assert(currentChar == '{' || currentChar == '}'); + + if (currentChar == '{') + { + // is this the first opening brace in the array? + if (isOpeningArrayBrace) + { + formatFirstOpenBrace(braceType); + } + else // not the first opening brace + { + formatOpenBrace(); + } + } + else if (currentChar == '}') + { + formatCloseBrace(braceType); + } +} + +/** + * determine if a run-in can be attached. + * if it can insert the indents in formattedLine and reset the current line break. + */ +void ASFormatter::formatRunIn() +{ + assert(braceFormatMode == RUN_IN_MODE || braceFormatMode == NONE_MODE); + + // keep one line blocks returns true without indenting the run-in + if (formattingStyle != STYLE_PICO + && !isOkToBreakBlock(braceTypeStack->back())) + return; // true; + + // make sure the line begins with a brace + size_t lastText = formattedLine.find_last_not_of(" \t"); + if (lastText == std::string::npos || formattedLine[lastText] != '{') + return; // false; + + // make sure the brace is broken + if (formattedLine.find_first_not_of(" \t{") != std::string::npos) + return; // false; + + if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE)) + return; // false; + + bool extraIndent = false; + bool extraHalfIndent = false; + isInLineBreak = true; + + // cannot attach a class modifier without indent-classes + if (isCStyle() + && isCharPotentialHeader(currentLine, charNum) + && (isBraceType(braceTypeStack->back(), CLASS_TYPE) + || (isBraceType(braceTypeStack->back(), STRUCT_TYPE) + && isInIndentableStruct))) + { + if (findKeyword(currentLine, charNum, ASResource::AS_PUBLIC) + || findKeyword(currentLine, charNum, ASResource::AS_PRIVATE) + || findKeyword(currentLine, charNum, ASResource::AS_PROTECTED)) + { + if (getModifierIndent()) + extraHalfIndent = true; + else if (!getClassIndent()) + return; // false; + } + else if (getClassIndent()) + extraIndent = true; + } + + // cannot attach a 'case' statement without indent-switches + if (!getSwitchIndent() + && isCharPotentialHeader(currentLine, charNum) + && (findKeyword(currentLine, charNum, ASResource::AS_CASE) + || findKeyword(currentLine, charNum, ASResource::AS_DEFAULT))) + return; // false; + + // extra indent for switch statements + if (getSwitchIndent() + && !preBraceHeaderStack->empty() + && preBraceHeaderStack->back() == &ASResource::AS_SWITCH + && (isLegalNameChar(currentChar) + && !findKeyword(currentLine, charNum, ASResource::AS_CASE))) + extraIndent = true; + + isInLineBreak = false; + // remove for extra whitespace + if (formattedLine.length() > lastText + 1 + && formattedLine.find_first_not_of(" \t", lastText + 1) == std::string::npos) + formattedLine.erase(lastText + 1); + + if (extraHalfIndent) + { + int indentLength_ = getIndentLength(); + runInIndentChars = indentLength_ / 2; + formattedLine.append(runInIndentChars - 1, ' '); + } + else if (getForceTabIndentation() && getIndentLength() != getTabLength()) + { + // insert the space indents + std::string indent; + int indentLength_ = getIndentLength(); + int tabLength_ = getTabLength(); + indent.append(indentLength_, ' '); + if (extraIndent) + indent.append(indentLength_, ' '); + // replace spaces indents with tab indents + size_t tabCount = indent.length() / tabLength_; // truncate extra spaces + indent.replace(0U, tabCount * tabLength_, tabCount, '\t'); + runInIndentChars = indentLength_; + if (indent[0] == ' ') // allow for brace + indent.erase(0, 1); + formattedLine.append(indent); + } + else if (getIndentString() == "\t") + { + appendChar('\t', false); + runInIndentChars = 2; // one for { and one for tab + if (extraIndent) + { + appendChar('\t', false); + runInIndentChars++; + } + } + else // spaces + { + int indentLength_ = getIndentLength(); + formattedLine.append(indentLength_ - 1, ' '); + runInIndentChars = indentLength_; + if (extraIndent) + { + formattedLine.append(indentLength_, ' '); + runInIndentChars += indentLength_; + } + } + isInBraceRunIn = true; +} + +/** + * remove whitespace and add indentation for an array run-in. + */ +void ASFormatter::formatArrayRunIn() +{ + assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE)); + + // make sure the brace is broken + if (formattedLine.find_first_not_of(" \t{") != std::string::npos) + return; + + size_t lastText = formattedLine.find_last_not_of(" \t"); + if (lastText == std::string::npos || formattedLine[lastText] != '{') + return; + + // check for extra whitespace + if (formattedLine.length() > lastText + 1 + && formattedLine.find_first_not_of(" \t", lastText + 1) == std::string::npos) + formattedLine.erase(lastText + 1); + + if (getIndentString() == "\t") + { + appendChar('\t', false); + runInIndentChars = 2; // one for { and one for tab + } + else + { + int indent = getIndentLength(); + formattedLine.append(indent - 1, ' '); + runInIndentChars = indent; + } + isInBraceRunIn = true; + isInLineBreak = false; +} + +/** + * delete a braceTypeStack std::vector object + * BraceTypeStack did not work with the DeleteContainer template + */ +void ASFormatter::deleteContainer(std::vector*& container) +{ + if (container != nullptr) + { + container->clear(); + delete (container); + container = nullptr; + } +} + +/** + * delete a std::vector object + * T is the type of std::vector + * used for all std::vectors except braceTypeStack + */ +template +void ASFormatter::deleteContainer(T& container) +{ + if (container != nullptr) + { + container->clear(); + delete (container); + container = nullptr; + } +} + +/** + * initialize a braceType std::vector object + * braceType did not work with the DeleteContainer template + */ +void ASFormatter::initContainer(std::vector*& container, std::vector* value) +{ + if (container != nullptr) + deleteContainer(container); + container = value; +} + +/** + * initialize a std::vector object + * T is the type of std::vector + * used for all std::vectors except braceTypeStack + */ +template +void ASFormatter::initContainer(T& container, T value) +{ + // since the ASFormatter object is never deleted, + // the existing std::vectors must be deleted before creating new ones + if (container != nullptr) + deleteContainer(container); + container = value; +} + +/** + * convert a tab to spaces. + * charNum points to the current character to convert to spaces. + * tabIncrementIn is the increment that must be added for tab indent characters + * to get the correct column for the current tab. + * replaces the tab in currentLine with the required number of spaces. + * replaces the value of currentChar. + */ +void ASFormatter::convertTabToSpaces() +{ + assert(currentChar == '\t'); + + // do NOT replace if in quotes + if (isInQuote || isInQuoteContinuation) + return; + + size_t tabSize = getTabLength(); + size_t numSpaces = tabSize - ((tabIncrementIn + charNum) % tabSize); + currentLine.replace(charNum, 1, numSpaces, ' '); + currentChar = currentLine[charNum]; +} + +/** +* is it ok to break this block? +*/ +bool ASFormatter::isOkToBreakBlock(BraceType braceType) const +{ + // Actually, there should not be an ARRAY_TYPE brace here. + // But this will avoid breaking a one line block when there is. + // Otherwise they will be formatted differently on consecutive runs. + if (isBraceType(braceType, ARRAY_TYPE) + && isBraceType(braceType, SINGLE_LINE_TYPE)) + return false; + if (isBraceType(braceType, COMMAND_TYPE) + && isBraceType(braceType, EMPTY_BLOCK_TYPE)) + return false; + if (!isBraceType(braceType, SINGLE_LINE_TYPE) + || isBraceType(braceType, BREAK_BLOCK_TYPE) + || shouldBreakOneLineBlocks) + return true; + return false; +} + +/** +* check if a sharp header is a paren or non-paren header +*/ +bool ASFormatter::isSharpStyleWithParen(const std::string* header) const +{ + return (isSharpStyle() && peekNextChar() == '(' + && (header == &ASResource::AS_CATCH + || header == &ASResource::AS_DELEGATE)); +} + +/** + * Check for a following header when a comment is reached. + * firstLine must contain the start of the comment. + * return value is a pointer to the header or nullptr. + */ +const std::string* ASFormatter::checkForHeaderFollowingComment(std::string_view firstLine) const +{ + assert(isInComment || isInLineComment); + assert(shouldBreakElseIfs || shouldBreakBlocks || isInSwitchStatement()); + // look ahead to find the next non-comment text + bool endOnEmptyLine = (currentHeader == nullptr); + if (isInSwitchStatement()) + endOnEmptyLine = false; + std::string nextText = peekNextText(firstLine, endOnEmptyLine); + + if (nextText.empty() || !isCharPotentialHeader(nextText, 0)) + return nullptr; + + return ASBase::findHeader(nextText, 0, headers); +} + +/** + * process preprocessor statements. + * charNum should be the index of the #. + * + * delete braceTypeStack entries added by #if if a #else is found. + * prevents double entries in the braceTypeStack. + */ +void ASFormatter::processPreprocessor() +{ + assert(currentChar == '#'); + + const size_t preproc = currentLine.find_first_not_of(" \t", charNum + 1); + if (preproc == std::string::npos) + return; + + if (currentLine.compare(preproc, 2, "if") == 0) + { + preprocBraceTypeStackSize = braceTypeStack->size(); + } + else if (currentLine.compare(preproc, 4, "else") == 0) + { + // delete stack entries added in #if + // should be replaced by #else + if (preprocBraceTypeStackSize > 0) + { + int addedPreproc = braceTypeStack->size() - preprocBraceTypeStackSize; + for (int i = 0; i < addedPreproc; i++) + braceTypeStack->pop_back(); + } + } + else if (currentLine.compare(preproc, 6, "define") == 0) + isInPreprocessorDefineDef = true; + + + //https://sourceforge.net/p/astyle/tickets/117/ + const size_t preprocPos = currentLine.find_first_not_of(" \t", charNum + 1); + + if (includeDirectivePaddingMode != INCLUDE_PAD_NO_CHANGE + && currentLine.compare(preprocPos, 7, "include") == 0) + { + size_t firstChar = currentLine.find_first_not_of(" \t", preprocPos + 7); + if (firstChar != std::string::npos && (currentLine[firstChar] == '<' || currentLine[firstChar] == '"')) + { + currentLine.erase (preprocPos + 7, firstChar - (preprocPos + 7)); + } + + if (includeDirectivePaddingMode == INCLUDE_PAD_AFTER && + (currentLine[preprocPos + 7] == '<' || currentLine[preprocPos + 7] == '"' || std::isalpha(currentLine[preprocPos + 7])) + ) + { + currentLine.insert(preprocPos + 7, 1, ' '); + } + } + + // if top level it is potentially indentable + if (shouldIndentPreprocBlock + && (isBraceType(braceTypeStack->back(), NULL_TYPE) || isBraceType(braceTypeStack->back(), NAMESPACE_TYPE)) + && !foundClassHeader + && !isInClassInitializer + && sourceIterator->tellg() > preprocBlockEnd) + { + std::string preprocessor = ASBeautifier::extractPreprocessorStatement(currentLine); + + // indent the #if preprocessor blocks + if (preprocessor.length() >= 2 && preprocessor.substr(0, 2) == "if") // #if, #ifdef, #ifndef + { + if (isImmediatelyPostPreprocessor) + breakLine(); + isIndentablePreprocessorBlck = isIndentablePreprocessorBlock(currentLine, charNum); + isIndentablePreprocessor = isIndentablePreprocessorBlck; + } + } + + if (isIndentablePreprocessorBlck + && charNum < (int) currentLine.length() - 1 + && std::isblank(currentLine[charNum + 1])) + { + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextText != std::string::npos) + { + currentLine.erase(charNum + 1, nextText - charNum - 1); + } + } + + if (isIndentablePreprocessorBlck + && sourceIterator->tellg() >= preprocBlockEnd) + isIndentablePreprocessorBlck = false; + // need to fall thru here to reset the variables +} + +/** + * determine if the next line starts a comment + * and a header follows the comment or comments. + */ +bool ASFormatter::commentAndHeaderFollows() +{ + // called ONLY IF shouldDeleteEmptyLines and shouldBreakBlocks are TRUE. + assert(shouldDeleteEmptyLines && shouldBreakBlocks); + + // is the next line a comment + auto stream = std::make_shared(sourceIterator); + if (!stream->hasMoreLines()) + return false; + std::string nextLine_ = stream->peekNextLine(); + size_t firstChar = nextLine_.find_first_not_of(" \t"); + if (firstChar == std::string::npos + || !(nextLine_.compare(firstChar, 2, "//") == 0 + || nextLine_.compare(firstChar, 2, "/*") == 0)) + return false; + + // find the next non-comment text, and reset + std::string nextText = peekNextText(nextLine_, false, stream); + if (nextText.empty() || !isCharPotentialHeader(nextText, 0)) + return false; + + const std::string* newHeader = ASBase::findHeader(nextText, 0, headers); + + if (newHeader == nullptr) + return false; + + // if a closing header, reset break unless break is requested + if (isClosingHeader(newHeader) && !shouldBreakClosingHeaderBlocks) + { + isAppendPostBlockEmptyLineRequested = false; + return false; + } + + return true; +} + +/** + * determine if a brace should be attached or broken + * uses braces in the braceTypeStack + * the last brace in the braceTypeStack is the one being formatted + * returns true if the brace should be broken + */ +bool ASFormatter::isCurrentBraceBroken() const +{ + assert(braceTypeStack->size() > 1); + + bool breakBrace = false; + size_t stackEnd = braceTypeStack->size() - 1; + + // check brace modifiers + if (shouldAttachExternC + && isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE)) + { + return false; + } + if (shouldAttachNamespace + && isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE)) + { + return false; + } + if (shouldAttachClass + && (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE) + || isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE))) + { + return false; + } + if (shouldAttachInline + && isCStyle() // for C++ only + && braceFormatMode != RUN_IN_MODE + && !(currentLineBeginsWithBrace && peekNextChar() == '/') + && isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE)) + { + size_t i; + for (i = 1; i < braceTypeStack->size(); i++) + if (isBraceType((*braceTypeStack)[i], CLASS_TYPE) + || isBraceType((*braceTypeStack)[i], STRUCT_TYPE)) + return false; + } + + // check braces + if (isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE)) + { + if (currentLineBeginsWithBrace || braceFormatMode == RUN_IN_MODE) + { + breakBrace = true; + } + } + else if (braceFormatMode == NONE_MODE) + { + if (currentLineBeginsWithBrace + && currentLineFirstBraceNum == (size_t) charNum) + { + breakBrace = true; + } + } + else if (braceFormatMode == BREAK_MODE || braceFormatMode == RUN_IN_MODE) + { + breakBrace = true; + } + else if (braceFormatMode == LINUX_MODE) + { + // break a namespace + if (isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE)) + { + if (formattingStyle != STYLE_STROUSTRUP + && formattingStyle != STYLE_MOZILLA + && formattingStyle != STYLE_WEBKIT) + { + breakBrace = true; + } + } + // break a class or interface + else if (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE) + || isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE)) + { + if (formattingStyle != STYLE_STROUSTRUP + && formattingStyle != STYLE_WEBKIT) + { + breakBrace = true; + } + } + // break a struct if mozilla - an enum is processed as an array brace + else if (isBraceType((*braceTypeStack)[stackEnd], STRUCT_TYPE)) + { + if (formattingStyle == STYLE_MOZILLA) + { + breakBrace = true; + } + } + // break the first brace if a function + else if (isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE)) + { + if (stackEnd == 1) + { + breakBrace = true; + } + else if (stackEnd > 1) + { + // break the first brace after these if a function + if (isBraceType((*braceTypeStack)[stackEnd - 1], NAMESPACE_TYPE) + || isBraceType((*braceTypeStack)[stackEnd - 1], CLASS_TYPE) + || (isBraceType((*braceTypeStack)[stackEnd - 1], ARRAY_TYPE) && !lambdaIndicator) + || isBraceType((*braceTypeStack)[stackEnd - 1], STRUCT_TYPE) + || isBraceType((*braceTypeStack)[stackEnd - 1], EXTERN_TYPE) + ) + { + breakBrace = true; + } + } + } + } + + //breakBrace = false; + return breakBrace; +} + +/** + * format comment body + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::formatCommentBody() +{ + assert(isInComment); + + // append the comment + while (charNum < (int) currentLine.length()) + { + currentChar = currentLine[charNum]; + if (isSequenceReached(ASResource::AS_CLOSE_COMMENT) || (isGSCStyle() && isSequenceReached(ASResource::AS_GSC_CLOSE_COMMENT)) ) + { + formatCommentCloser(); + break; + } + if (currentChar == '\t' && shouldConvertTabs) + convertTabToSpaces(); + appendCurrentChar(); + ++charNum; + } + if (shouldStripCommentPrefix) + stripCommentPrefix(); +} + +/** + * format a comment opener + * the comment opener will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::formatCommentOpener() +{ + assert(isSequenceReached(ASResource::AS_OPEN_COMMENT) || isSequenceReached(ASResource::AS_GSC_OPEN_COMMENT)); + + bool isCppComment = isSequenceReached(ASResource::AS_OPEN_COMMENT); + + isInComment = isInCommentStartLine = true; + isImmediatelyPostLineComment = false; + if (previousNonWSChar == '}') + resetEndOfStatement(); + + // Check for a following header. + // For speed do not check multiple comment lines more than once. + // For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'. + const std::string* followingHeader = nullptr; + if ((doesLineStartComment + && !isImmediatelyPostCommentOnly + && isBraceType(braceTypeStack->back(), COMMAND_TYPE)) + && (shouldBreakElseIfs + || isInSwitchStatement() + || (shouldBreakBlocks + && !isImmediatelyPostEmptyLine + && previousCommandChar != '{'))) + followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum)); + + if (spacePadNum != 0 && !isInLineBreak) + adjustComments(); + formattedLineCommentNum = formattedLine.length(); + + // must be done BEFORE appendSequence + if (previousCommandChar == '{' + && !isImmediatelyPostComment + && !isImmediatelyPostLineComment) + { + if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE)) + { + // namespace run-in is always broken. + isInLineBreak = true; + } + else if (braceFormatMode == NONE_MODE) + { + // should a run-in statement be attached? + if (currentLineBeginsWithBrace) + formatRunIn(); + } + else if (braceFormatMode == ATTACH_MODE) + { + // if the brace was not attached? + if (!formattedLine.empty() && formattedLine[0] == '{' + && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)) + isInLineBreak = true; + } + else if (braceFormatMode == RUN_IN_MODE) + { + // should a run-in statement be attached? + if (!formattedLine.empty() && formattedLine[0] == '{') + formatRunIn(); + } + } + else if (!doesLineStartComment) + noTrimCommentContinuation = true; + + // ASBeautifier needs to know the following statements + if (shouldBreakElseIfs && followingHeader == &ASResource::AS_ELSE) + elseHeaderFollowsComments = true; + if (followingHeader == &ASResource::AS_CASE || followingHeader == &ASResource::AS_DEFAULT) + caseHeaderFollowsComments = true; + + // appendSequence will write the previous line + appendSequence(isCppComment ? ASResource::AS_OPEN_COMMENT : ASResource::AS_GSC_OPEN_COMMENT); + goForward(1); + + // must be done AFTER appendSequence + + // Break before the comment if a header follows the line comment. + // But not break if previous line is empty, a comment, or a '{'. + if (shouldBreakBlocks + && followingHeader != nullptr + && !isImmediatelyPostEmptyLine + && previousCommandChar != '{') + { + if (isClosingHeader(followingHeader)) + { + if (!shouldBreakClosingHeaderBlocks) + isPrependPostBlockEmptyLineRequested = false; + } + // if an opening header, break before the comment + else + isPrependPostBlockEmptyLineRequested = true; + } + + if (previousCommandChar == '}') + currentHeader = nullptr; +} + +/** + * format a comment closer + * the comment closer will be appended to the current formattedLine + */ +void ASFormatter::formatCommentCloser() +{ + assert(isSequenceReached(ASResource::AS_CLOSE_COMMENT) || (isGSCStyle() && isSequenceReached(ASResource::AS_GSC_CLOSE_COMMENT)) ); + isInComment = false; + noTrimCommentContinuation = false; + isImmediatelyPostComment = true; + appendSequence(isSequenceReached(ASResource::AS_CLOSE_COMMENT) ? ASResource::AS_CLOSE_COMMENT : ASResource::AS_GSC_CLOSE_COMMENT); + goForward(1); + if (doesLineStartComment + && (currentLine.find_first_not_of(" \t", charNum + 1) == std::string::npos)) + lineEndsInCommentOnly = true; + if (peekNextChar() == '}' + && previousCommandChar != ';' + && !isBraceType(braceTypeStack->back(), ARRAY_TYPE) + && !isInPreprocessor + && isOkToBreakBlock(braceTypeStack->back())) + { + isInLineBreak = true; + shouldBreakLineAtNextChar = true; + } +} + +/** + * format a line comment body + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::formatLineCommentBody() +{ + assert(isInLineComment); + + // append the comment + while (charNum < (int) currentLine.length()) +// && !isLineReady // commented out in release 2.04, unnecessary + { + currentChar = currentLine[charNum]; + if (currentChar == '\t' && shouldConvertTabs) + convertTabToSpaces(); + appendCurrentChar(); + ++charNum; + } + + // explicitly break a line when a line comment's end is found. + if (charNum == (int) currentLine.length()) + { + isInLineBreak = true; + isInLineComment = false; + isImmediatelyPostLineComment = true; + currentChar = 0; //make sure it is a neutral char. + } +} + +/** + * format a line comment opener + * the line comment opener will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::formatLineCommentOpener() +{ + assert(isSequenceReached(ASResource::AS_OPEN_LINE_COMMENT)); + + if ((int) currentLine.length() > charNum + 2 + && currentLine[charNum + 2] == '\xf2') // check for windows line marker + isAppendPostBlockEmptyLineRequested = false; + + isInLineComment = true; + isCharImmediatelyPostComment = false; + if (previousNonWSChar == '}') + resetEndOfStatement(); + + // Check for a following header. + // For speed do not check multiple comment lines more than once. + // For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'. + const std::string* followingHeader = nullptr; + if ((lineIsLineCommentOnly + && !isImmediatelyPostCommentOnly + && isBraceType(braceTypeStack->back(), COMMAND_TYPE)) + && (shouldBreakElseIfs + || isInSwitchStatement() + || (shouldBreakBlocks + && !isImmediatelyPostEmptyLine + && previousCommandChar != '{'))) + followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum)); + + // do not indent if in column 1 or 2 + // or in a namespace before the opening brace + if ((!shouldIndentCol1Comments && !lineCommentNoIndent) + || foundNamespaceHeader) + { + if (charNum == 0) + lineCommentNoIndent = true; + else if (charNum == 1 && currentLine[0] == ' ') + lineCommentNoIndent = true; + } + // move comment if spaces were added or deleted + if (!lineCommentNoIndent && spacePadNum != 0 && !isInLineBreak) + adjustComments(); + formattedLineCommentNum = formattedLine.length(); + + // must be done BEFORE appendSequence + // check for run-in statement + if (previousCommandChar == '{' + && !isImmediatelyPostComment + && !isImmediatelyPostLineComment) + { + if (braceFormatMode == NONE_MODE) + { + if (currentLineBeginsWithBrace) + formatRunIn(); + } + else if (braceFormatMode == RUN_IN_MODE) + { + if (!lineCommentNoIndent) + formatRunIn(); + else + isInLineBreak = true; + } + else if (braceFormatMode == BREAK_MODE) + { + if (!formattedLine.empty() && formattedLine[0] == '{') + isInLineBreak = true; + } + else + { + if (currentLineBeginsWithBrace) + isInLineBreak = true; + } + } + + // ASBeautifier needs to know the following statements + if (shouldBreakElseIfs && followingHeader == &ASResource::AS_ELSE) + elseHeaderFollowsComments = true; + if (followingHeader == &ASResource::AS_CASE || followingHeader == &ASResource::AS_DEFAULT) + caseHeaderFollowsComments = true; + + // appendSequence will write the previous line + appendSequence(ASResource::AS_OPEN_LINE_COMMENT); + goForward(1); + + // must be done AFTER appendSequence + + // Break before the comment if a header follows the line comment. + // But do not break if previous line is empty, a comment, or a '{'. + if (shouldBreakBlocks + && followingHeader != nullptr + && !isImmediatelyPostEmptyLine + && previousCommandChar != '{') + { + if (isClosingHeader(followingHeader)) + { + if (!shouldBreakClosingHeaderBlocks) + isPrependPostBlockEmptyLineRequested = false; + } + // if an opening header, break before the comment + else + isPrependPostBlockEmptyLineRequested = true; + } + + if (previousCommandChar == '}') + currentHeader = nullptr; + + // if tabbed input don't convert the immediately following tabs to spaces + if (getIndentString() == "\t" && lineCommentNoIndent) + { + while (charNum + 1 < (int) currentLine.length() + && currentLine[charNum + 1] == '\t') + { + currentChar = currentLine[++charNum]; + appendCurrentChar(); + } + } + + // explicitly break a line when a line comment's end is found. + if (charNum + 1 == (int) currentLine.length()) + { + isInLineBreak = true; + isInLineComment = false; + isImmediatelyPostLineComment = true; + currentChar = 0; //make sure it is a neutral char. + } +} + +/** + * format quote body + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::formatQuoteBody() +{ + assert(isInQuote); + + int _braceCount = 0; + + if (checkInterpolation && currentChar == '{') + { + ++_braceCount; + } + + if (isSpecialChar) + { + isSpecialChar = false; + } + else if (currentChar == '\\' && !isInVerbatimQuote) + { + if (peekNextChar() == ' ') // is this '\' at end of line + haveLineContinuationChar = true; + else + isSpecialChar = true; + } + else if (isInVerbatimQuote && currentChar == '"' ) + { + if (isCStyle()) + { + std::string delim = ')' + verbatimDelimiter; + int delimStart = charNum - delim.length(); + if (delimStart > 0 && currentLine.substr(delimStart, delim.length()) == delim) + { + isInQuote = false; + isInVerbatimQuote = false; + checkInterpolation = false; + } + } + else if (isSharpStyle() ) // GH16 + { + if ((int) currentLine.length() > charNum + 1 + && currentLine[charNum + 1] == '"') // check consecutive quotes + { + appendSequence("\"\""); + goForward(1); + return; + } + + //if ( charNum>0 && currentLine[charNum - 1] != '\\') + isInQuote = false; + + if (checkInterpolation) + isInVerbatimQuote = false; + + checkInterpolation = false; + } + } + else if (quoteChar == currentChar) + { + ////do not quit if we have CS std::string with interpolation + 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 || _braceCount > 0 ) + && currentLine[charNum + 1] != '\\') + { + currentChar = currentLine[++charNum]; + + if (checkInterpolation) + { + if (currentChar == '{') + ++_braceCount; + + if (currentChar == '}') + --_braceCount; + } + appendCurrentChar(); + } + } + if (charNum + 1 >= (int) currentLine.length() + && currentChar != '\\' + && !isInVerbatimQuote) + { + isInQuote = false; // missing closing quote + } + +} + +/** + * format a quote opener + * the quote opener will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::formatQuoteOpener() +{ + assert(currentChar == '"' + || (currentChar == '\'' && !isDigitSeparator(currentLine, charNum))); + + isInQuote = true; + quoteChar = currentChar; + + char prevPrevCh = charNum > 2 ? currentLine[charNum - 2] : ' '; // GL39 + if (isCStyle() && previousChar == 'R' && !isalpha(prevPrevCh)) + { + int parenPos = currentLine.find('(', charNum); + if (parenPos != -1) + { + isInVerbatimQuote = true; + verbatimDelimiter = currentLine.substr(charNum + 1, parenPos - charNum - 1); + } + } + else if (isSharpStyle() && (previousChar == '@' )) + { + isInVerbatimQuote = true; + checkInterpolation = true; + } + + + // a quote following a brace is an array + if (previousCommandChar == '{' + && !isImmediatelyPostComment + && !isImmediatelyPostLineComment + && isNonInStatementArray + && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE) + && !std::isblank(peekNextChar())) + { + if (braceFormatMode == NONE_MODE) + { + if (currentLineBeginsWithBrace) + formatRunIn(); + } + else if (braceFormatMode == RUN_IN_MODE) + { + formatRunIn(); + } + else if (braceFormatMode == BREAK_MODE) + { + if (!formattedLine.empty() && formattedLine[0] == '{') + isInLineBreak = true; + } + else + { + if (currentLineBeginsWithBrace) + isInLineBreak = true; + } + } + previousCommandChar = ' '; + appendCurrentChar(); +} + +/** + * get the next line comment adjustment that results from breaking a closing brace. + * the brace must be on the same line as the closing header. + * i.e "} else" changed to "} else". + */ +int ASFormatter::getNextLineCommentAdjustment() +{ + assert(foundClosingHeader && previousNonWSChar == '}'); + if (charNum < 1) // "else" is in column 1 + return 0; + size_t lastBrace = currentLine.rfind('}', charNum - 1); + if (lastBrace != std::string::npos) + return (lastBrace - charNum); // return a negative number + return 0; +} + +// for console build only +LineEndFormat ASFormatter::getLineEndFormat() const +{ + return lineEnd; +} + +/** + * get the current line comment adjustment that results from attaching + * a closing header to a closing brace. + * the brace must be on the line previous to the closing header. + * the adjustment is 2 chars, one for the brace and one for the space. + * i.e "} else" changed to "} else". + */ +int ASFormatter::getCurrentLineCommentAdjustment() +{ + assert(foundClosingHeader && previousNonWSChar == '}'); + if (charNum < 1) + return 2; + size_t lastBrace = currentLine.rfind('}', charNum - 1); + if (lastBrace == std::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 std::string if none found. + */ +std::string ASFormatter::getPreviousWord(const std::string& line, int currPos, bool allowDots) const +{ + // get the last legal word (may be a number) + if (currPos == 0) + return std::string(); + + size_t end = line.find_last_not_of(" \t", currPos - 1); + if (end == std::string::npos || !isLegalNameChar(line[end])) + return std::string(); + + int start; // start of the previous word + for (start = end; start > -1; start--) + { + if (!isLegalNameChar(line[start]) || (!allowDots && line[start] == '.') ) + break; + } + start++; + + return (line.substr(start, end - start + 1)); +} + +/** + * check if a line break is needed when a closing brace + * is followed by a closing header. + * the break depends on the braceFormatMode and other factors. + */ +void ASFormatter::isLineBreakBeforeClosingHeader() +{ + assert(foundClosingHeader && previousNonWSChar == '}'); + + if (currentHeader == &ASResource::AS_WHILE && shouldAttachClosingWhile) + { + appendClosingHeader(); + return; + } + + if (braceFormatMode == BREAK_MODE + || braceFormatMode == RUN_IN_MODE + || attachClosingBraceMode) + { + isInLineBreak = true; + } + else if (braceFormatMode == NONE_MODE) + { + if (shouldBreakClosingHeaderBraces + || getBraceIndent() || getBlockIndent()) + { + isInLineBreak = true; + } + else + { + appendSpacePad(); + // is closing brace broken? + size_t i = currentLine.find_first_not_of(" \t"); + if (i != std::string::npos && currentLine[i] == '}') + isInLineBreak = false; + + if (shouldBreakBlocks) + isAppendPostBlockEmptyLineRequested = false; + } + } + // braceFormatMode == ATTACH_MODE, LINUX_MODE + else + { + if (shouldBreakClosingHeaderBraces + || getBraceIndent() || getBlockIndent()) + { + isInLineBreak = true; + } + else + { + appendClosingHeader(); + if (shouldBreakBlocks) + isAppendPostBlockEmptyLineRequested = false; + } + } +} + +/** + * Append a closing header to the previous closing brace, if possible + */ +void ASFormatter::appendClosingHeader() +{ + // if a blank line does not precede this + // or last line is not a one line block, attach header + bool previousLineIsEmpty = isEmptyLine(formattedLine); + int previousLineIsOneLineBlock = 0; + size_t firstBrace = findNextChar(formattedLine, '{'); + if (firstBrace != std::string::npos) + previousLineIsOneLineBlock = isOneLineBlockReached(formattedLine, firstBrace); + if (!previousLineIsEmpty + && previousLineIsOneLineBlock == 0) + { + isInLineBreak = false; + appendSpacePad(); + spacePadNum = 0; // don't count as comment padding + } +} + +/** + * Add braces to a single line statement following a header. + * braces are not added if the proper conditions are not met. + * braces are added to the currentLine. + */ +bool ASFormatter::addBracesToStatement() +{ + assert(isImmediatelyPostHeader); + + if (currentHeader != &ASResource::AS_IF + && currentHeader != &ASResource::AS_ELSE + && currentHeader != &ASResource::AS_FOR + && currentHeader != &ASResource::AS_WHILE + && currentHeader != &ASResource::AS_DO + && currentHeader != &ASResource::AS_FOREACH + && currentHeader != &ASResource::AS_QFOREACH + && currentHeader != &ASResource::AS_QFOREVER + && currentHeader != &ASResource::AS_FOREVER) + return false; + + if (currentHeader == &ASResource::AS_WHILE && foundClosingHeader) // do-while + return false; + + // do not brace an empty statement + if (currentChar == ';') + return false; + + + // old behavior + if (shouldAddBraces) + { + + // do not add if a header follows + if (isCharPotentialHeader(currentLine, charNum)) + if (findHeader(headers) != nullptr) + return false; + + // find the next semi-colon + size_t nextSemiColon = charNum; + if (currentChar != ';') + nextSemiColon = findNextChar(currentLine, ';', charNum + 1); + if (nextSemiColon == std::string::npos) + return false; + + // add closing brace before changing the line length + if (nextSemiColon == currentLine.length() - 1) + currentLine.append(" }"); + else + currentLine.insert(nextSemiColon + 1, " }"); + } + + // add opening brace + currentLine.insert(charNum, "{ "); + assert(computeChecksumIn("{}")); + currentChar = '{'; + if ((int) currentLine.find_first_not_of(" \t") == charNum) + currentLineBeginsWithBrace = true; + // remove extra spaces + if (!shouldAddOneLineBraces) + { + size_t lastText = formattedLine.find_last_not_of(" \t"); + if ((formattedLine.length() - 1) - lastText > 1) + formattedLine.erase(lastText + 1); + } + return true; +} + +/** + * Remove braces from a single line statement following a header. + * braces are not removed if the proper conditions are not met. + * The first brace is replaced by a space. + */ +bool ASFormatter::removeBracesFromStatement() +{ + assert(isImmediatelyPostHeader); + assert(currentChar == '{'); + + if (currentHeader != &ASResource::AS_IF + && currentHeader != &ASResource::AS_ELSE + && currentHeader != &ASResource::AS_FOR + && currentHeader != &ASResource::AS_WHILE + && currentHeader != &ASResource::AS_FOREACH) + return false; + + if (currentHeader == &ASResource::AS_WHILE && foundClosingHeader) // do-while + return false; + + bool isFirstLine = true; + std::string nextLine_; + // leave nextLine_ empty if end of line comment follows + if (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace) + nextLine_ = currentLine.substr(charNum + 1); + size_t nextChar = 0; + + // find the first non-blank text + ASPeekStream stream(sourceIterator); + while (stream.hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + { + nextLine_ = stream.peekNextLine(); + nextChar = 0; + } + + nextChar = nextLine_.find_first_not_of(" \t", nextChar); + if (nextChar != std::string::npos) + break; + } + if (!stream.hasMoreLines()) + return false; + + // don't remove if comments or a header follow the brace + if ((nextLine_.compare(nextChar, 2, "/*") == 0) + || (nextLine_.compare(nextChar, 2, "//") == 0) + || (isCharPotentialHeader(nextLine_, nextChar) + && ASBase::findHeader(nextLine_, nextChar, headers) != nullptr)) + return false; + + // find the next semi-colon + size_t nextSemiColon = nextChar; + if (nextLine_[nextChar] != ';') + nextSemiColon = findNextChar(nextLine_, ';', nextChar + 1); + if (nextSemiColon == std::string::npos) + return false; + + // find the closing brace + isFirstLine = true; + nextChar = nextSemiColon + 1; + while (stream.hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + { + nextLine_ = stream.peekNextLine(); + nextChar = 0; + } + nextChar = nextLine_.find_first_not_of(" \t", nextChar); + if (nextChar != std::string::npos) + break; + } + if (nextLine_.empty() || nextLine_[nextChar] != '}') + return false; + + // remove opening brace + currentLine[charNum] = currentChar = ' '; + assert(adjustChecksumIn(-'{')); + return true; +} + +/** + * Find the next character that is not in quotes or a comment. + * + * @param line the line to be searched. + * @param searchChar the char to find. + * @param searchStart the start position on the line (default is 0). + * @return the position on the line or std::string::npos if not found. + */ +size_t ASFormatter::findNextChar(std::string_view line, char searchChar, int searchStart /*0*/) const +{ + // find the next searchChar + size_t i; + for (i = searchStart; i < line.length(); i++) + { + if (line.compare(i, 2, "//") == 0) + return std::string::npos; + if (line.compare(i, 2, "/*") == 0) + { + size_t endComment = line.find("*/", i + 2); + if (endComment == std::string::npos) + return std::string::npos; + i = endComment + 2; + if (i >= line.length()) + return std::string::npos; + } + if (line[i] == '"' + || (line[i] == '\'' && !isDigitSeparator(line, i))) + { + char quote = line[i]; + while (i < line.length()) + { + size_t endQuote = line.find(quote, i + 1); + if (endQuote == std::string::npos) + return std::string::npos; + i = endQuote; + if (line[endQuote - 1] != '\\') // check for '\"' + break; + if (line[endQuote - 2] == '\\') // check for '\\' + break; + } + } + + if (line[i] == searchChar) + break; + + // for now don't process C# 'delegate' braces + // do this last in case the search char is a '{' + if (line[i] == '{') + return std::string::npos; + } + if (i >= line.length()) // didn't find searchChar + return std::string::npos; + + return i; +} + +/** + * Find split point for break/attach return type. + */ +void ASFormatter::findReturnTypeSplitPoint(const std::string& firstLine) +{ + assert((isBraceType(braceTypeStack->back(), NULL_TYPE) + || isBraceType(braceTypeStack->back(), DEFINITION_TYPE))); + assert(shouldBreakReturnType || shouldBreakReturnTypeDecl + || shouldAttachReturnType || shouldAttachReturnTypeDecl); + + bool isFirstLine = true; + bool isInComment_ = false; + bool isInQuote_ = false; + bool foundSplitPoint = false; + bool isAlreadyBroken = false; + char quoteChar_ = ' '; + char currNonWSChar = ' '; + char prevNonWSChar = ' '; + size_t parenCount = 0; + size_t squareCount = 0; + size_t angleCount = 0; + size_t breakLineNum = 0; + size_t breakCharNum = std::string::npos; + std::string line = firstLine; + + // Process the lines until a ';' or '{'. + ASPeekStream stream(sourceIterator); + while (stream.hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + { + if (isInQuote_) + return; + line = stream.peekNextLine(); + if (!foundSplitPoint) + ++breakLineNum; + } + size_t firstCharNum = line.find_first_not_of(" \t"); + if (firstCharNum == std::string::npos) + continue; + if (line[firstCharNum] == '#') + { + // don't attach to a preprocessor + if (shouldAttachReturnType || shouldAttachReturnTypeDecl) + return; + continue; + } + // parse the line + for (size_t i = 0; i < line.length(); i++) + { + if (!std::isblank(line[i])) + { + prevNonWSChar = currNonWSChar; + currNonWSChar = line[i]; + } + else if (line[i] == '\t' && shouldConvertTabs) + { + size_t tabSize = getTabLength(); + size_t numSpaces = tabSize - ((tabIncrementIn + i) % tabSize); + line.replace(i, 1, numSpaces, ' '); + currentChar = line[i]; + } + if (line.compare(i, 2, "/*") == 0) + isInComment_ = true; + if (isInComment_) + { + if (line.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + if (line[i] == '\\') + { + ++i; + continue; + } + + if (isInQuote_) + { + if (line[i] == quoteChar_) + isInQuote_ = false; + continue; + } + + if (line[i] == '"' + || (line[i] == '\'' && !isDigitSeparator(line, i))) + { + isInQuote_ = true; + quoteChar_ = line[i]; + continue; + } + if (line.compare(i, 2, "//") == 0) + { + i = line.length(); + continue; + } + + // https://sourceforge.net/p/astyle/bugs/504/ + if (line[line.length() - 1] == ':') + { + i = line.length(); + foundSplitPoint = true; + continue; + } + + // not in quote or comment + if (!foundSplitPoint) + { + if (line[i] == '<') + { + ++angleCount; + continue; + } + if (line[i] == '>') + { + if (angleCount) + --angleCount; + if (!angleCount) + { + size_t nextCharNum = line.find_first_not_of(" \t*&", i + 1); + if (nextCharNum == std::string::npos) + { + breakCharNum = std::string::npos; + continue; + } + if (line[nextCharNum] != ':') // scope operator + breakCharNum = nextCharNum; + } + continue; + } + if (angleCount) + continue; + if (line[i] == '[') + { + ++squareCount; + continue; + } + if (line[i] == ']') + { + if (squareCount) + --squareCount; + continue; + } + // an assignment before the parens is not a function + if (line[i] == '=') + return; + if (std::isblank(line[i]) || line[i] == '*' || line[i] == '&') + { + size_t nextNum = line.find_first_not_of(" \t", i + 1); + if (nextNum == std::string::npos) + breakCharNum = std::string::npos; + else + { + if (line.length() > nextNum + 1 + && line[nextNum] == ':' && line[nextNum + 1] == ':') + i = --nextNum; + else if (line[nextNum] != '(') + breakCharNum = std::string::npos; + } + continue; + } + if ((isLegalNameChar(line[i]) || line[i] == '~') + && breakCharNum == std::string::npos) + { + breakCharNum = i; + if (isLegalNameChar(line[i]) + && findKeyword(line, i, ASResource::AS_OPERATOR)) + { + if (breakCharNum == firstCharNum) + isAlreadyBroken = true; + foundSplitPoint = true; + // find the operator, may be parens + size_t parenNum = + line.find_first_not_of(" \t", i + ASResource::AS_OPERATOR.length()); + if (parenNum == std::string::npos) + return; + // find paren after the operator + parenNum = line.find('(', parenNum + 1); + if (parenNum == std::string::npos) + return; + i = --parenNum; + } + continue; + } + if (line[i] == ':' + && line.length() > i + 1 + && line[i + 1] == ':') + { + size_t nextCharNum = line.find_first_not_of(" \t:", i + 1); + if (nextCharNum == std::string::npos) + return; + + if (isLegalNameChar(line[nextCharNum]) + && findKeyword(line, nextCharNum, ASResource::AS_OPERATOR)) + { + i = nextCharNum; + if (breakCharNum == firstCharNum) + isAlreadyBroken = true; + foundSplitPoint = true; + // find the operator, may be parens + size_t parenNum = + line.find_first_not_of(" \t", i + ASResource::AS_OPERATOR.length()); + if (parenNum == std::string::npos) + return; + // find paren after the operator + parenNum = line.find('(', parenNum + 1); + if (parenNum == std::string::npos) + return; + i = --parenNum; + } + else + i = --nextCharNum; + continue; + } + if (line[i] == '(' && !squareCount) + { + + // is line is already broken? + if (breakCharNum == firstCharNum && breakLineNum > 0) + isAlreadyBroken = true; + ++parenCount; + foundSplitPoint = true; + continue; + } + } + // end !foundSplitPoint + if (line[i] == '(') + { + // consecutive ')(' parens is probably a function pointer + if (prevNonWSChar == ')' && !parenCount) + return; + ++parenCount; + continue; + } + if (line[i] == ')') + { + if (parenCount) + --parenCount; + continue; + } + if (line[i] == '{') + { + if (shouldBreakReturnType && foundSplitPoint && !isAlreadyBroken) + { + methodBreakCharNum = breakCharNum; + methodBreakLineNum = breakLineNum; + } + + if (shouldAttachReturnType && foundSplitPoint && isAlreadyBroken) + { + //https://sourceforge.net/p/astyle/bugs/545/ + if ((maxCodeLength != std::string::npos && previousReadyFormattedLineLength < maxCodeLength) || maxCodeLength == std::string::npos) + { + methodAttachCharNum = breakCharNum; + methodAttachLineNum = breakLineNum; + } + } + return; + } + if (line[i] == ';') + { + if (shouldBreakReturnTypeDecl && foundSplitPoint && !isAlreadyBroken) + { + methodBreakCharNum = breakCharNum; + methodBreakLineNum = breakLineNum; + } + if (shouldAttachReturnTypeDecl && foundSplitPoint && isAlreadyBroken) + { + methodAttachCharNum = breakCharNum; + methodAttachLineNum = breakLineNum; + } + return; + } + if (line[i] == '}') + return; + } // end of for loop + if (!foundSplitPoint) + breakCharNum = std::string::npos; + } // end of while loop +} + +/** + * Look ahead in the file to see if a struct has access modifiers. + * + * @param firstLine a reference to the line to indent. + * @param index the current line index. + * @return true if the struct has access modifiers. + */ +bool ASFormatter::isStructAccessModified(const std::string& firstLine, size_t index) const +{ + assert(firstLine[index] == '{'); + assert(isCStyle()); + + bool isFirstLine = true; + size_t braceCount = 1; + std::string nextLine_ = firstLine.substr(index + 1); + ASPeekStream stream(sourceIterator); + + // find the first non-blank text, bypassing all comments and quotes. + bool isInComment_ = false; + bool isInQuote_ = false; + char quoteChar_ = ' '; + while (stream.hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + nextLine_ = stream.peekNextLine(); + // parse the line + for (size_t i = 0; i < nextLine_.length(); i++) + { + if (std::isblank(nextLine_[i])) + continue; + if (nextLine_.compare(i, 2, "/*") == 0) + isInComment_ = true; + if (isInComment_) + { + if (nextLine_.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + if (nextLine_[i] == '\\') + { + ++i; + continue; + } + + if (isInQuote_) + { + if (nextLine_[i] == quoteChar_) + isInQuote_ = false; + continue; + } + + if (nextLine_[i] == '"' + || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i))) + { + isInQuote_ = true; + quoteChar_ = nextLine_[i]; + continue; + } + if (nextLine_.compare(i, 2, "//") == 0) + { + i = nextLine_.length(); + continue; + } + // handle braces + if (nextLine_[i] == '{') + ++braceCount; + if (nextLine_[i] == '}') + --braceCount; + if (braceCount == 0) + return false; + // check for access modifiers + if (isCharPotentialHeader(nextLine_, i)) + { + if (findKeyword(nextLine_, i, ASResource::AS_PUBLIC) + || findKeyword(nextLine_, i, ASResource::AS_PRIVATE) + || findKeyword(nextLine_, i, ASResource::AS_PROTECTED)) + return true; + std::string_view name = getCurrentWord(nextLine_, i); + i += name.length() - 1; + } + } // end of for loop + } // end of while loop + + return false; +} + +/** +* Look ahead in the file to see if a preprocessor block is indentable. +* +* @param firstLine a reference to the line to indent. +* @param index the current line index. +* @return true if the block is indentable. +*/ +bool ASFormatter::isIndentablePreprocessorBlock(const std::string& firstLine, size_t index) +{ + assert(firstLine[index] == '#'); + + bool isFirstLine = true; + bool isInIndentableBlock = false; + bool blockContainsBraces = false; + bool blockContainsDefineContinuation = false; + bool isInClassConstructor = false; + bool isPotentialHeaderGuard = false; // ifndef is first preproc statement + bool isPotentialHeaderGuard2 = false; // define is within the first preproc + int numBlockIndents = 0; + int lineParenCount = 0; + std::string nextLine_ = firstLine.substr(index); + auto stream = std::make_shared(sourceIterator); + + // find end of the block, bypassing all comments and quotes. + bool isInComment_ = false; + bool isInQuote_ = false; + char quoteChar_ = ' '; + while (stream->hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + nextLine_ = stream->peekNextLine(); + // parse the line + for (size_t i = 0; i < nextLine_.length(); i++) + { + if (std::isblank(nextLine_[i])) + continue; + if (nextLine_.compare(i, 2, "/*") == 0) + isInComment_ = true; + if (isInComment_) + { + if (nextLine_.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + if (nextLine_[i] == '\\') + { + ++i; + continue; + } + if (isInQuote_) + { + if (nextLine_[i] == quoteChar_) + isInQuote_ = false; + continue; + } + + if (nextLine_[i] == '"' + || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i))) + { + isInQuote_ = true; + quoteChar_ = nextLine_[i]; + continue; + } + if (nextLine_.compare(i, 2, "//") == 0) + { + i = nextLine_.length(); + continue; + } + // handle preprocessor statement + if (nextLine_[i] == '#') + { + std::string preproc = ASBeautifier::extractPreprocessorStatement(nextLine_); + if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef + { + numBlockIndents += 1; + isInIndentableBlock = true; + // flag first preprocessor conditional for header include guard check + if (!processedFirstConditional) + { + processedFirstConditional = true; + isFirstPreprocConditional = true; + if (isNDefPreprocStatement(nextLine_, preproc)) + isPotentialHeaderGuard = true; + } + } + else if (preproc == "endif") + { + if (numBlockIndents > 0) + numBlockIndents -= 1; + // must exit BOTH loops + if (numBlockIndents == 0) + goto EndOfWhileLoop; + } + else if (preproc == "define") + { + if (nextLine_[nextLine_.length() - 1] == '\\') + blockContainsDefineContinuation = true; + // check for potential header include guards + else if (isPotentialHeaderGuard && numBlockIndents == 1) + isPotentialHeaderGuard2 = true; + } + i = nextLine_.length(); + continue; + } + // handle exceptions + if (nextLine_[i] == '{' || nextLine_[i] == '}') + blockContainsBraces = true; + else if (nextLine_[i] == '(') + ++lineParenCount; + else if (nextLine_[i] == ')') + --lineParenCount; + else if (nextLine_[i] == ':') + { + // check for '::' + if (nextLine_.length() > i + 1 && nextLine_[i + 1] == ':') + ++i; + else + isInClassConstructor = true; + } + + // bypass unnecessary parsing - must exit BOTH loops + if (blockContainsBraces || isInClassConstructor || blockContainsDefineContinuation) + goto EndOfWhileLoop; + } // end of for loop, end of line + if (lineParenCount != 0) + break; + } // end of while loop +EndOfWhileLoop: + preprocBlockEnd = sourceIterator->tellg(); + if (preprocBlockEnd < 0) + preprocBlockEnd = sourceIterator->getStreamLength(); + if (blockContainsBraces + || isInClassConstructor + || blockContainsDefineContinuation + || lineParenCount != 0 + || numBlockIndents != 0) + isInIndentableBlock = false; + // find next executable instruction + // this WILL RESET the get pointer + std::string nextText = peekNextText("", false, stream); + // bypass header include guards + if (isFirstPreprocConditional) + { + isFirstPreprocConditional = false; + if (nextText.empty() && isPotentialHeaderGuard2) + { + isInIndentableBlock = false; + preprocBlockEnd = 0; + } + } + // this allows preprocessor blocks within this block to be indented + if (!isInIndentableBlock) + preprocBlockEnd = 0; + // peekReset() is done by previous peekNextText() + return isInIndentableBlock; +} + +bool ASFormatter::isNDefPreprocStatement(std::string_view nextLine_, std::string_view preproc) const +{ + if (preproc == "ifndef") + return true; + // check for '!defined' + if (preproc == "if") + { + size_t i = nextLine_.find('!'); + if (i == std::string::npos) + return false; + i = nextLine_.find_first_not_of(" \t", ++i); + if (i != std::string::npos && nextLine_.compare(i, 7, "defined") == 0) + return true; + } + return false; +} + +/** + * Check to see if this is an EXEC SQL statement. + * + * @param line a reference to the line to indent. + * @param index the current line index. + * @return true if the statement is EXEC SQL. + */ +bool ASFormatter::isExecSQL(std::string_view line, size_t index) const +{ + if (line[index] != 'e' && line[index] != 'E') // quick check to reject most + return false; + std::string_view word; + if (isCharPotentialHeader(line, index)) + word = getCurrentWord(line, index); + for (char character : word) + character = (char) toupper(character); + if (word != "EXEC") + return false; + size_t index2 = index + word.length(); + index2 = line.find_first_not_of(" \t", index2); + if (index2 == std::string::npos) + return false; + + std::string_view word2; + + if (isCharPotentialHeader(line, index2)) + word2 = getCurrentWord(line, index2); + for (char character : word2) + character = (char) toupper(character); + if (word2 != "SQL") + return false; + return true; +} + +/** + * The continuation lines must be adjusted so the leading spaces + * is equivalent to the text on the opening line. + * + * Updates currentLine and charNum. + */ +void ASFormatter::trimContinuationLine() +{ + size_t len = currentLine.length(); + size_t tabSize = getTabLength(); + charNum = 0; + + if (leadingSpaces > 0 && len > 0) + { + size_t i; + size_t continuationIncrementIn = 0; + for (i = 0; (i < len) && (i + continuationIncrementIn < leadingSpaces); i++) + { + if (!std::isblank(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 + std::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.empty()) + currentLine = std::string(" "); // a null is inserted if this is not done + } + if (i >= len) + charNum = 0; + } +} + +/** + * Determine if a header is a closing header + * + * @return true if the header is a closing header. + */ +bool ASFormatter::isClosingHeader(const std::string* header) const +{ + return (header == &ASResource::AS_ELSE + || header == &ASResource::AS_CATCH + || header == &ASResource::AS_FINALLY); +} + +/** + * Determine if a * following a closing paren is immediately. + * after a cast. If so it is a deference and not a multiply. + * e.g. "(int*) *ptr" is a deference. + */ +bool ASFormatter::isImmediatelyPostCast() const +{ + assert(previousNonWSChar == ')' && currentChar == '*'); + // find preceding closing paren on currentLine or readyFormattedLine + std::string line; // currentLine or readyFormattedLine + size_t paren = currentLine.rfind(')', charNum); + if (paren != std::string::npos) + line = currentLine; + // if not on currentLine it must be on the previous line + else + { + line = readyFormattedLine; + paren = line.rfind(')'); + if (paren == std::string::npos) + return false; + } + if (paren == 0) + return false; + + // find character preceding the closing paren + size_t lastChar = line.find_last_not_of(" \t", paren - 1); + if (lastChar == std::string::npos) + return false; + // check for pointer cast + if (line[lastChar] == '*') + return true; + return false; +} + +/** + * Determine if a < is a template definition or instantiation. + * Sets the class variables isInTemplate and templateDepth. + */ +void ASFormatter::checkIfTemplateOpener() +{ + assert(!isInTemplate && currentChar == '<'); + + // find first char after the '<' operators + size_t firstChar = currentLine.find_first_not_of("< \t", charNum); + if (firstChar == std::string::npos + || currentLine[firstChar] == '=') + { + // this is not a template -> leave... + isInTemplate = false; + return; + } + + bool isFirstLine = true; + int parenDepth_ = 0; + int maxTemplateDepth = 0; + templateDepth = 0; + std::string nextLine_ = currentLine.substr(charNum); + ASPeekStream stream(sourceIterator); + + // find the angle braces, bypassing all comments and quotes. + bool isInComment_ = false; + bool isInQuote_ = false; + char quoteChar_ = ' '; + while (stream.hasMoreLines() || isFirstLine) + { + if (isFirstLine) + isFirstLine = false; + else + nextLine_ = stream.peekNextLine(); + // parse the line + for (size_t i = 0; i < nextLine_.length(); i++) + { + char currentChar_ = nextLine_[i]; + if (std::isblank(currentChar_)) + continue; + if (nextLine_.compare(i, 2, "/*") == 0) + isInComment_ = true; + if (isInComment_) + { + if (nextLine_.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + if (currentChar_ == '\\') + { + ++i; + continue; + } + + if (isInQuote_) + { + if (currentChar_ == quoteChar_) + isInQuote_ = false; + continue; + } + + if (currentChar_ == '"' + || (currentChar_ == '\'' && !isDigitSeparator(nextLine_, i))) + { + isInQuote_ = true; + quoteChar_ = currentChar_; + continue; + } + if (nextLine_.compare(i, 2, "//") == 0) + { + i = nextLine_.length(); + continue; + } + + // not in a comment or quote + if (currentChar_ == '<') + { + ++templateDepth; + ++maxTemplateDepth; + continue; + } + if (currentChar_ == '>') + { + --templateDepth; + if (templateDepth == 0) + { + if (parenDepth_ == 0) + { + // this is a template! + // gl85 + sf585 + isInTemplate = true; //!isInStruct; + templateDepth = maxTemplateDepth; + } + return; + } + continue; + } + if (currentChar_ == '(' || currentChar_ == ')') + { + if (currentChar_ == '(') + ++parenDepth_; + else + --parenDepth_; + if (parenDepth_ >= 0) + continue; + // this is not a template -> leave... + isInTemplate = false; + templateDepth = 0; + return; + } + if (nextLine_.compare(i, 2, ASResource::AS_AND) == 0 + || nextLine_.compare(i, 2, ASResource::AS_OR) == 0) + { + // this is not a template -> leave... + isInTemplate = false; + templateDepth = 0; + return; + } + + if (currentChar_ == ',' // comma, e.g. A + || currentChar_ == '&' // reference, e.g. A + || currentChar_ == '*' // pointer, e.g. A + || currentChar_ == '^' // C++/CLI managed pointer, e.g. A + || currentChar_ == ':' // ::, e.g. std::string + || currentChar_ == '=' // assign e.g. default parameter + || currentChar_ == '[' // [] e.g. std::string[] + || currentChar_ == ']' // [] e.g. std::string[] + || currentChar_ == '(' // (...) e.g. function definition + || currentChar_ == ')' // (...) e.g. function definition + || (isJavaStyle() && currentChar_ == '?') // Java wildcard + ) + { + continue; + } + if (!isLegalNameChar(currentChar_)) + { + // this is not a template -> leave... + isInTemplate = false; + templateDepth = 0; + return; + } + std::string_view name = getCurrentWord(nextLine_, i); + i += name.length() - 1; + } // end for loop + } // end while loop +} + +void ASFormatter::updateFormattedLineSplitPoints(char appendedChar) +{ + assert(maxCodeLength != std::string::npos); + assert(!formattedLine.empty()); + + if (!isOkToSplitFormattedLine()) + return; + + char nextChar = peekNextChar(); + + // don't split before an end of line comment + if (nextChar == '/') + return; + + // don't split before or after a brace + if (appendedChar == '{' || appendedChar == '}' + || previousNonWSChar == '{' || previousNonWSChar == '}' + || nextChar == '{' || nextChar == '}' + || currentChar == '{' || currentChar == '}') // currentChar tests for an appended brace + return; + + // don't split before or after a block paren + if (appendedChar == '[' || appendedChar == ']' + || previousNonWSChar == '[' + || nextChar == '[' || nextChar == ']') + return; + + if (std::isblank(appendedChar)) + { + if (nextChar != ')' // space before a closing paren + && nextChar != '(' // space before an opening paren + && nextChar != '/' // space before a comment + && nextChar != ':' // space before a colon + && currentChar != ')' // appended space before and after a closing paren + && currentChar != '(' // appended space before and after a opening paren + && previousNonWSChar != '(' // decided at the '(' + // don't break before a pointer or reference aligned to type + && !(nextChar == '*' + && !isCharPotentialOperator(previousNonWSChar) + && pointerAlignment == PTR_ALIGN_TYPE) + && !(nextChar == '&' + && !isCharPotentialOperator(previousNonWSChar) + && (referenceAlignment == REF_ALIGN_TYPE + || (referenceAlignment == REF_SAME_AS_PTR && pointerAlignment == PTR_ALIGN_TYPE))) + ) + { + if (formattedLine.length() - 1 <= maxCodeLength) + maxWhiteSpace = formattedLine.length() - 1; + else + maxWhiteSpacePending = formattedLine.length() - 1; + } + } + // unpadded closing parens may split after the paren (counts as whitespace) + else if (appendedChar == ')') + { + if (nextChar != ')' + && nextChar != ' ' + && nextChar != ';' + && nextChar != ',' + && nextChar != '.' + && !(nextChar == '-' && pointerSymbolFollows())) // check for -> + { + if (formattedLine.length() <= maxCodeLength) + maxWhiteSpace = formattedLine.length(); + else + maxWhiteSpacePending = formattedLine.length(); + } + } + // unpadded commas may split after the comma + else if (appendedChar == ',') + { + if (formattedLine.length() <= maxCodeLength) + maxComma = formattedLine.length(); + else + maxCommaPending = formattedLine.length(); + } + else if (appendedChar == '(') + { + if (nextChar != ')' && nextChar != '(' && nextChar != '"' && nextChar != '\'') + { + // if follows an operator break before + size_t parenNum; + if (previousNonWSChar != ' ' && isCharPotentialOperator(previousNonWSChar)) + parenNum = formattedLine.length() - 1; + else + parenNum = formattedLine.length(); + if (formattedLine.length() <= maxCodeLength) + maxParen = parenNum; + else + maxParenPending = parenNum; + } + } + else if (appendedChar == ';') + { + if (nextChar != ' ' && nextChar != '}' && nextChar != '/') // check for following comment + { + if (formattedLine.length() <= maxCodeLength) + maxSemi = formattedLine.length(); + else + maxSemiPending = formattedLine.length(); + } + } +} + +void ASFormatter::updateFormattedLineSplitPointsOperator(std::string_view sequence) +{ + assert(maxCodeLength != std::string::npos); + assert(!formattedLine.empty()); + + if (!isOkToSplitFormattedLine()) + return; + + char nextChar = peekNextChar(); + + // don't split before an end of line comment + if (nextChar == '/') + return; + + // check for logical conditional + if (sequence == "||" || sequence == "&&" || sequence == "or" || sequence == "and") + { + if (shouldBreakLineAfterLogical) + { + if (formattedLine.length() <= maxCodeLength) + maxAndOr = formattedLine.length(); + else + maxAndOrPending = formattedLine.length(); + } + else + { + // adjust for leading space in the sequence + size_t sequenceLength = sequence.length(); + if (formattedLine.length() > sequenceLength + && std::isblank(formattedLine[formattedLine.length() - sequenceLength - 1])) + sequenceLength++; + if (formattedLine.length() - sequenceLength <= maxCodeLength) + maxAndOr = formattedLine.length() - sequenceLength; + else + maxAndOrPending = formattedLine.length() - sequenceLength; + } + } + // comparison operators will split after the operator (counts as whitespace) + else if (sequence == "==" || sequence == "!=" || sequence == ">=" || sequence == "<=") + { + if (formattedLine.length() <= maxCodeLength) + maxWhiteSpace = formattedLine.length(); + else + maxWhiteSpacePending = formattedLine.length(); + } + // unpadded operators that will split BEFORE the operator (counts as whitespace) + else if (sequence == "+" || sequence == "-" || sequence == "?") + { + if (charNum > 0 + && !(sequence == "+" && isInExponent()) + && !(sequence == "-" && isInExponent()) + && (isLegalNameChar(currentLine[charNum - 1]) + || currentLine[charNum - 1] == ')' + || currentLine[charNum - 1] == ']' + || currentLine[charNum - 1] == '\"')) + { + if (formattedLine.length() - 1 <= maxCodeLength) + maxWhiteSpace = formattedLine.length() - 1; + else + maxWhiteSpacePending = formattedLine.length() - 1; + } + } + // unpadded operators that will USUALLY split AFTER the operator (counts as whitespace) + else if (sequence == "=" || sequence == ":") + { + // split BEFORE if the line is too long + // do NOT use <= here, must allow for a brace attached to an array + size_t splitPoint = 0; + if (formattedLine.length() < maxCodeLength) + splitPoint = formattedLine.length(); + else + splitPoint = formattedLine.length() - 1; + // padded or unpadded arrays + if (previousNonWSChar == ']') + { + if (formattedLine.length() - 1 <= maxCodeLength) + maxWhiteSpace = splitPoint; + else + maxWhiteSpacePending = splitPoint; + } + else if (charNum > 0 + && (isLegalNameChar(currentLine[charNum - 1]) + || currentLine[charNum - 1] == ')' + || currentLine[charNum - 1] == ']')) + { + if (formattedLine.length() <= maxCodeLength) + maxWhiteSpace = splitPoint; + else + maxWhiteSpacePending = splitPoint; + } + } +} + +/** + * Update the split point when a pointer or reference is formatted. + * The argument is the maximum index of the last whitespace character. + */ +void ASFormatter::updateFormattedLineSplitPointsPointerOrReference(size_t index) +{ + assert(maxCodeLength != std::string::npos); + assert(!formattedLine.empty()); + assert(index < formattedLine.length()); + + if (!isOkToSplitFormattedLine()) + return; + + if (index < maxWhiteSpace) // just in case + return; + + if (index <= maxCodeLength) + maxWhiteSpace = index; + else + maxWhiteSpacePending = index; +} + +bool ASFormatter::isOkToSplitFormattedLine() +{ + assert(maxCodeLength != std::string::npos); + // Is it OK to split the line? + if (shouldKeepLineUnbroken + || isInLineComment + || isInComment + || isInQuote + || isInCase + || isInPreprocessor + || isInExecSQL + || isInAsm || isInAsmOneLine || isInAsmBlock + || isInTemplate) + return false; + + if (!isOkToBreakBlock(braceTypeStack->back()) && currentChar != '{') + { + shouldKeepLineUnbroken = true; + clearFormattedLineSplitPoints(); + return false; + } + if (isBraceType(braceTypeStack->back(), ARRAY_TYPE)) + { + shouldKeepLineUnbroken = true; + if (!isBraceType(braceTypeStack->back(), ARRAY_NIS_TYPE)) + clearFormattedLineSplitPoints(); + return false; + } + return true; +} + +/* This is called if the option maxCodeLength is set. + */ +void ASFormatter::testForTimeToSplitFormattedLine() +{ + // DO NOT ASSERT maxCodeLength HERE + // should the line be split + if (formattedLine.length() > maxCodeLength && !isLineReady) + { + size_t splitPoint = findFormattedLineSplitPoint(); + if (splitPoint > 0 && splitPoint < formattedLine.length()) + { + std::string splitLine = formattedLine.substr(splitPoint); + formattedLine = formattedLine.substr(0, splitPoint); + breakLine(true); + formattedLine = splitLine; + // if break-blocks is requested and this is a one-line statement + std::string nextWord = ASBeautifier::getNextWord(currentLine, charNum - 1); + if (isAppendPostBlockEmptyLineRequested + && (nextWord == "break" || nextWord == "continue")) + { + isAppendPostBlockEmptyLineRequested = false; + isPrependPostBlockEmptyLineRequested = true; + } + else + isPrependPostBlockEmptyLineRequested = false; + // adjust max split points + maxAndOr = (maxAndOr > splitPoint) ? (maxAndOr - splitPoint) : 0; + maxSemi = (maxSemi > splitPoint) ? (maxSemi - splitPoint) : 0; + maxComma = (maxComma > splitPoint) ? (maxComma - splitPoint) : 0; + maxParen = (maxParen > splitPoint) ? (maxParen - splitPoint) : 0; + maxWhiteSpace = (maxWhiteSpace > splitPoint) ? (maxWhiteSpace - splitPoint) : 0; + if (maxSemiPending > 0) + { + maxSemi = (maxSemiPending > splitPoint) ? (maxSemiPending - splitPoint) : 0; + maxSemiPending = 0; + } + if (maxAndOrPending > 0) + { + maxAndOr = (maxAndOrPending > splitPoint) ? (maxAndOrPending - splitPoint) : 0; + maxAndOrPending = 0; + } + if (maxCommaPending > 0) + { + maxComma = (maxCommaPending > splitPoint) ? (maxCommaPending - splitPoint) : 0; + maxCommaPending = 0; + } + if (maxParenPending > 0) + { + maxParen = (maxParenPending > splitPoint) ? (maxParenPending - splitPoint) : 0; + maxParenPending = 0; + } + if (maxWhiteSpacePending > 0) + { + maxWhiteSpace = (maxWhiteSpacePending > splitPoint) ? (maxWhiteSpacePending - splitPoint) : 0; + maxWhiteSpacePending = 0; + } + // don't allow an empty formatted line + size_t firstText = formattedLine.find_first_not_of(" \t"); + if (firstText == std::string::npos && !formattedLine.empty()) + { + formattedLine.erase(); + clearFormattedLineSplitPoints(); + if (std::isblank(currentChar)) + for (size_t i = charNum + 1; i < currentLine.length() && std::isblank(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 != std::string::npos) + { + formattedLineCommentNum = formattedLine.find("//"); + if (formattedLineCommentNum == std::string::npos) + formattedLineCommentNum = formattedLine.find("/*"); + } + } + } +} + +size_t ASFormatter::findFormattedLineSplitPoint() const +{ + assert(maxCodeLength != std::string::npos); + // determine where to split + size_t minCodeLength = 10; + size_t splitPoint = 0; + splitPoint = maxSemi; + if (maxAndOr >= minCodeLength) + splitPoint = maxAndOr; + if (splitPoint < minCodeLength) + { + splitPoint = maxWhiteSpace; + // use maxParen instead if it is long enough + if (maxParen > splitPoint + || maxParen >= maxCodeLength * .7) + splitPoint = maxParen; + // use maxComma instead if it is long enough + // increasing the multiplier causes more splits at whitespace + if (maxComma > splitPoint + || maxComma >= maxCodeLength * .3) + splitPoint = maxComma; + } + // replace split point with first available break point + if (splitPoint < minCodeLength) + { + splitPoint = std::string::npos; + if (maxSemiPending > 0 && maxSemiPending < splitPoint) + splitPoint = maxSemiPending; + if (maxAndOrPending > 0 && maxAndOrPending < splitPoint) + splitPoint = maxAndOrPending; + if (maxCommaPending > 0 && maxCommaPending < splitPoint) + splitPoint = maxCommaPending; + if (maxParenPending > 0 && maxParenPending < splitPoint) + splitPoint = maxParenPending; + if (maxWhiteSpacePending > 0 && maxWhiteSpacePending < splitPoint) + splitPoint = maxWhiteSpacePending; + if (splitPoint == std::string::npos) + splitPoint = 0; + } + // if remaining line after split is too long + else if (formattedLine.length() - splitPoint > maxCodeLength) + { + // if end of the currentLine, find a new split point + size_t newCharNum; + if (!std::isblank(currentChar) && isCharPotentialHeader(currentLine, charNum)) + newCharNum = getCurrentWord(currentLine, charNum).length() + charNum; + else + newCharNum = charNum + 2; + if (newCharNum + 1 > currentLine.length()) + { + // don't move splitPoint from before a conditional to after + if (maxWhiteSpace > splitPoint + 3) + splitPoint = maxWhiteSpace; + if (maxParen > splitPoint) + splitPoint = maxParen; + } + } + + return splitPoint; +} + +void ASFormatter::clearFormattedLineSplitPoints() +{ + maxSemi = 0; + maxAndOr = 0; + maxComma = 0; + maxParen = 0; + maxWhiteSpace = 0; + maxSemiPending = 0; + maxAndOrPending = 0; + maxCommaPending = 0; + maxParenPending = 0; + maxWhiteSpacePending = 0; +} + +/** + * Check if a pointer symbol (->) follows on the currentLine. + */ +bool ASFormatter::pointerSymbolFollows() const +{ + size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); + if (peekNum == std::string::npos || currentLine.compare(peekNum, 2, "->") != 0) + return false; + return true; +} + +/** + * Compute the input checksum. + * This is called as an assert so it for is debug config only + */ +bool ASFormatter::computeChecksumIn(std::string_view currentLine_) +{ + for (const char character : currentLine_) + if (!std::isblank(character)) + checksumIn += character; + return true; +} + +/** + * Adjust the input checksum for deleted chars. + * This is called as an assert so it for is debug config only + */ +bool ASFormatter::adjustChecksumIn(int adjustment) +{ + checksumIn += adjustment; + return true; +} + +/** + * get the value of checksumIn for unit testing + * + * @return checksumIn. + */ +size_t ASFormatter::getChecksumIn() const +{ + return checksumIn; +} + +/** + * Compute the output checksum. + * This is called as an assert so it is for debug config only + */ +bool ASFormatter::computeChecksumOut(std::string_view beautifiedLine) +{ + for (const char character : beautifiedLine) + if (!std::isblank(character)) + checksumOut += character; + 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 std::string* ASFormatter::getFollowingOperator() const +{ + // find next word + size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextNum == std::string::npos) + return nullptr; + + if (!isLegalNameChar(currentLine[nextNum])) + return nullptr; + + // bypass next word and following spaces + while (nextNum < currentLine.length()) + { + if (!isLegalNameChar(currentLine[nextNum]) + && !std::isblank(currentLine[nextNum])) + break; + nextNum++; + } + + if (nextNum >= currentLine.length() + || !isCharPotentialOperator(currentLine[nextNum]) + || currentLine[nextNum] == '/') // comment + return nullptr; + + const std::string* newOperator = ASBase::findOperator(currentLine, nextNum, operators); + return newOperator; +} + +// Check following data to determine if the current character is an array operator. +bool ASFormatter::isArrayOperator() const +{ + assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); + assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE)); + + // find next word + size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextNum == std::string::npos) + return false; + + if (!isLegalNameChar(currentLine[nextNum])) + return false; + + // bypass next word and following spaces + while (nextNum < currentLine.length()) + { + if (!isLegalNameChar(currentLine[nextNum]) + && !std::isblank(currentLine[nextNum])) + break; + nextNum++; + } + + // check for characters that indicate an operator + if (currentLine[nextNum] == ',' + || currentLine[nextNum] == '}' + || currentLine[nextNum] == ')' + || currentLine[nextNum] == '(') + return true; + return false; +} + + +// Reset the flags that indicate various statement information. +void ASFormatter::resetEndOfStatement() +{ + foundQuestionMark = false; + foundNamespaceHeader = false; + foundClassHeader = false; + foundStructHeader = false; + foundInterfaceHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundPreCommandMacro = false; + foundTrailingReturnType = false; + foundCastOperator = false; + isInPotentialCalculation = false; + isSharpAccessor = false; + isSharpDelegate = false; + isInObjCMethodDefinition = false; + isImmediatelyPostObjCMethodPrefix = false; + isInObjCReturnType = false; + isInObjCParam = false; + isInObjCInterface = false; + isInObjCSelector = false; + isInEnum = false; + isInExternC = false; + elseHeaderFollowsComments = false; + returnTypeChecked = false; + nonInStatementBrace = 0; + while (!questionMarkStack->empty()) + questionMarkStack->pop_back(); +} + +// Find the colon alignment for Objective-C method definitions and method calls. +int ASFormatter::findObjCColonAlignment() const +{ + assert(currentChar == '+' || currentChar == '-' || currentChar == '['); + assert(getAlignMethodColon()); + + bool isFirstLine = true; + bool haveFirstColon = false; + bool foundMethodColon = false; + bool isInComment_ = false; + bool isInQuote_ = false; + bool haveTernary = false; + char quoteChar_ = ' '; + int sqBracketCount = 0; + int colonAdjust = 0; + int colonAlign = 0; + std::string nextLine_ = currentLine; + ASPeekStream stream(sourceIterator); + + // peek next line + while (sourceIterator->hasMoreLines() || isFirstLine) + { + if (!isFirstLine) + nextLine_ = stream.peekNextLine(); + // parse the line + haveFirstColon = false; + nextLine_ = ASBeautifier::trim(nextLine_); + for (size_t i = 0; i < nextLine_.length(); i++) + { + if (std::isblank(nextLine_[i])) + continue; + if (nextLine_.compare(i, 2, "/*") == 0) + isInComment_ = true; + if (isInComment_) + { + if (nextLine_.compare(i, 2, "*/") == 0) + { + isInComment_ = false; + ++i; + } + continue; + } + if (nextLine_[i] == '\\') + { + ++i; + continue; + } + if (isInQuote_) + { + if (nextLine_[i] == quoteChar_) + isInQuote_ = false; + continue; + } + + if (nextLine_[i] == '"' + || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i))) + { + isInQuote_ = true; + quoteChar_ = nextLine_[i]; + continue; + } + if (nextLine_.compare(i, 2, "//") == 0) + { + i = nextLine_.length(); + continue; + } + // process the current char + if ((nextLine_[i] == '{' && (currentChar == '-' || currentChar == '+')) + || nextLine_[i] == ';') + goto EndOfWhileLoop; // end of method definition + if (nextLine_[i] == ']') + { + --sqBracketCount; + if (sqBracketCount == 0) + goto EndOfWhileLoop; // end of method call + } + if (nextLine_[i] == '[') + ++sqBracketCount; + if (isFirstLine) // colon align does not include the first line + continue; + if (sqBracketCount > 1) + continue; + if (haveFirstColon) // multiple colons per line + continue; + if (nextLine_[i] == '?') + { + haveTernary = true; + continue; + } + // compute colon adjustment + if (nextLine_[i] == ':') + { + if (haveTernary) + { + haveTernary = false; + continue; + } + haveFirstColon = true; + foundMethodColon = true; + if (isObjCStyle() && shouldPadMethodColon) + { + int spacesStart; + for (spacesStart = i; spacesStart > 0; spacesStart--) + if (!std::isblank(nextLine_[spacesStart - 1])) + break; + int spaces = i - spacesStart; + if (objCColonPadMode == COLON_PAD_ALL || objCColonPadMode == COLON_PAD_BEFORE) + colonAdjust = 1 - spaces; + else if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_AFTER) + colonAdjust = 0 - spaces; + } + // compute alignment + int colonPosition = i + colonAdjust; + if (colonPosition > colonAlign) + colonAlign = colonPosition; + } + } // end of for loop + isFirstLine = false; + } // end of while loop +EndOfWhileLoop: + if (!foundMethodColon) + colonAlign = -1; + return colonAlign; +} + +// pad an Objective-C method colon +void ASFormatter::padObjCMethodColon() +{ + assert(currentChar == ':'); + int commentAdjust = 0; + char nextChar = peekNextChar(); + if (objCColonPadMode == COLON_PAD_NONE + || objCColonPadMode == COLON_PAD_AFTER + || nextChar == ')') + { + // remove spaces before + for (int i = formattedLine.length() - 1; (i > -1) && std::isblank(formattedLine[i]); i--) + { + formattedLine.erase(i); + --commentAdjust; + } + } + else + { + // pad space before + for (int i = formattedLine.length() - 1; (i > 0) && std::isblank(formattedLine[i]); i--) + if (std::isblank(formattedLine[i - 1])) + { + formattedLine.erase(i); + --commentAdjust; + } + if (!formattedLine.empty()) + { + appendSpacePad(); + formattedLine.back() = ' '; // convert any tab to space + } + } + if (objCColonPadMode == COLON_PAD_NONE + || objCColonPadMode == COLON_PAD_BEFORE + || nextChar == ')') + { + // remove spaces after + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextText == std::string::npos) + nextText = currentLine.length(); + int spaces = nextText - charNum - 1; + if (spaces > 0) + { + // do not use goForward here + currentLine.erase(charNum + 1, spaces); + spacePadNum -= spaces; + } + } + else + { + // pad space after + size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); + if (nextText == std::string::npos) + nextText = currentLine.length(); + int spaces = nextText - charNum - 1; + if (spaces == 0) + { + currentLine.insert(charNum + 1, 1, ' '); + spacePadNum += 1; + } + else if (spaces > 1) + { + // do not use goForward here + currentLine.erase(charNum + 1, spaces - 1); + currentLine[charNum + 1] = ' '; // convert any tab to space + spacePadNum -= spaces - 1; + } + } + spacePadNum += commentAdjust; +} + +// Remove the leading '*' from a comment line and indent to the next tab. +void ASFormatter::stripCommentPrefix() +{ + int firstChar = formattedLine.find_first_not_of(" \t"); + if (firstChar < 0) + return; + + if (isInCommentStartLine) + { + // comment opener must begin the line + if (formattedLine.compare(firstChar, 2, "/*") != 0) + return; + int commentOpener = firstChar; + // ignore single line comments + int commentEnd = formattedLine.find("*/", firstChar + 2); + if (commentEnd != -1) + return; + // first char after the comment opener must be at least one indent + int followingText = formattedLine.find_first_not_of(" \t", commentOpener + 2); + if (followingText < 0) + return; + if (formattedLine[followingText] == '*' || formattedLine[followingText] == '!') + followingText = formattedLine.find_first_not_of(" \t", followingText + 1); + if (followingText < 0) + return; + if (formattedLine[followingText] == '*') + return; + int indentLen = getIndentLength(); + int followingTextIndent = followingText - commentOpener; + if (followingTextIndent < indentLen) + { + std::string stringToInsert(indentLen - followingTextIndent, ' '); + formattedLine.insert(followingText, stringToInsert); + } + return; + } + // comment body including the closer + if (formattedLine[firstChar] == '*') + { + if (formattedLine.compare(firstChar, 2, "*/") == 0) + { + // line starts with an end comment + formattedLine = "*/"; + } + else + { + // build a new line with one indent + int secondChar = formattedLine.find_first_not_of(" \t", firstChar + 1); + if (secondChar < 0) + { + adjustChecksumIn(-'*'); + formattedLine.erase(); + return; + } + if (formattedLine[secondChar] == '*') + return; + // replace the leading '*' + int indentLen = getIndentLength(); + adjustChecksumIn(-'*'); + // second char must be at least one indent + if (formattedLine.substr(0, secondChar).find('\t') != std::string::npos) + { + formattedLine.erase(firstChar, 1); + } + else + { + int spacesToInsert = 0; + if (secondChar >= indentLen) + spacesToInsert = secondChar; + else + spacesToInsert = indentLen; + formattedLine = std::string(spacesToInsert, ' ') + formattedLine.substr(secondChar); + } + // remove a trailing '*' + int lastChar = formattedLine.find_last_not_of(" \t"); + if (lastChar > -1 && formattedLine[lastChar] == '*') + { + adjustChecksumIn(-'*'); + formattedLine[lastChar] = ' '; + } + } + } + else + { + // first char not a '*' + // first char must be at least one indent + if (formattedLine.substr(0, firstChar).find('\t') == std::string::npos) + { + int indentLen = getIndentLength(); + if (firstChar < indentLen) + { + std::string stringToInsert(indentLen, ' '); + formattedLine = stringToInsert + formattedLine.substr(firstChar); + } + } + } +} + +} // end namespace astyle diff --git a/astyle/AStyle/src/ASLocalizer.cpp b/astyle/AStyle/src/ASLocalizer.cpp new file mode 100644 index 0000000..0ea6484 --- /dev/null +++ b/astyle/AStyle/src/ASLocalizer.cpp @@ -0,0 +1,1163 @@ +// ASLocalizer.cpp +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. +// +// File encoding for this file is UTF-8 WITHOUT a byte order mark (BOM). +// русский 中文(简体) 日本語 한국의 +// +// Windows: +// Add the required "Language" to the system. +// The settings do NOT need to be changed to the added language. +// Change the "Region" settings. +// Change both the "Format" and the "Current Language..." settings. +// A restart is required if the codepage has changed. +// Windows problems: +// Hindi - no available locale, language pack removed +// Japanese - language pack install error +// Ukrainian - displays a ? instead of i +// +// Linux: +// Change the LANG environment variable: LANG=fr_FR.UTF-8. +// setlocale() will use the LANG environment variable on Linux. +// +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * To add a new language to this source module: + * + * Add a new translation class to ASLocalizer.h. + * Update the WinLangCode array in ASLocalizer.cpp. + * Add the language code to setTranslationClass() in ASLocalizer.cpp. + * Add the English-Translation pair to the constructor in ASLocalizer.cpp. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +//---------------------------------------------------------------------------- +// headers +//---------------------------------------------------------------------------- + +#include "ASLocalizer.h" + +#ifdef _WIN32 + #include +#endif + +#include + + +#include // needed by some compilers +#include +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning(disable: 4996) // secure version deprecation warnings +#endif + +#ifdef __BORLANDC__ + #pragma warn -8104 // Local Static with constructor dangerous for multi-threaded apps +#endif + +#ifdef __INTEL_COMPILER + // #pragma warning(disable: 383) // value copied to temporary, reference to temporary used + // #pragma warning(disable: 981) // operands are evaluated in unspecified order +#endif + +#ifdef __clang__ + #pragma clang diagnostic ignored "-Wdeprecated-declarations" // wcstombs +#endif + +namespace astyle { + +#ifndef ASTYLE_LIB + +//---------------------------------------------------------------------------- +// ASLocalizer class methods. +//---------------------------------------------------------------------------- + +ASLocalizer::ASLocalizer() +// Set the locale information. +{ + // set language default values to english (ascii) + // this will be used if a locale or a language cannot be found + m_langID = "en"; + m_subLangID.clear(); + m_translationClass = nullptr; + + // Not all compilers support the C++ function locale::global(locale("")); + char* localeName = setlocale(LC_ALL, ""); + if (localeName == nullptr) // use the english (ascii) defaults + { + fprintf(stderr, "\n%s\n\n", "Cannot set native locale, reverting to English"); + setTranslationClass(); + return; + } + // set the class variables +#ifdef _WIN32 + size_t lcid = GetUserDefaultLCID(); + setLanguageFromLCID(lcid); + m_codepage = GetACP(); +#else + setLanguageFromName(localeName); +#endif +} + +ASLocalizer::~ASLocalizer() +// Delete dynamically allocated memory. +{ + delete m_translationClass; +} + +#ifdef _WIN32 + +struct WinLangCode +{ + size_t winLang; + char canonicalLang[3]; +}; + +static WinLangCode wlc[] = +// primary language identifier http://msdn.microsoft.com/en-us/library/aa912554.aspx +// sublanguage identifier http://msdn.microsoft.com/en-us/library/aa913256.aspx +// language ID http://msdn.microsoft.com/en-us/library/ee797784%28v=cs.20%29.aspx +{ + { LANG_BULGARIAN, "bg" }, // bg-BG 1251 + { LANG_CHINESE, "zh" }, // zh-CHS, zh-CHT + { LANG_DUTCH, "nl" }, // nl-NL 1252 + { LANG_ENGLISH, "en" }, // en-US 1252 + { LANG_ESTONIAN, "et" }, // et-EE + { LANG_FINNISH, "fi" }, // fi-FI 1252 + { LANG_FRENCH, "fr" }, // fr-FR 1252 + { LANG_GERMAN, "de" }, // de-DE 1252 + { LANG_GREEK, "el" }, // el-GR 1253 + { LANG_HINDI, "hi" }, // hi-IN + { LANG_HUNGARIAN, "hu" }, // hu-HU 1250 + { LANG_ITALIAN, "it" }, // it-IT 1252 + { LANG_JAPANESE, "ja" }, // ja-JP + { LANG_KOREAN, "ko" }, // ko-KR + { LANG_NORWEGIAN, "nn" }, // nn-NO 1252 + { LANG_POLISH, "pl" }, // pl-PL 1250 + { LANG_PORTUGUESE, "pt" }, // pt-PT 1252 + { LANG_ROMANIAN, "ro" }, // ro-RO 1250 + { LANG_RUSSIAN, "ru" }, // ru-RU 1251 + { LANG_SPANISH, "es" }, // es-ES 1252 + { LANG_SWEDISH, "sv" }, // sv-SE 1252 + { LANG_UKRAINIAN, "uk" }, // uk-UA 1251 +}; + +void ASLocalizer::setLanguageFromLCID(size_t lcid) +// Windows get the language to use from the user locale. +// NOTE: GetUserDefaultLocaleName() gets nearly the same name as Linux. +// But it needs Windows Vista or higher. +// Same with LCIDToLocaleName(). +{ + m_lcid = lcid; + m_langID = "en"; // default to english + + size_t lang = PRIMARYLANGID(LANGIDFROMLCID(m_lcid)); + size_t sublang = SUBLANGID(LANGIDFROMLCID(m_lcid)); + // find language in the wlc table + for (WinLangCode language : wlc) + { + if (language.winLang == lang) + { + m_langID = language.canonicalLang; + break; + } + } + if (m_langID == "zh") + { + if (sublang == SUBLANG_CHINESE_SIMPLIFIED || sublang == SUBLANG_CHINESE_SINGAPORE) + m_subLangID = "CHS"; + else + m_subLangID = "CHT"; // default + } + setTranslationClass(); +} + +#endif // _WIN32 + +std::string ASLocalizer::getLanguageID() const +// Returns the language ID in m_langID. +{ + return m_langID; +} + +const Translation* ASLocalizer::getTranslationClass() const +// Returns the name of the translation class in m_translation. Used for testing. +{ + assert(m_translationClass); + return m_translationClass; +} + +void ASLocalizer::setLanguageFromName(const char* langID) +// Linux set the language to use from the langID. +// +// the language string has the following form +// +// lang[_LANG][.encoding][@modifier] +// +// (see environ(5) in the Open Unix specification) +// +// where lang is the primary language, LANG is a sublang/territory, +// encoding is the charset to use and modifier "allows the user to select +// a specific instance of localization data within a single category" +// +// for example, the following strings are valid: +// fr +// fr_FR +// de_DE.iso88591 +// de_DE@euro +// de_DE.iso88591@euro +{ + // the constants describing the format of lang_LANG locale string + std::string langStr = langID; + m_langID = langStr.substr(0, 2); + + // need the sublang for chinese + if (m_langID == "zh" && langStr[2] == '_') + { + std::string subLang = langStr.substr(3, 2); + if (subLang == "CN" || subLang == "SG") + m_subLangID = "CHS"; + else + m_subLangID = "CHT"; // default + } + setTranslationClass(); +} + +const char* ASLocalizer::settext(const char* textIn) const +// Call the settext class and return the value. +{ + assert(m_translationClass); + const std::string stringIn = textIn; + return m_translationClass->translate(stringIn).c_str(); +} + +void ASLocalizer::setTranslationClass() +// Return the required translation class. +// Sets the class variable m_translationClass from the value of m_langID. +// Get the language ID at http://msdn.microsoft.com/en-us/library/ee797784%28v=cs.20%29.aspx +{ + assert(m_langID.length()); + // delete previously set (--ascii option) + if (m_translationClass != nullptr) + { + delete m_translationClass; + m_translationClass = nullptr; + } + if (m_langID == "bg") + m_translationClass = new Bulgarian; + else if (m_langID == "zh" && m_subLangID == "CHS") + m_translationClass = new ChineseSimplified; + else if (m_langID == "zh" && m_subLangID == "CHT") + m_translationClass = new ChineseTraditional; + else if (m_langID == "nl") + m_translationClass = new Dutch; + else if (m_langID == "en") + m_translationClass = new English; + else if (m_langID == "et") + m_translationClass = new Estonian; + else if (m_langID == "fi") + m_translationClass = new Finnish; + else if (m_langID == "fr") + m_translationClass = new French; + else if (m_langID == "de") + m_translationClass = new German; + else if (m_langID == "el") + m_translationClass = new Greek; + else if (m_langID == "hi") + m_translationClass = new Hindi; + else if (m_langID == "hu") + m_translationClass = new Hungarian; + else if (m_langID == "it") + m_translationClass = new Italian; + else if (m_langID == "ja") + m_translationClass = new Japanese; + else if (m_langID == "ko") + m_translationClass = new Korean; + else if (m_langID == "nn") + m_translationClass = new Norwegian; + else if (m_langID == "pl") + m_translationClass = new Polish; + else if (m_langID == "pt") + m_translationClass = new Portuguese; + else if (m_langID == "ro") + m_translationClass = new Romanian; + else if (m_langID == "ru") + m_translationClass = new Russian; + else if (m_langID == "es") + m_translationClass = new Spanish; + else if (m_langID == "sv") + m_translationClass = new Swedish; + else if (m_langID == "uk") + m_translationClass = new Ukrainian; + else // default + m_translationClass = new English; +} + +//---------------------------------------------------------------------------- +// Translation base class methods. +//---------------------------------------------------------------------------- + +Translation::Translation() +{ + m_translationVector.reserve(translationElements); +} + +void Translation::addPair(const std::string& english, const std::wstring& translated) +// Add a string pair to the translation vector. +{ + std::pair entry(english, translated); + m_translationVector.emplace_back(entry); + assert(m_translationVector.size() <= translationElements); +} + +std::string Translation::convertToMultiByte(const std::wstring& wideStr) const +// Convert wchar_t to a multibyte string using the currently assigned locale. +// Return an empty string if an error occurs. +{ + static bool msgDisplayed = false; + // get length of the output excluding the nullptr and validate the parameters + size_t mbLen = wcstombs(nullptr, wideStr.c_str(), 0); + if (mbLen == std::string::npos) + { + if (!msgDisplayed) + { + fprintf(stderr, "\n%s\n\n", "Cannot convert to multi-byte string, reverting to English"); + msgDisplayed = true; + } + return ""; + } + // convert the characters + char* mbStr = new (std::nothrow) char[mbLen + 1]; + if (mbStr == nullptr) + { + if (!msgDisplayed) + { + fprintf(stderr, "\n%s\n\n", "Bad memory alloc for multi-byte string, reverting to English"); + msgDisplayed = true; + } + return ""; + } + wcstombs(mbStr, wideStr.c_str(), mbLen + 1); + // return the string + std::string mbTranslation = mbStr; + delete[] mbStr; + return mbTranslation; +} + +std::string Translation::getTranslationString(size_t i) const +// Return the translation ascii value. Used for testing. +{ + if (i >= m_translationVector.size()) + return std::string(); + return m_translationVector[i].first; +} + +size_t Translation::getTranslationVectorSize() const +// Return the translation vector size. Used for testing. +{ + return m_translationVector.size(); +} + +bool Translation::getWideTranslation(const std::string& stringIn, std::wstring& wideOut) const +// Get the wide translation string. Used for testing. +{ + for (const std::pair& translation : m_translationVector) + { + if (translation.first == stringIn) + { + wideOut = translation.second; + return true; + } + } + // not found + wideOut = L""; + return false; +} + +std::string& Translation::translate(const std::string& stringIn) const +// Translate a string. +// Return a mutable string so the method can have a "const" designation. +// This allows "settext" to be called from a "const" method. +{ + m_mbTranslation.clear(); + for (const std::pair& translation : m_translationVector) + { + if (translation.first == stringIn) + { + m_mbTranslation = convertToMultiByte(translation.second); + break; + } + } + // not found, return english + if (m_mbTranslation.empty()) + m_mbTranslation = stringIn; + return m_mbTranslation; +} + +//---------------------------------------------------------------------------- +// Translation class methods. +// These classes have only a constructor which builds the language vector. +//---------------------------------------------------------------------------- + +Bulgarian::Bulgarian() // български +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Форматиран %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Непроменен %s\n"); // should align with formatted + addPair("Directory %s\n", L"директория %s\n"); + addPair("Default option file %s\n", L"Файл с опции по подразбиране %s\n"); + addPair("Project option file %s\n", L"Файл с опции за проекта %s\n"); + addPair("Exclude %s\n", L"Изключвам %s\n"); + addPair("Exclude (unmatched) %s\n", L"Изключване (несравнимо) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s форматиран %s hепроменен "); + addPair(" seconds ", L" секунди "); + addPair("%d min %d sec ", L"%d мин %d сек "); + addPair("%s lines\n", L"%s линии\n"); + addPair("Opening HTML documentation %s\n", L"Откриване HTML документация %s\n"); + addPair("Invalid default options:", L"Невалидни опции по подразбиране:"); + addPair("Invalid project options:", L"Невалидни опции за проекти:"); + addPair("Invalid command line options:", L"Невалидни опции за командния ред:"); + addPair("For help on options type 'astyle -h'", L"За помощ относно възможностите тип 'astyle -h'"); + addPair("Cannot open default option file", L"Не може да се отвори файлът с опции по подразбиране"); + addPair("Cannot open project option file", L"Не може да се отвори файла с опции за проекта"); + addPair("Cannot open directory", L"Не може да се отвори директория"); + addPair("Cannot open HTML file %s\n", L"Не може да се отвори HTML файл %s\n"); + addPair("Command execute failure", L"Command изпълни недостатъчност"); + addPair("Command is not installed", L"Command не е инсталиран"); + addPair("Missing filename in %s\n", L"Липсва името на файла в %s\n"); + addPair("Recursive option with no wildcard", L"Рекурсивно опция, без маска"); + addPair("Did you intend quote the filename", L"Знаете ли намерение да цитирам името на файла"); + addPair("No file to process %s\n", L"Не файл за обработка %s\n"); + addPair("Did you intend to use --recursive", L"Знаете ли възнамерявате да използвате --recursive"); + addPair("Cannot process UTF-32 encoding", L"Не може да са UTF-32 кодиране"); + addPair("Artistic Style has terminated\n", L"Artistic Style е прекратено\n"); +} + +ChineseSimplified::ChineseSimplified() // 中文(简体) +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"格式化 %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"未改变 %s\n"); // should align with formatted + addPair("Directory %s\n", L"目录 %s\n"); + addPair("Default option file %s\n", L"默认选项文件 %s\n"); + addPair("Project option file %s\n", L"项目选项文件 %s\n"); + addPair("Exclude %s\n", L"排除 %s\n"); + addPair("Exclude (unmatched) %s\n", L"排除(无匹配项) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s 格式化 %s 未改变 "); + addPair(" seconds ", L" 秒 "); + addPair("%d min %d sec ", L"%d 分 %d 秒 "); + addPair("%s lines\n", L"%s 行\n"); + addPair("Opening HTML documentation %s\n", L"打开HTML文档 %s\n"); + addPair("Invalid default options:", L"默认选项无效:"); + addPair("Invalid project options:", L"项目选项无效:"); + addPair("Invalid command line options:", L"无效的命令行选项:"); + addPair("For help on options type 'astyle -h'", L"输入 'astyle -h' 以获得有关命令行的帮助"); + addPair("Cannot open default option file", L"无法打开默认选项文件"); + addPair("Cannot open project option file", L"无法打开项目选项文件"); + addPair("Cannot open directory", L"无法打开目录"); + addPair("Cannot open HTML file %s\n", L"无法打开HTML文件 %s\n"); + addPair("Command execute failure", L"执行命令失败"); + addPair("Command is not installed", L"未安装命令"); + addPair("Missing filename in %s\n", L"在%s缺少文件名\n"); + addPair("Recursive option with no wildcard", L"递归选项没有通配符"); + addPair("Did you intend quote the filename", L"你打算引用文件名"); + addPair("No file to process %s\n", L"没有文件可处理 %s\n"); + addPair("Did you intend to use --recursive", L"你打算使用 --recursive"); + addPair("Cannot process UTF-32 encoding", L"不能处理UTF-32编码"); + addPair("Artistic Style has terminated\n", L"Artistic Style 已经终止运行\n"); +} + +ChineseTraditional::ChineseTraditional() // 中文(繁體) +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"格式化 %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"未改變 %s\n"); // should align with formatted + addPair("Directory %s\n", L"目錄 %s\n"); + addPair("Default option file %s\n", L"默認選項文件 %s\n"); + addPair("Project option file %s\n", L"項目選項文件 %s\n"); + addPair("Exclude %s\n", L"排除 %s\n"); + addPair("Exclude (unmatched) %s\n", L"排除(無匹配項) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s 格式化 %s 未改變 "); + addPair(" seconds ", L" 秒 "); + addPair("%d min %d sec ", L"%d 分 %d 秒 "); + addPair("%s lines\n", L"%s 行\n"); + addPair("Opening HTML documentation %s\n", L"打開HTML文檔 %s\n"); + addPair("Invalid default options:", L"默認選項無效:"); + addPair("Invalid project options:", L"項目選項無效:"); + addPair("Invalid command line options:", L"無效的命令行選項:"); + addPair("For help on options type 'astyle -h'", L"輸入'astyle -h'以獲得有關命令行的幫助:"); + addPair("Cannot open default option file", L"無法打開默認選項文件"); + addPair("Cannot open project option file", L"無法打開項目選項文件"); + addPair("Cannot open directory", L"無法打開目錄"); + addPair("Cannot open HTML file %s\n", L"無法打開HTML文件 %s\n"); + addPair("Command execute failure", L"執行命令失敗"); + addPair("Command is not installed", L"未安裝命令"); + addPair("Missing filename in %s\n", L"在%s缺少文件名\n"); + addPair("Recursive option with no wildcard", L"遞歸選項沒有通配符"); + addPair("Did you intend quote the filename", L"你打算引用文件名"); + addPair("No file to process %s\n", L"沒有文件可處理 %s\n"); + addPair("Did you intend to use --recursive", L"你打算使用 --recursive"); + addPair("Cannot process UTF-32 encoding", L"不能處理UTF-32編碼"); + addPair("Artistic Style has terminated\n", L"Artistic Style 已經終止運行\n"); +} + +Dutch::Dutch() // Nederlandse +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Geformatteerd %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Onveranderd %s\n"); // should align with formatted + addPair("Directory %s\n", L"Directory %s\n"); + addPair("Default option file %s\n", L"Standaard optie bestand %s\n"); + addPair("Project option file %s\n", L"Project optie bestand %s\n"); + addPair("Exclude %s\n", L"Uitsluiten %s\n"); + addPair("Exclude (unmatched) %s\n", L"Uitgesloten (ongeëvenaarde) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s geformatteerd %s onveranderd "); + addPair(" seconds ", L" seconden "); + addPair("%d min %d sec ", L"%d min %d sec "); + addPair("%s lines\n", L"%s lijnen\n"); + addPair("Opening HTML documentation %s\n", L"Het openen van HTML-documentatie %s\n"); + addPair("Invalid default options:", L"Ongeldige standaardopties:"); + addPair("Invalid project options:", L"Ongeldige projectopties:"); + addPair("Invalid command line options:", L"Ongeldige command line opties:"); + addPair("For help on options type 'astyle -h'", L"Voor hulp bij 'astyle-h' opties het type"); + addPair("Cannot open default option file", L"Kan het standaardoptiesbestand niet openen"); + addPair("Cannot open project option file", L"Kan het project optie bestand niet openen"); + addPair("Cannot open directory", L"Kan niet open directory"); + addPair("Cannot open HTML file %s\n", L"Kan HTML-bestand niet openen %s\n"); + addPair("Command execute failure", L"Voeren commando falen"); + addPair("Command is not installed", L"Command is niet geïnstalleerd"); + addPair("Missing filename in %s\n", L"Ontbrekende bestandsnaam in %s\n"); + addPair("Recursive option with no wildcard", L"Recursieve optie met geen wildcard"); + addPair("Did you intend quote the filename", L"Heeft u van plan citaat van de bestandsnaam"); + addPair("No file to process %s\n", L"Geen bestand te verwerken %s\n"); + addPair("Did you intend to use --recursive", L"Hebt u van plan bent te gebruiken --recursive"); + addPair("Cannot process UTF-32 encoding", L"Kan niet verwerken UTF-32 codering"); + addPair("Artistic Style has terminated\n", L"Artistic Style heeft beëindigd\n"); +} + +English::English() = default; +// this class is NOT translated + +Estonian::Estonian() // Eesti +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formaadis %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Muutumatu %s\n"); // should align with formatted + addPair("Directory %s\n", L"Kataloog %s\n"); + addPair("Default option file %s\n", L"Vaikefunktsioonifail %s\n"); + addPair("Project option file %s\n", L"Projekti valiku fail %s\n"); + addPair("Exclude %s\n", L"Välista %s\n"); + addPair("Exclude (unmatched) %s\n", L"Välista (tasakaalustamata) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formaadis %s muutumatu "); + addPair(" seconds ", L" sekundit "); + addPair("%d min %d sec ", L"%d min %d sek "); + addPair("%s lines\n", L"%s read\n"); + addPair("Opening HTML documentation %s\n", L"Avamine HTML dokumentatsioon %s\n"); + addPair("Invalid default options:", L"Vaikevalikud on sobimatud:"); + addPair("Invalid project options:", L"Projekti valikud on sobimatud:"); + addPair("Invalid command line options:", L"Vale käsureavõtmetega:"); + addPair("For help on options type 'astyle -h'", L"Abiks võimaluste tüüp 'astyle -h'"); + addPair("Cannot open default option file", L"Vaikimisi valitud faili ei saa avada"); + addPair("Cannot open project option file", L"Projektivaliku faili ei saa avada"); + addPair("Cannot open directory", L"Ei saa avada kataloogi"); + addPair("Cannot open HTML file %s\n", L"Ei saa avada HTML-faili %s\n"); + addPair("Command execute failure", L"Käsk täita rike"); + addPair("Command is not installed", L"Käsk ei ole paigaldatud"); + addPair("Missing filename in %s\n", L"Kadunud failinimi %s\n"); + addPair("Recursive option with no wildcard", L"Rekursiivne võimalus ilma metamärgi"); + addPair("Did you intend quote the filename", L"Kas te kavatsete tsiteerida failinimi"); + addPair("No file to process %s\n", L"No faili töötlema %s\n"); + addPair("Did you intend to use --recursive", L"Kas te kavatsete kasutada --recursive"); + addPair("Cannot process UTF-32 encoding", L"Ei saa töödelda UTF-32 kodeeringus"); + addPair("Artistic Style has terminated\n", L"Artistic Style on lõpetatud\n"); +} + +Finnish::Finnish() // Suomeksi +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Muotoiltu %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Ennallaan %s\n"); // should align with formatted + addPair("Directory %s\n", L"Directory %s\n"); + addPair("Default option file %s\n", L"Oletusasetustiedosto %s\n"); + addPair("Project option file %s\n", L"Projektin valintatiedosto %s\n"); + addPair("Exclude %s\n", L"Sulkea %s\n"); + addPair("Exclude (unmatched) %s\n", L"Sulkea (verraton) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s muotoiltu %s ennallaan "); + addPair(" seconds ", L" sekuntia "); + addPair("%d min %d sec ", L"%d min %d sek "); + addPair("%s lines\n", L"%s linjat\n"); + addPair("Opening HTML documentation %s\n", L"Avaaminen HTML asiakirjat %s\n"); + addPair("Invalid default options:", L"Virheelliset oletusasetukset:"); + addPair("Invalid project options:", L"Virheelliset hankevalinnat:"); + addPair("Invalid command line options:", L"Virheellinen komentorivin:"); + addPair("For help on options type 'astyle -h'", L"Apua vaihtoehdoista tyyppi 'astyle -h'"); + addPair("Cannot open default option file", L"Et voi avata oletusasetustiedostoa"); + addPair("Cannot open project option file", L"Projektin asetustiedostoa ei voi avata"); + addPair("Cannot open directory", L"Ei Open Directory"); + addPair("Cannot open HTML file %s\n", L"Ei voi avata HTML-tiedoston %s\n"); + addPair("Command execute failure", L"Suorita komento vika"); + addPair("Command is not installed", L"Komento ei ole asennettu"); + addPair("Missing filename in %s\n", L"Puuttuvat tiedostonimi %s\n"); + addPair("Recursive option with no wildcard", L"Rekursiivinen vaihtoehto ilman wildcard"); + addPair("Did you intend quote the filename", L"Oletko aio lainata tiedostonimi"); + addPair("No file to process %s\n", L"Ei tiedostoa käsitellä %s\n"); + addPair("Did you intend to use --recursive", L"Oliko aiot käyttää --recursive"); + addPair("Cannot process UTF-32 encoding", L"Ei voi käsitellä UTF-32 koodausta"); + addPair("Artistic Style has terminated\n", L"Artistic Style on päättynyt\n"); +} + +French::French() // Française +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formaté %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Inchangée %s\n"); // should align with formatted + addPair("Directory %s\n", L"Répertoire %s\n"); + addPair("Default option file %s\n", L"Fichier d'option par défaut %s\n"); + addPair("Project option file %s\n", L"Fichier d'option de projet %s\n"); + addPair("Exclude %s\n", L"Exclure %s\n"); + addPair("Exclude (unmatched) %s\n", L"Exclure (non appariés) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formaté %s inchangée "); + addPair(" seconds ", L" seconde "); + addPair("%d min %d sec ", L"%d min %d sec "); + addPair("%s lines\n", L"%s lignes\n"); + addPair("Opening HTML documentation %s\n", L"Ouverture documentation HTML %s\n"); + addPair("Invalid default options:", L"Options par défaut invalides:"); + addPair("Invalid project options:", L"Options de projet non valides:"); + addPair("Invalid command line options:", L"Blancs options ligne de commande:"); + addPair("For help on options type 'astyle -h'", L"Pour de l'aide sur les options tapez 'astyle -h'"); + addPair("Cannot open default option file", L"Impossible d'ouvrir le fichier d'option par défaut"); + addPair("Cannot open project option file", L"Impossible d'ouvrir le fichier d'option de projet"); + addPair("Cannot open directory", L"Impossible d'ouvrir le répertoire"); + addPair("Cannot open HTML file %s\n", L"Impossible d'ouvrir le fichier HTML %s\n"); + addPair("Command execute failure", L"Exécuter échec de la commande"); + addPair("Command is not installed", L"Commande n'est pas installé"); + addPair("Missing filename in %s\n", L"Nom de fichier manquant dans %s\n"); + addPair("Recursive option with no wildcard", L"Option récursive sans joker"); + addPair("Did you intend quote the filename", L"Avez-vous l'intention de citer le nom de fichier"); + addPair("No file to process %s\n", L"Aucun fichier à traiter %s\n"); + addPair("Did you intend to use --recursive", L"Avez-vous l'intention d'utiliser --recursive"); + addPair("Cannot process UTF-32 encoding", L"Impossible de traiter codage UTF-32"); + addPair("Artistic Style has terminated\n", L"Artistic Style a mis fin\n"); +} + +German::German() // Deutsch +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formatiert %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Unverändert %s\n"); // should align with formatted + addPair("Directory %s\n", L"Verzeichnis %s\n"); + addPair("Default option file %s\n", L"Standard-Optionsdatei %s\n"); + addPair("Project option file %s\n", L"Projekt-Optionsdatei %s\n"); + addPair("Exclude %s\n", L"Ausschließen %s\n"); + addPair("Exclude (unmatched) %s\n", L"Ausschließen (keine Übereinstimmung) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formatiert %s unverändert "); + addPair(" seconds ", L" Sekunden "); + addPair("%d min %d sec ", L"%d min %d sek "); + addPair("%s lines\n", L"%s Zeilen\n"); + addPair("Opening HTML documentation %s\n", L"Öffne HTML-Dokumentation %s\n"); + addPair("Invalid default options:", L"Ungültige Standardoptionen:"); + addPair("Invalid project options:", L"Ungültige Projektoptionen:"); + addPair("Invalid command line options:", L"Ungültige Kommandozeilen-Optionen:"); + addPair("For help on options type 'astyle -h'", L"Für Hilfe zu den Optionen geben Sie 'astyle -h' ein"); + addPair("Cannot open default option file", L"Die Standardoptionsdatei kann nicht geöffnet werden"); + addPair("Cannot open project option file", L"Die Projektoptionsdatei kann nicht geöffnet werden"); + addPair("Cannot open directory", L"Das Verzeichnis kann nicht geöffnet werden"); + addPair("Cannot open HTML file %s\n", L"Kann HTML-Datei nicht öffnen %s\n"); + addPair("Command execute failure", L"Ausführung gescheitert"); + addPair("Command is not installed", L"Kommando ist nicht installiert"); + addPair("Missing filename in %s\n", L"Fehlender Dateiname in %s\n"); + addPair("Recursive option with no wildcard", L"Rekursive Option ohne Wildcard"); + addPair("Did you intend quote the filename", L"Haben Sie die Absicht Inhalte der Dateiname"); + addPair("No file to process %s\n", L"Keine Datei zu verarbeiten %s\n"); + addPair("Did you intend to use --recursive", L"Wollten Sie --recursive verwenden"); + addPair("Cannot process UTF-32 encoding", L"Kann UTF-32 Codierung nicht verarbeiten"); + addPair("Artistic Style has terminated\n", L"Artistic Style wurde beendet\n"); +} + +Greek::Greek() // ελληνικά +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Διαμορφωμένη %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Αμετάβλητος %s\n"); // should align with formatted + addPair("Directory %s\n", L"Κατάλογος %s\n"); + addPair("Default option file %s\n", L"Προεπιλεγμένο αρχείο επιλογών %s\n"); + addPair("Project option file %s\n", L"Αρχείο επιλογής έργου %s\n"); + addPair("Exclude %s\n", L"Αποκλείω %s\n"); + addPair("Exclude (unmatched) %s\n", L"Exclude (unmatched) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s σχηματοποιημένη %s αμετάβλητες "); + addPair(" seconds ", L" δευτερόλεπτα "); + addPair("%d min %d sec ", L"%d λεπ %d δευ "); + addPair("%s lines\n", L"%s γραμμές\n"); + addPair("Opening HTML documentation %s\n", L"Εγκαίνια έγγραφα HTML %s\n"); + addPair("Invalid default options:", L"Μη έγκυρες επιλογές προεπιλογής:"); + addPair("Invalid project options:", L"Μη έγκυρες επιλογές έργου:"); + addPair("Invalid command line options:", L"Μη έγκυρη επιλογές γραμμής εντολών:"); + addPair("For help on options type 'astyle -h'", L"Για βοήθεια σχετικά με το είδος επιλογές 'astyle -h'"); + addPair("Cannot open default option file", L"Δεν είναι δυνατό να ανοίξει το προεπιλεγμένο αρχείο επιλογών"); + addPair("Cannot open project option file", L"Δεν είναι δυνατό να ανοίξει το αρχείο επιλογής έργου"); + addPair("Cannot open directory", L"Δεν μπορείτε να ανοίξετε τον κατάλογο"); + addPair("Cannot open HTML file %s\n", L"Δεν μπορείτε να ανοίξετε το αρχείο HTML %s\n"); + addPair("Command execute failure", L"Εντολή να εκτελέσει την αποτυχία"); + addPair("Command is not installed", L"Η εντολή δεν έχει εγκατασταθεί"); + addPair("Missing filename in %s\n", L"Λείπει το όνομα αρχείου σε %s\n"); + addPair("Recursive option with no wildcard", L"Αναδρομικές επιλογή χωρίς μπαλαντέρ"); + addPair("Did you intend quote the filename", L"Μήπως σκοπεύετε να αναφέρετε το όνομα του αρχείου"); + addPair("No file to process %s\n", L"Δεν υπάρχει αρχείο για την επεξεργασία %s\n"); + addPair("Did you intend to use --recursive", L"Μήπως σκοπεύετε να χρησιμοποιήσετε --recursive"); + addPair("Cannot process UTF-32 encoding", L"δεν μπορεί να επεξεργαστεί UTF-32 κωδικοποίηση"); + addPair("Artistic Style has terminated\n", L"Artistic Style έχει λήξει\n"); +} + +Hindi::Hindi() // हिन्दी +// build the translation vector in the Translation base class +{ + // NOTE: Scintilla based editors (CodeBlocks) cannot always edit Hindi. + // Use Visual Studio instead. + addPair("Formatted %s\n", L"स्वरूपित किया %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"अपरिवर्तित %s\n"); // should align with formatted + addPair("Directory %s\n", L"निर्देशिका %s\n"); + addPair("Default option file %s\n", L"डिफ़ॉल्ट विकल्प फ़ाइल %s\n"); + addPair("Project option file %s\n", L"प्रोजेक्ट विकल्प फ़ाइल %s\n"); + addPair("Exclude %s\n", L"निकालना %s\n"); + addPair("Exclude (unmatched) %s\n", L"अपवर्जित (बेजोड़) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s स्वरूपित किया %s अपरिवर्तित "); + addPair(" seconds ", L" सेकंड "); + addPair("%d min %d sec ", L"%d मिनट %d सेकंड "); + addPair("%s lines\n", L"%s लाइनों\n"); + addPair("Opening HTML documentation %s\n", L"एचटीएमएल प्रलेखन खोलना %s\n"); + addPair("Invalid default options:", L"अमान्य डिफ़ॉल्ट विकल्प:"); + addPair("Invalid project options:", L"अमान्य प्रोजेक्ट विकल्प:"); + addPair("Invalid command line options:", L"कमांड लाइन विकल्प अवैध:"); + addPair("For help on options type 'astyle -h'", L"विकल्पों पर मदद के लिए प्रकार 'astyle -h'"); + addPair("Cannot open default option file", L"डिफ़ॉल्ट विकल्प फ़ाइल नहीं खोल सकता"); + addPair("Cannot open project option file", L"परियोजना विकल्प फ़ाइल नहीं खोल सकता"); + addPair("Cannot open directory", L"निर्देशिका नहीं खोल सकता"); + addPair("Cannot open HTML file %s\n", L"HTML फ़ाइल नहीं खोल सकता %s\n"); + addPair("Command execute failure", L"आदेश विफलता निष्पादित"); + addPair("Command is not installed", L"कमान स्थापित नहीं है"); + addPair("Missing filename in %s\n", L"लापता में फ़ाइलनाम %s\n"); + addPair("Recursive option with no wildcard", L"कोई वाइल्डकार्ड साथ पुनरावर्ती विकल्प"); + addPair("Did you intend quote the filename", L"क्या आप बोली फ़ाइलनाम का इरादा"); + addPair("No file to process %s\n", L"कोई फ़ाइल %s प्रक्रिया के लिए\n"); + addPair("Did you intend to use --recursive", L"क्या आप उपयोग करना चाहते हैं --recursive"); + addPair("Cannot process UTF-32 encoding", L"UTF-32 कूटबन्धन प्रक्रिया नहीं कर सकते"); + addPair("Artistic Style has terminated\n", L"Artistic Style समाप्त किया है\n"); +} + +Hungarian::Hungarian() // Magyar +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formázott %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Változatlan %s\n"); // should align with formatted + addPair("Directory %s\n", L"Címjegyzék %s\n"); + addPair("Default option file %s\n", L"Alapértelmezett beállítási fájl %s\n"); + addPair("Project option file %s\n", L"Projekt opciófájl %s\n"); + addPair("Exclude %s\n", L"Kizár %s\n"); + addPair("Exclude (unmatched) %s\n", L"Escludere (senza pari) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formázott %s változatlan "); + addPair(" seconds ", L" másodperc "); + addPair("%d min %d sec ", L"%d jeg %d más "); + addPair("%s lines\n", L"%s vonalak\n"); + addPair("Opening HTML documentation %s\n", L"Nyitó HTML dokumentáció %s\n"); + addPair("Invalid default options:", L"Érvénytelen alapértelmezett beállítások:"); + addPair("Invalid project options:", L"Érvénytelen projektbeállítások:"); + addPair("Invalid command line options:", L"Érvénytelen parancssori opciók:"); + addPair("For help on options type 'astyle -h'", L"Ha segítségre van lehetőség típus 'astyle-h'"); + addPair("Cannot open default option file", L"Nem lehet megnyitni az alapértelmezett beállítási fájlt"); + addPair("Cannot open project option file", L"Nem lehet megnyitni a projekt opció fájlt"); + addPair("Cannot open directory", L"Nem lehet megnyitni könyvtár"); + addPair("Cannot open HTML file %s\n", L"Nem lehet megnyitni a HTML fájlt %s\n"); + addPair("Command execute failure", L"Command végre hiba"); + addPair("Command is not installed", L"Parancs nincs telepítve"); + addPair("Missing filename in %s\n", L"Hiányzó fájlnév %s\n"); + addPair("Recursive option with no wildcard", L"Rekurzív kapcsolót nem wildcard"); + addPair("Did you intend quote the filename", L"Esetleg kívánja idézni a fájlnév"); + addPair("No file to process %s\n", L"Nincs fájl feldolgozása %s\n"); + addPair("Did you intend to use --recursive", L"Esetleg a használni kívánt --recursive"); + addPair("Cannot process UTF-32 encoding", L"Nem tudja feldolgozni UTF-32 kódolással"); + addPair("Artistic Style has terminated\n", L"Artistic Style megszűnt\n"); +} + +Italian::Italian() // Italiano +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formattato %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Immutato %s\n"); // should align with formatted + addPair("Directory %s\n", L"Directory %s\n"); + addPair("Default option file %s\n", L"File di configurazione predefinito %s\n"); + addPair("Project option file %s\n", L"File di configurazione del progetto %s\n"); + addPair("Exclude %s\n", L"Escluso %s\n"); + addPair("Exclude (unmatched) %s\n", L"Escluso (senza pari) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formattato %s immutato "); + addPair(" seconds ", L" secondi "); + addPair("%d min %d sec ", L"%d min %d sec "); + addPair("%s lines\n", L"%s linee\n"); + addPair("Opening HTML documentation %s\n", L"Apertura di documenti HTML %s\n"); + addPair("Invalid default options:", L"Opzioni di default non valide:"); + addPair("Invalid project options:", L"Opzioni di progetto non valide:"); + addPair("Invalid command line options:", L"Opzioni della riga di comando non valide:"); + addPair("For help on options type 'astyle -h'", L"Per informazioni sulle opzioni di tipo 'astyle -h'"); + addPair("Cannot open default option file", L"Impossibile aprire il file di configurazione predefinito"); + addPair("Cannot open project option file", L"Impossibile aprire il file di configurazione del progetto"); + addPair("Cannot open directory", L"Impossibile aprire la directory"); + addPair("Cannot open HTML file %s\n", L"Impossibile aprire il file HTML %s\n"); + addPair("Command execute failure", L"Esecuzione del comando fallita"); + addPair("Command is not installed", L"Il comando non è installato"); + addPair("Missing filename in %s\n", L"Nome del file mancante in %s\n"); + addPair("Recursive option with no wildcard", L"Opzione ricorsiva senza jolly"); + addPair("Did you intend quote the filename", L"Avete intenzione citare il nome del file"); + addPair("No file to process %s\n", L"Nessun file da processare %s\n"); + addPair("Did you intend to use --recursive", L"Hai intenzione di utilizzare --recursive"); + addPair("Cannot process UTF-32 encoding", L"Non è possibile processare codifica UTF-32"); + addPair("Artistic Style has terminated\n", L"Artistic Style è terminato\n"); +} + +Japanese::Japanese() // 日本語 +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"フォーマット済みの %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"変わりません %s\n"); // should align with formatted + addPair("Directory %s\n", L"ディレクトリ %s\n"); + addPair("Default option file %s\n", L"デフォルトオプションファイル %s\n"); + addPair("Project option file %s\n", L"プロジェクトオプションファイル %s\n"); + addPair("Exclude %s\n", L"除外する %s\n"); + addPair("Exclude (unmatched) %s\n", L"除外する(一致しません) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s フフォーマット済みの %s 変わりません "); + addPair(" seconds ", L" 秒 "); + addPair("%d min %d sec ", L"%d 分 %d 秒 "); + addPair("%s lines\n", L"%s ライン\n"); + addPair("Opening HTML documentation %s\n", L"オープニングHTMLドキュメント %s\n"); + addPair("Invalid default options:", L"無効なデフォルトオプション:"); + addPair("Invalid project options:", L"無効なプロジェクトオプション:"); + addPair("Invalid command line options:", L"無効なコマンドラインオプション:"); + addPair("For help on options type 'astyle -h'", L"コオプションの種類のヘルプについて'astyle- h'を入力してください"); + addPair("Cannot open default option file", L"デフォルトのオプションファイルを開くことができません"); + addPair("Cannot open project option file", L"プロジェクトオプションファイルを開くことができません"); + addPair("Cannot open directory", L"ディレクトリを開くことができません。"); + addPair("Cannot open HTML file %s\n", L"HTMLファイルを開くことができません %s\n"); + addPair("Command execute failure", L"コマンドが失敗を実行します"); + addPair("Command is not installed", L"コマンドがインストールされていません"); + addPair("Missing filename in %s\n", L"%s で、ファイル名がありません\n"); + addPair("Recursive option with no wildcard", L"無ワイルドカードを使用して再帰的なオプション"); + addPair("Did you intend quote the filename", L"あなたはファイル名を引用するつもりでした"); + addPair("No file to process %s\n", L"いいえファイルは処理しないように %s\n"); + addPair("Did you intend to use --recursive", L"あなたは--recursive使用するつもりでした"); + addPair("Cannot process UTF-32 encoding", L"UTF - 32エンコーディングを処理できません"); + addPair("Artistic Style has terminated\n", L"Artistic Style 終了しました\n"); +} + +Korean::Korean() // 한국의 +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"수정됨 %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"변경없음 %s\n"); // should align with formatted + addPair("Directory %s\n", L"디렉토리 %s\n"); + addPair("Default option file %s\n", L"기본 옵션 파일 %s\n"); + addPair("Project option file %s\n", L"프로젝트 옵션 파일 %s\n"); + addPair("Exclude %s\n", L"제외됨 %s\n"); + addPair("Exclude (unmatched) %s\n", L"제외 (NO 일치) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s 수정됨 %s 변경없음 "); + addPair(" seconds ", L" 초 "); + addPair("%d min %d sec ", L"%d 분 %d 초 "); + addPair("%s lines\n", L"%s 라인\n"); + addPair("Opening HTML documentation %s\n", L"HTML 문서를 열기 %s\n"); + addPair("Invalid default options:", L"잘못된 기본 옵션:"); + addPair("Invalid project options:", L"잘못된 프로젝트 옵션:"); + addPair("Invalid command line options:", L"잘못된 명령줄 옵션 :"); + addPair("For help on options type 'astyle -h'", L"도움말을 보려면 옵션 유형 'astyle - H'를 사용합니다"); + addPair("Cannot open default option file", L"기본 옵션 파일을 열 수 없습니다."); + addPair("Cannot open project option file", L"프로젝트 옵션 파일을 열 수 없습니다."); + addPair("Cannot open directory", L"디렉토리를 열지 못했습니다"); + addPair("Cannot open HTML file %s\n", L"HTML 파일을 열 수 없습니다 %s\n"); + addPair("Command execute failure", L"명령 실패를 실행"); + addPair("Command is not installed", L"명령이 설치되어 있지 않습니다"); + addPair("Missing filename in %s\n", L"%s 에서 누락된 파일 이름\n"); + addPair("Recursive option with no wildcard", L"와일드 카드없이 재귀 옵션"); + addPair("Did you intend quote the filename", L"당신은 파일 이름을 인용하고자하나요"); + addPair("No file to process %s\n", L"처리할 파일이 없습니다 %s\n"); + addPair("Did you intend to use --recursive", L"--recursive 를 사용하고자 하십니까"); + addPair("Cannot process UTF-32 encoding", L"UTF-32 인코딩을 처리할 수 없습니다"); + addPair("Artistic Style has terminated\n", L"Artistic Style를 종료합니다\n"); +} + +Norwegian::Norwegian() // Norsk +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formatert %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Uendret %s\n"); // should align with formatted + addPair("Directory %s\n", L"Katalog %s\n"); + addPair("Default option file %s\n", L"Standard alternativfil %s\n"); + addPair("Project option file %s\n", L"Prosjekt opsjonsfil %s\n"); + addPair("Exclude %s\n", L"Ekskluder %s\n"); + addPair("Exclude (unmatched) %s\n", L"Ekskluder (uovertruffen) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formatert %s uendret "); + addPair(" seconds ", L" sekunder "); + addPair("%d min %d sec ", L"%d min %d sek? "); + addPair("%s lines\n", L"%s linjer\n"); + addPair("Opening HTML documentation %s\n", L"Åpning HTML dokumentasjon %s\n"); + addPair("Invalid default options:", L"Ugyldige standardalternativer:"); + addPair("Invalid project options:", L"Ugyldige prosjektalternativer:"); + addPair("Invalid command line options:", L"Kommandolinjevalg Ugyldige:"); + addPair("For help on options type 'astyle -h'", L"For hjelp til alternativer type 'astyle -h'"); + addPair("Cannot open default option file", L"Kan ikke åpne standardvalgsfilen"); + addPair("Cannot open project option file", L"Kan ikke åpne prosjektvalgsfilen"); + addPair("Cannot open directory", L"Kan ikke åpne katalog"); + addPair("Cannot open HTML file %s\n", L"Kan ikke åpne HTML-fil %s\n"); + addPair("Command execute failure", L"Command utføre svikt"); + addPair("Command is not installed", L"Command er ikke installert"); + addPair("Missing filename in %s\n", L"Mangler filnavn i %s\n"); + addPair("Recursive option with no wildcard", L"Rekursiv alternativ uten wildcard"); + addPair("Did you intend quote the filename", L"Har du tenkt sitere filnavnet"); + addPair("No file to process %s\n", L"Ingen fil å behandle %s\n"); + addPair("Did you intend to use --recursive", L"Har du tenkt å bruke --recursive"); + addPair("Cannot process UTF-32 encoding", L"Kan ikke behandle UTF-32 koding"); + addPair("Artistic Style has terminated\n", L"Artistic Style har avsluttet\n"); +} + +Polish::Polish() // Polski +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Sformatowany %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Niezmienione %s\n"); // should align with formatted + addPair("Directory %s\n", L"Katalog %s\n"); + addPair("Default option file %s\n", L"Domyślny plik opcji %s\n"); + addPair("Project option file %s\n", L"Plik opcji projektu %s\n"); + addPair("Exclude %s\n", L"Wykluczać %s\n"); + addPair("Exclude (unmatched) %s\n", L"Wyklucz (niezrównany) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s sformatowany %s niezmienione "); + addPair(" seconds ", L" sekund "); + addPair("%d min %d sec ", L"%d min %d sek "); + addPair("%s lines\n", L"%s linii\n"); + addPair("Opening HTML documentation %s\n", L"Otwarcie dokumentacji HTML %s\n"); + addPair("Invalid default options:", L"Nieprawidłowe opcje domyślne:"); + addPair("Invalid project options:", L"Nieprawidłowe opcje projektu:"); + addPair("Invalid command line options:", L"Nieprawidłowe opcje wiersza polecenia:"); + addPair("For help on options type 'astyle -h'", L"Aby uzyskać pomoc od rodzaju opcji 'astyle -h'"); + addPair("Cannot open default option file", L"Nie można otworzyć pliku opcji domyślnych"); + addPair("Cannot open project option file", L"Nie można otworzyć pliku opcji projektu"); + addPair("Cannot open directory", L"Nie można otworzyć katalogu"); + addPair("Cannot open HTML file %s\n", L"Nie można otworzyć pliku HTML %s\n"); + addPair("Command execute failure", L"Wykonaj polecenia niepowodzenia"); + addPair("Command is not installed", L"Polecenie nie jest zainstalowany"); + addPair("Missing filename in %s\n", L"Brakuje pliku w %s\n"); + addPair("Recursive option with no wildcard", L"Rekurencyjne opcja bez symboli"); + addPair("Did you intend quote the filename", L"Czy zamierza Pan podać nazwę pliku"); + addPair("No file to process %s\n", L"Brak pliku do procesu %s\n"); + addPair("Did you intend to use --recursive", L"Czy masz zamiar używać --recursive"); + addPair("Cannot process UTF-32 encoding", L"Nie można procesu kodowania UTF-32"); + addPair("Artistic Style has terminated\n", L"Artistic Style został zakończony\n"); +} + +Portuguese::Portuguese() // Português +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formatado %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Inalterado %s\n"); // should align with formatted + addPair("Directory %s\n", L"Diretório %s\n"); + addPair("Default option file %s\n", L"Arquivo de opção padrão %s\n"); + addPair("Project option file %s\n", L"Arquivo de opção de projeto %s\n"); + addPair("Exclude %s\n", L"Excluir %s\n"); + addPair("Exclude (unmatched) %s\n", L"Excluir (incomparável) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formatado %s inalterado "); + addPair(" seconds ", L" segundo "); + addPair("%d min %d sec ", L"%d min %d seg "); + addPair("%s lines\n", L"%s linhas\n"); + addPair("Opening HTML documentation %s\n", L"Abrindo a documentação HTML %s\n"); + addPair("Invalid default options:", L"Opções padrão inválidas:"); + addPair("Invalid project options:", L"Opções de projeto inválidas:"); + addPair("Invalid command line options:", L"Opções de linha de comando inválida:"); + addPair("For help on options type 'astyle -h'", L"Para obter ajuda sobre as opções de tipo 'astyle -h'"); + addPair("Cannot open default option file", L"Não é possível abrir o arquivo de opção padrão"); + addPair("Cannot open project option file", L"Não é possível abrir o arquivo de opção do projeto"); + addPair("Cannot open directory", L"Não é possível abrir diretório"); + addPair("Cannot open HTML file %s\n", L"Não é possível abrir arquivo HTML %s\n"); + addPair("Command execute failure", L"Executar falha de comando"); + addPair("Command is not installed", L"Comando não está instalado"); + addPair("Missing filename in %s\n", L"Filename faltando em %s\n"); + addPair("Recursive option with no wildcard", L"Opção recursiva sem curinga"); + addPair("Did you intend quote the filename", L"Será que você pretende citar o nome do arquivo"); + addPair("No file to process %s\n", L"Nenhum arquivo para processar %s\n"); + addPair("Did you intend to use --recursive", L"Será que você pretende usar --recursive"); + addPair("Cannot process UTF-32 encoding", L"Não pode processar a codificação UTF-32"); + addPair("Artistic Style has terminated\n", L"Artistic Style terminou\n"); +} + +Romanian::Romanian() // Română +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formatat %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Neschimbat %s\n"); // should align with formatted + addPair("Directory %s\n", L"Director %s\n"); + addPair("Default option file %s\n", L"Fișier opțional implicit %s\n"); + addPair("Project option file %s\n", L"Fișier opțiune proiect %s\n"); + addPair("Exclude %s\n", L"Excludeți %s\n"); + addPair("Exclude (unmatched) %s\n", L"Excludeți (necompensată) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formatat %s neschimbat "); + addPair(" seconds ", L" secunde "); + addPair("%d min %d sec ", L"%d min %d sec "); + addPair("%s lines\n", L"%s linii\n"); + addPair("Opening HTML documentation %s\n", L"Documentație HTML deschidere %s\n"); + addPair("Invalid default options:", L"Opțiuni implicite nevalide:"); + addPair("Invalid project options:", L"Opțiunile de proiect nevalide:"); + addPair("Invalid command line options:", L"Opțiuni de linie de comandă nevalide:"); + addPair("For help on options type 'astyle -h'", L"Pentru ajutor cu privire la tipul de opțiuni 'astyle -h'"); + addPair("Cannot open default option file", L"Nu se poate deschide fișierul cu opțiuni implicite"); + addPair("Cannot open project option file", L"Nu se poate deschide fișierul cu opțiuni de proiect"); + addPair("Cannot open directory", L"Nu se poate deschide directorul"); + addPair("Cannot open HTML file %s\n", L"Nu se poate deschide fișierul HTML %s\n"); + addPair("Command execute failure", L"Comandă executa eșec"); + addPair("Command is not installed", L"Comanda nu este instalat"); + addPair("Missing filename in %s\n", L"Lipsă nume de fișier %s\n"); + addPair("Recursive option with no wildcard", L"Opțiunea recursiv cu nici un wildcard"); + addPair("Did you intend quote the filename", L"V-intentionati cita numele de fișier"); + addPair("No file to process %s\n", L"Nu există un fișier pentru a procesa %s\n"); + addPair("Did you intend to use --recursive", L"V-ați intenționați să utilizați --recursive"); + addPair("Cannot process UTF-32 encoding", L"Nu se poate procesa codificarea UTF-32"); + addPair("Artistic Style has terminated\n", L"Artistic Style a terminat\n"); +} + +Russian::Russian() // русский +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Форматированный %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"без изменений %s\n"); // should align with formatted + addPair("Directory %s\n", L"каталог %s\n"); + addPair("Default option file %s\n", L"Файл с опцией по умолчанию %s\n"); + addPair("Project option file %s\n", L"Файл опций проекта %s\n"); + addPair("Exclude %s\n", L"исключать %s\n"); + addPair("Exclude (unmatched) %s\n", L"Исключить (непревзойденный) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s Форматированный %s без изменений "); + addPair(" seconds ", L" секунды "); + addPair("%d min %d sec ", L"%d мин %d сек "); + addPair("%s lines\n", L"%s линий\n"); + addPair("Opening HTML documentation %s\n", L"Открытие HTML документации %s\n"); + addPair("Invalid default options:", L"Недействительные параметры по умолчанию:"); + addPair("Invalid project options:", L"Недопустимые параметры проекта:"); + addPair("Invalid command line options:", L"Недопустимые параметры командной строки:"); + addPair("For help on options type 'astyle -h'", L"Для получения справки по 'astyle -h' опций типа"); + addPair("Cannot open default option file", L"Не удается открыть файл параметров по умолчанию"); + addPair("Cannot open project option file", L"Не удается открыть файл опций проекта"); + addPair("Cannot open directory", L"Не могу открыть каталог"); + addPair("Cannot open HTML file %s\n", L"Не удается открыть файл HTML %s\n"); + addPair("Command execute failure", L"Выполнить команду недостаточности"); + addPair("Command is not installed", L"Не установлен Команда"); + addPair("Missing filename in %s\n", L"Отсутствует имя файла в %s\n"); + addPair("Recursive option with no wildcard", L"Рекурсивный вариант без каких-либо шаблона"); + addPair("Did you intend quote the filename", L"Вы намерены цитатой файла"); + addPair("No file to process %s\n", L"Нет файлов для обработки %s\n"); + addPair("Did you intend to use --recursive", L"Неужели вы собираетесь использовать --recursive"); + addPair("Cannot process UTF-32 encoding", L"Не удается обработать UTF-32 кодировке"); + addPair("Artistic Style has terminated\n", L"Artistic Style прекратил\n"); +} + +Spanish::Spanish() // Español +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formato %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Inalterado %s\n"); // should align with formatted + addPair("Directory %s\n", L"Directorio %s\n"); + addPair("Default option file %s\n", L"Archivo de opciones predeterminado %s\n"); + addPair("Project option file %s\n", L"Archivo de opciones del proyecto %s\n"); + addPair("Exclude %s\n", L"Excluir %s\n"); + addPair("Exclude (unmatched) %s\n", L"Excluir (incomparable) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formato %s inalterado "); + addPair(" seconds ", L" segundo "); + addPair("%d min %d sec ", L"%d min %d seg "); + addPair("%s lines\n", L"%s líneas\n"); + addPair("Opening HTML documentation %s\n", L"Apertura de documentación HTML %s\n"); + addPair("Invalid default options:", L"Opciones predeterminadas no válidas:"); + addPair("Invalid project options:", L"Opciones de proyecto no válidas:"); + addPair("Invalid command line options:", L"No válido opciones de línea de comando:"); + addPair("For help on options type 'astyle -h'", L"Para obtener ayuda sobre las opciones tipo 'astyle -h'"); + addPair("Cannot open default option file", L"No se puede abrir el archivo de opciones predeterminado"); + addPair("Cannot open project option file", L"No se puede abrir el archivo de opciones del proyecto"); + addPair("Cannot open directory", L"No se puede abrir el directorio"); + addPair("Cannot open HTML file %s\n", L"No se puede abrir el archivo HTML %s\n"); + addPair("Command execute failure", L"Ejecutar el fracaso de comandos"); + addPair("Command is not installed", L"El comando no está instalado"); + addPair("Missing filename in %s\n", L"Falta nombre del archivo en %s\n"); + addPair("Recursive option with no wildcard", L"Recursiva opción sin comodín"); + addPair("Did you intend quote the filename", L"Se tiene la intención de citar el nombre de archivo"); + addPair("No file to process %s\n", L"No existe el fichero a procesar %s\n"); + addPair("Did you intend to use --recursive", L"Se va a utilizar --recursive"); + addPair("Cannot process UTF-32 encoding", L"No se puede procesar la codificación UTF-32"); + addPair("Artistic Style has terminated\n", L"Artistic Style ha terminado\n"); +} + +Swedish::Swedish() // Svenska +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"Formaterade %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"Oförändrade %s\n"); // should align with formatted + addPair("Directory %s\n", L"Katalog %s\n"); + addPair("Default option file %s\n", L"Standardalternativsfil %s\n"); + addPair("Project option file %s\n", L"Projektalternativ fil %s\n"); + addPair("Exclude %s\n", L"Uteslut %s\n"); + addPair("Exclude (unmatched) %s\n", L"Uteslut (oöverträffad) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s formaterade %s oförändrade "); + addPair(" seconds ", L" sekunder "); + addPair("%d min %d sec ", L"%d min %d sek "); + addPair("%s lines\n", L"%s linjer\n"); + addPair("Opening HTML documentation %s\n", L"Öppna HTML-dokumentation %s\n"); + addPair("Invalid default options:", L"Ogiltiga standardalternativ:"); + addPair("Invalid project options:", L"Ogiltiga projektalternativ:"); + addPair("Invalid command line options:", L"Ogiltig kommandoraden alternativ:"); + addPair("For help on options type 'astyle -h'", L"För hjälp om alternativ typ 'astyle -h'"); + addPair("Cannot open default option file", L"Kan inte öppna standardalternativsfilen"); + addPair("Cannot open project option file", L"Kan inte öppna projektalternativsfilen"); + addPair("Cannot open directory", L"Kan inte öppna katalog"); + addPair("Cannot open HTML file %s\n", L"Kan inte öppna HTML-filen %s\n"); + addPair("Command execute failure", L"Utför kommando misslyckande"); + addPair("Command is not installed", L"Kommandot är inte installerat"); + addPair("Missing filename in %s\n", L"Saknade filnamn i %s\n"); + addPair("Recursive option with no wildcard", L"Rekursiva alternativ utan jokertecken"); + addPair("Did you intend quote the filename", L"Visste du tänker citera filnamnet"); + addPair("No file to process %s\n", L"Ingen fil att bearbeta %s\n"); + addPair("Did you intend to use --recursive", L"Har du för avsikt att använda --recursive"); + addPair("Cannot process UTF-32 encoding", L"Kan inte hantera UTF-32 kodning"); + addPair("Artistic Style has terminated\n", L"Artistic Style har upphört\n"); +} + +Ukrainian::Ukrainian() // Український +// build the translation vector in the Translation base class +{ + addPair("Formatted %s\n", L"форматований %s\n"); // should align with unchanged + addPair("Unchanged %s\n", L"без змін %s\n"); // should align with formatted + addPair("Directory %s\n", L"Каталог %s\n"); + addPair("Default option file %s\n", L"Файл параметра за замовчуванням %s\n"); + addPair("Project option file %s\n", L"Файл варіанту проекту %s\n"); + addPair("Exclude %s\n", L"Виключити %s\n"); + addPair("Exclude (unmatched) %s\n", L"Виключити (неперевершений) %s\n"); + addPair(" %s formatted %s unchanged ", L" %s відформатований %s без змін "); + addPair(" seconds ", L" секунди "); + addPair("%d min %d sec ", L"%d хви %d cek "); + addPair("%s lines\n", L"%s ліній\n"); + addPair("Opening HTML documentation %s\n", L"Відкриття HTML документації %s\n"); + addPair("Invalid default options:", L"Недійсні параметри за умовчанням:"); + addPair("Invalid project options:", L"Недійсні параметри проекту:"); + addPair("Invalid command line options:", L"Неприпустима параметри командного рядка:"); + addPair("For help on options type 'astyle -h'", L"Для отримання довідки по 'astyle -h' опцій типу"); + addPair("Cannot open default option file", L"Неможливо відкрити файл параметрів за замовчуванням"); + addPair("Cannot open project option file", L"Неможливо відкрити файл параметрів проекту"); + addPair("Cannot open directory", L"Не можу відкрити каталог"); + addPair("Cannot open HTML file %s\n", L"Не вдається відкрити файл HTML %s\n"); + addPair("Command execute failure", L"Виконати команду недостатності"); + addPair("Command is not installed", L"Не встановлений Команда"); + addPair("Missing filename in %s\n", L"Відсутня назва файлу в %s\n"); + addPair("Recursive option with no wildcard", L"Рекурсивний варіант без будь-яких шаблону"); + addPair("Did you intend quote the filename", L"Ви маєте намір цитатою файлу"); + addPair("No file to process %s\n", L"Немає файлів для обробки %s\n"); + addPair("Did you intend to use --recursive", L"Невже ви збираєтеся використовувати --recursive"); + addPair("Cannot process UTF-32 encoding", L"Не вдається обробити UTF-32 кодуванні"); + addPair("Artistic Style has terminated\n", L"Artistic Style припинив\n"); +} + + +#endif // ASTYLE_LIB + +} // end of namespace astyle diff --git a/astyle/AStyle/src/ASLocalizer.h b/astyle/AStyle/src/ASLocalizer.h new file mode 100644 index 0000000..2ee4b55 --- /dev/null +++ b/astyle/AStyle/src/ASLocalizer.h @@ -0,0 +1,170 @@ +// ASLocalizer.h +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + + +#ifndef ASLOCALIZER_H +#define ASLOCALIZER_H + +#include +#include + +// library builds do not need ASLocalizer +#ifdef ASTYLE_JNI + #ifndef ASTYLE_LIB // ASTYLE_LIB must be defined for ASTYLE_JNI + #define ASTYLE_LIB + #endif +#endif // ASTYLE_JNI + +namespace astyle { + +#ifndef ASTYLE_LIB + +//----------------------------------------------------------------------------- +// ASLocalizer class for console build. +// This class encapsulates all language-dependent settings and is a +// generalization of the C locale concept. +//----------------------------------------------------------------------------- +class Translation; + +class ASLocalizer +{ +public: // functions + ASLocalizer(); + virtual ~ASLocalizer(); + std::string getLanguageID() const; + const Translation* getTranslationClass() const; +#ifdef _WIN32 + void setLanguageFromLCID(size_t lcid); +#endif + void setLanguageFromName(const char* langID); + const char* settext(const char* textIn) const; + +private: // functions + void setTranslationClass(); + +private: // variables + Translation* m_translationClass;// pointer to a polymorphic Translation class + std::string m_langID; // language identifier from the locale + std::string m_subLangID; // sub language identifier, if needed +#ifdef _WIN32 + size_t m_lcid; // LCID of the user locale (Windows only) + size_t m_codepage; // active codepage, 65001 = utf-8 +#endif +}; + +//---------------------------------------------------------------------------- +// Translation base class. +//---------------------------------------------------------------------------- + +class Translation +// This base class is inherited by the language translation classes. +// Polymorphism is used to call the correct language translator. +// This class contains the translation std::vector and settext translation method. +// The language std::vector is built by the language sub classes. +// NOTE: This class must have virtual methods for typeid() to work. +// typeid() is used by AStyleTestI18n_Localizer.cpp. +{ +public: + Translation(); + virtual ~Translation() = default; + std::string convertToMultiByte(const std::wstring& wideStr) const; + std::string getTranslationString(size_t i) const; + size_t getTranslationVectorSize() const; + bool getWideTranslation(const std::string& stringIn, std::wstring& wideOut) const; + std::string& translate(const std::string& stringIn) const; + +protected: + void addPair(const std::string& english, const std::wstring& translated); + // variables + std::vector > m_translationVector; + +private: + // the number of translation pairs added a constructor + static const size_t translationElements = 30; // need static for vs2013 + // the translated std::string + mutable std::string m_mbTranslation; +}; + +//---------------------------------------------------------------------------- +// Translation classes +// One class for each language. +// These classes have only a constructor which builds the language std::vector. +//---------------------------------------------------------------------------- + +class Bulgarian : public Translation +{ public: Bulgarian(); }; + +class ChineseSimplified : public Translation +{ public: ChineseSimplified(); }; + +class ChineseTraditional : public Translation +{ public: ChineseTraditional(); }; + +class Dutch : public Translation +{ public: Dutch(); }; + +class English : public Translation +{ public: English(); }; + +class Estonian : public Translation +{ public: Estonian(); }; + +class Finnish : public Translation +{ public: Finnish(); }; + +class French : public Translation +{ public: French(); }; + +class German : public Translation +{ public: German(); }; + +class Greek : public Translation +{ public: Greek(); }; + +class Hindi : public Translation +{ public: Hindi(); }; + +class Hungarian : public Translation +{ public: Hungarian(); }; + +class Italian : public Translation +{ public: Italian(); }; + +class Japanese : public Translation +{ public: Japanese(); }; + +class Korean : public Translation +{ public: Korean(); }; + +class Norwegian : public Translation +{ public: Norwegian(); }; + +class Polish : public Translation +{ public: Polish(); }; + +class Portuguese : public Translation +{ public: Portuguese(); }; + +class Romanian : public Translation +{ public: Romanian(); }; + +class Russian : public Translation +{ public: Russian(); }; + +class Spanish : public Translation +{ public: Spanish(); }; + +class Swedish : public Translation +{ public: Swedish(); }; + +class Ukrainian : public Translation +{ public: Ukrainian(); }; + + +#endif // ASTYLE_LIB + +} // namespace astyle + +#endif // ASLOCALIZER_H diff --git a/astyle/AStyle/src/ASResource.cpp b/astyle/AStyle/src/ASResource.cpp new file mode 100644 index 0000000..ca526b4 --- /dev/null +++ b/astyle/AStyle/src/ASResource.cpp @@ -0,0 +1,828 @@ +// ASResource.cpp +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +//----------------------------------------------------------------------------- +// headers +//----------------------------------------------------------------------------- + +#include "astyle.h" +#include +#include + +//----------------------------------------------------------------------------- +// astyle namespace +//----------------------------------------------------------------------------- + +namespace astyle { +// +const std::string ASResource::_AS_EXCEPT = std::string("__except"); +const std::string ASResource::_AS_FINALLY = std::string("__finally"); +const std::string ASResource::_AS_TRY = std::string("__try"); +const std::string ASResource::AS_ADD = std::string("add"); +const std::string ASResource::AS_AUTO = std::string("auto"); +const std::string ASResource::AS_AUTORELEASEPOOL = std::string("autoreleasepool"); +const std::string ASResource::AS_CASE = std::string("case"); +const std::string ASResource::AS_CATCH = std::string("catch"); +const std::string ASResource::AS_CLASS = std::string("class"); +const std::string ASResource::AS_CONST = std::string("const"); +const std::string ASResource::AS_CONST_CAST = std::string("const_cast"); +const std::string ASResource::AS_DEFAULT = std::string("default"); +const std::string ASResource::AS_DELEGATE = std::string("delegate"); +const std::string ASResource::AS_DELETE = std::string("delete"); +const std::string ASResource::AS_DO = std::string("do"); +const std::string ASResource::AS_DYNAMIC_CAST = std::string("dynamic_cast"); +const std::string ASResource::AS_ELSE = std::string("else"); +const std::string ASResource::AS_END = std::string("end"); +const std::string ASResource::AS_ENUM = std::string("enum"); +const std::string ASResource::AS_EXTERN = std::string("extern"); +const std::string ASResource::AS_FINAL = std::string("final"); +const std::string ASResource::AS_FINALLY = std::string("finally"); +const std::string ASResource::AS_FIXED = std::string("fixed"); +const std::string ASResource::AS_FOR = std::string("for"); +const std::string ASResource::AS_FOREACH = std::string("foreach"); +const std::string ASResource::AS_FOREVER = std::string("forever"); +const std::string ASResource::AS_GET = std::string("get"); +const std::string ASResource::AS_IF = std::string("if"); +const std::string ASResource::AS_INTERFACE = std::string("interface"); +const std::string ASResource::AS_INTERRUPT = std::string("interrupt"); +const std::string ASResource::AS_LET = std::string("let"); +const std::string ASResource::AS_LOCK = std::string("lock"); +const std::string ASResource::AS_MODULE = std::string("module"); // CORBA IDL module definition +const std::string ASResource::AS_NAMESPACE = std::string("namespace"); +const std::string ASResource::AS_NEW = std::string("new"); +const std::string ASResource::AS_NOEXCEPT = std::string("noexcept"); +const std::string ASResource::AS_NS_DURING = std::string("NS_DURING"); +const std::string ASResource::AS_NS_HANDLER = std::string("NS_HANDLER"); +const std::string ASResource::AS_OPERATOR = std::string("operator"); +const std::string ASResource::AS_OVERRIDE = std::string("override"); +const std::string ASResource::AS_PRIVATE = std::string("private"); +const std::string ASResource::AS_PROTECTED = std::string("protected"); +const std::string ASResource::AS_PUBLIC = std::string("public"); +const std::string ASResource::AS_QFOREACH = std::string("Q_FOREACH"); +const std::string ASResource::AS_QFOREVER = std::string("Q_FOREVER"); +const std::string ASResource::AS_REINTERPRET_CAST = std::string("reinterpret_cast"); +const std::string ASResource::AS_REMOVE = std::string("remove"); +const std::string ASResource::AS_SEALED = std::string("sealed"); +const std::string ASResource::AS_SELECTOR = std::string("selector"); +const std::string ASResource::AS_SET = std::string("set"); +const std::string ASResource::AS_STATIC = std::string("static"); +const std::string ASResource::AS_STATIC_CAST = std::string("static_cast"); +const std::string ASResource::AS_STRUCT = std::string("struct"); +const std::string ASResource::AS_TYPEDEF_STRUCT = std::string("typedef struct"); +const std::string ASResource::AS_SWITCH = std::string("switch"); +const std::string ASResource::AS_SYNCHRONIZED = std::string("synchronized"); +const std::string ASResource::AS_TEMPLATE = std::string("template"); +const std::string ASResource::AS_THROW = std::string("throw"); +const std::string ASResource::AS_THROWS = std::string("throws"); +const std::string ASResource::AS_TRY = std::string("try"); +const std::string ASResource::AS_UNCHECKED = std::string("unchecked"); +const std::string ASResource::AS_UNION = std::string("union"); +const std::string ASResource::AS_UNSAFE = std::string("unsafe"); +const std::string ASResource::AS_USING = std::string("using"); +const std::string ASResource::AS_VOLATILE = std::string("volatile"); +const std::string ASResource::AS_WHERE = std::string("where"); +const std::string ASResource::AS_WHILE = std::string("while"); + +const std::string ASResource::AS_ASM = std::string("asm"); +const std::string ASResource::AS__ASM__ = std::string("__asm__"); +const std::string ASResource::AS_MS_ASM = std::string("_asm"); +const std::string ASResource::AS_MS__ASM = std::string("__asm"); + +const std::string ASResource::AS_BAR_DEFINE = std::string("#define"); +const std::string ASResource::AS_BAR_INCLUDE = std::string("#include"); +const std::string ASResource::AS_BAR_IF = std::string("#if"); +const std::string ASResource::AS_BAR_EL = std::string("#el"); +const std::string ASResource::AS_BAR_ENDIF = std::string("#endif"); + +const std::string ASResource::AS_OPEN_PAREN = std::string("("); +const std::string ASResource::AS_CLOSE_PAREN = std::string(")"); +const std::string ASResource::AS_OPEN_BRACE = std::string("{"); +const std::string ASResource::AS_CLOSE_BRACE = std::string("}"); +const std::string ASResource::AS_OPEN_LINE_COMMENT = std::string("//"); +const std::string ASResource::AS_OPEN_COMMENT = std::string("/*"); +const std::string ASResource::AS_CLOSE_COMMENT = std::string("*/"); + +const std::string ASResource::AS_GSC_OPEN_COMMENT = std::string("/#"); +const std::string ASResource::AS_GSC_CLOSE_COMMENT = std::string("#/"); + +const std::string ASResource::AS_OPEN_CONFLICT = std::string("<<<<<<<"); +const std::string ASResource::AS_MIDDLE_CONFLICT = std::string("======="); +const std::string ASResource::AS_CLOSE_CONFLICT = std::string(">>>>>>>"); + +const std::string ASResource::AS_ASSIGN = std::string("="); +const std::string ASResource::AS_PLUS_ASSIGN = std::string("+="); +const std::string ASResource::AS_MINUS_ASSIGN = std::string("-="); +const std::string ASResource::AS_MULT_ASSIGN = std::string("*="); +const std::string ASResource::AS_DIV_ASSIGN = std::string("/="); +const std::string ASResource::AS_MOD_ASSIGN = std::string("%="); +const std::string ASResource::AS_OR_ASSIGN = std::string("|="); +const std::string ASResource::AS_AND_ASSIGN = std::string("&="); +const std::string ASResource::AS_XOR_ASSIGN = std::string("^="); +const std::string ASResource::AS_GR_GR_ASSIGN = std::string(">>="); +const std::string ASResource::AS_LS_LS_ASSIGN = std::string("<<="); +const std::string ASResource::AS_GR_GR_GR_ASSIGN = std::string(">>>="); +const std::string ASResource::AS_LS_LS_LS_ASSIGN = std::string("<<<="); +const std::string ASResource::AS_GCC_MIN_ASSIGN = std::string("?"); + +const std::string ASResource::AS_RETURN = std::string("return"); +const std::string ASResource::AS_CIN = std::string("cin"); +const std::string ASResource::AS_COUT = std::string("cout"); +const std::string ASResource::AS_CERR = std::string("cerr"); + +const std::string ASResource::AS_EQUAL = std::string("=="); +const std::string ASResource::AS_PLUS_PLUS = std::string("++"); +const std::string ASResource::AS_MINUS_MINUS = std::string("--"); +const std::string ASResource::AS_NOT_EQUAL = std::string("!="); +const std::string ASResource::AS_GR_EQUAL = std::string(">="); +const std::string ASResource::AS_GR_GR = std::string(">>"); +const std::string ASResource::AS_GR_GR_GR = std::string(">>>"); +const std::string ASResource::AS_LS_EQUAL = std::string("<="); +const std::string ASResource::AS_LS_LS = std::string("<<"); +const std::string ASResource::AS_LS_LS_LS = std::string("<<<"); +const std::string ASResource::AS_QUESTION_QUESTION = std::string("??"); +const std::string ASResource::AS_LAMBDA = std::string("=>"); // C# lambda expression arrow +const std::string ASResource::AS_ARROW = std::string("->"); +const std::string ASResource::AS_AND = std::string("&&"); +const std::string ASResource::AS_OR = std::string("||"); +const std::string ASResource::AS_SCOPE_RESOLUTION = std::string("::"); +const std::string ASResource::AS_SPACESHIP = std::string("<=>"); +const std::string ASResource::AS_EQUAL_JS = std::string("==="); +const std::string ASResource::AS_COALESCE_CS = std::string("?" "?="); // Avoid trigraph + +const std::string ASResource::AS_PLUS = std::string("+"); +const std::string ASResource::AS_MINUS = std::string("-"); +const std::string ASResource::AS_MULT = std::string("*"); +const std::string ASResource::AS_DIV = std::string("/"); +const std::string ASResource::AS_MOD = std::string("%"); +const std::string ASResource::AS_GR = std::string(">"); +const std::string ASResource::AS_LS = std::string("<"); +const std::string ASResource::AS_NOT = std::string("!"); +const std::string ASResource::AS_BIT_OR = std::string("|"); +const std::string ASResource::AS_BIT_AND = std::string("&"); +const std::string ASResource::AS_BIT_NOT = std::string("~"); +const std::string ASResource::AS_BIT_XOR = std::string("^"); +const std::string ASResource::AS_QUESTION = std::string("?"); +const std::string ASResource::AS_COLON = std::string(":"); +const std::string ASResource::AS_COMMA = std::string(","); +const std::string ASResource::AS_DOT = std::string("."); +const std::string ASResource::AS_SEMICOLON = std::string(";"); + + +/** + * Sort comparison function. + * Compares the length of the value of pointers in the vectors. + * The LONGEST std::strings will be first in the vector. + * + * @param a and b, the std::string pointers to be compared. + */ +bool sortOnLength(const std::string* a, const std::string* b) +{ + return (*a).length() > (*b).length(); +} + +/** + * Sort comparison function. + * Compares the value of pointers in the vectors. + * + * @param a and b, the std::string pointers to be compared. + */ +bool sortOnName(const std::string* a, const std::string* b) +{ + return *a < *b; +} + +/** + * Build the vector of assignment operators. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp + * + * @param assignmentOperators a reference to the vector to be built. + */ +void ASResource::buildAssignmentOperators(std::vector* assignmentOperators) +{ + const size_t elements = 15; + assignmentOperators->reserve(elements); + + assignmentOperators->emplace_back(&AS_ASSIGN); + assignmentOperators->emplace_back(&AS_PLUS_ASSIGN); + assignmentOperators->emplace_back(&AS_MINUS_ASSIGN); + assignmentOperators->emplace_back(&AS_MULT_ASSIGN); + assignmentOperators->emplace_back(&AS_DIV_ASSIGN); + assignmentOperators->emplace_back(&AS_MOD_ASSIGN); + assignmentOperators->emplace_back(&AS_OR_ASSIGN); + assignmentOperators->emplace_back(&AS_AND_ASSIGN); + assignmentOperators->emplace_back(&AS_XOR_ASSIGN); + + // Java + assignmentOperators->emplace_back(&AS_GR_GR_GR_ASSIGN); + assignmentOperators->emplace_back(&AS_GR_GR_ASSIGN); + assignmentOperators->emplace_back(&AS_LS_LS_ASSIGN); + + // Unknown + assignmentOperators->emplace_back(&AS_LS_LS_LS_ASSIGN); + + assert(assignmentOperators->size() < elements); + sort(assignmentOperators->begin(), assignmentOperators->end(), sortOnLength); +} + +/** + * Build the vector of C++ cast operators. + * Used by ONLY ASFormatter.cpp + * + * @param castOperators a reference to the vector to be built. + */ +void ASResource::buildCastOperators(std::vector* castOperators) +{ + const size_t elements = 5; + castOperators->reserve(elements); + + castOperators->emplace_back(&AS_CONST_CAST); + castOperators->emplace_back(&AS_DYNAMIC_CAST); + castOperators->emplace_back(&AS_REINTERPRET_CAST); + castOperators->emplace_back(&AS_STATIC_CAST); + + assert(castOperators->size() < elements); + sort(castOperators->begin(), castOperators->end(), sortOnName); +} + +/** + * Build the vector of header words. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp + * + * @param headers a reference to the vector to be built. + */ +void ASResource::buildHeaders(std::vector* headers, int fileType, bool beautifier) +{ + const size_t elements = 25; + headers->reserve(elements); + + headers->emplace_back(&AS_IF); + headers->emplace_back(&AS_ELSE); + headers->emplace_back(&AS_FOR); + headers->emplace_back(&AS_WHILE); + headers->emplace_back(&AS_DO); + headers->emplace_back(&AS_SWITCH); + headers->emplace_back(&AS_CASE); + headers->emplace_back(&AS_DEFAULT); + headers->emplace_back(&AS_TRY); + headers->emplace_back(&AS_CATCH); + headers->emplace_back(&AS_QFOREACH); // QT + headers->emplace_back(&AS_QFOREVER); // QT + headers->emplace_back(&AS_FOREACH); // QT & C# + headers->emplace_back(&AS_FOREVER); // Qt & Boost + + if (fileType == C_TYPE || fileType == OBJC_TYPE) + { + headers->emplace_back(&_AS_TRY); // __try + headers->emplace_back(&_AS_FINALLY); // __finally + headers->emplace_back(&_AS_EXCEPT); // __except + } + if (fileType == JAVA_TYPE) + { + headers->emplace_back(&AS_FINALLY); + headers->emplace_back(&AS_SYNCHRONIZED); + } + + if (fileType == SHARP_TYPE) + { + headers->emplace_back(&AS_FINALLY); + headers->emplace_back(&AS_LOCK); + headers->emplace_back(&AS_FIXED); + headers->emplace_back(&AS_GET); + headers->emplace_back(&AS_SET); + headers->emplace_back(&AS_ADD); + headers->emplace_back(&AS_REMOVE); + headers->emplace_back(&AS_USING); + } + + if (beautifier) + { + if (fileType == C_TYPE || fileType == OBJC_TYPE) + { + headers->emplace_back(&AS_TEMPLATE); + } + + if (fileType == JAVA_TYPE) + { + headers->emplace_back(&AS_STATIC); // for static constructor + } + } + + assert(headers->size() < elements); + sort(headers->begin(), headers->end(), sortOnName); +} + +/** + * Build the vector of indentable headers. + * Used by ONLY ASBeautifier.cpp + * + * @param indentableHeaders a reference to the vector to be built. + */ +void ASResource::buildIndentableHeaders(std::vector* indentableHeaders) +{ + indentableHeaders->emplace_back(&AS_RETURN); +} + +/** +* Build the vector of indentable macros pairs. +* Initialized by ASFormatter, used by ONLY ASEnhancer.cpp +* +* @param indentableMacros a reference to the vector to be built. +*/ +void ASResource::buildIndentableMacros(std::vector* >* indentableMacros) +{ + const size_t elements = 10; + indentableMacros->reserve(elements); + + // the pairs must be retained in memory because of pair pointers + using macro_pair = std::pair; + static const macro_pair macros[] = + { + // wxWidgets + macro_pair("BEGIN_EVENT_TABLE", "END_EVENT_TABLE"), + macro_pair("wxBEGIN_EVENT_TABLE", "wxEND_EVENT_TABLE"), + // MFC + macro_pair("BEGIN_DISPATCH_MAP", "END_DISPATCH_MAP"), + macro_pair("BEGIN_EVENT_MAP", "END_EVENT_MAP"), + macro_pair("BEGIN_MESSAGE_MAP", "END_MESSAGE_MAP"), + macro_pair("BEGIN_PROPPAGEIDS", "END_PROPPAGEIDS"), + }; + + for (const macro_pair& macro : macros) + indentableMacros->emplace_back(¯o); + + assert(indentableMacros->size() < elements); +} + +/** + * Build the vector of non-assignment operators. + * Used by ONLY ASBeautifier.cpp + * + * @param nonAssignmentOperators a reference to the vector to be built. + * @param fileType language identifier. + * + */ +void ASResource::buildNonAssignmentOperators(std::vector* nonAssignmentOperators, int fileType) +{ + const size_t elements = 16; + nonAssignmentOperators->reserve(elements); + + nonAssignmentOperators->emplace_back(&AS_EQUAL); + nonAssignmentOperators->emplace_back(&AS_PLUS_PLUS); + nonAssignmentOperators->emplace_back(&AS_MINUS_MINUS); + nonAssignmentOperators->emplace_back(&AS_NOT_EQUAL); + nonAssignmentOperators->emplace_back(&AS_GR_EQUAL); + nonAssignmentOperators->emplace_back(&AS_GR_GR); + nonAssignmentOperators->emplace_back(&AS_LS_EQUAL); + nonAssignmentOperators->emplace_back(&AS_LS_LS); + nonAssignmentOperators->emplace_back(&AS_ARROW); + nonAssignmentOperators->emplace_back(&AS_AND); + nonAssignmentOperators->emplace_back(&AS_OR); + nonAssignmentOperators->emplace_back(&AS_LAMBDA); + nonAssignmentOperators->emplace_back(&AS_DOT); + + if (fileType == JAVA_TYPE) { + nonAssignmentOperators->emplace_back(&AS_GR_GR_GR); + nonAssignmentOperators->emplace_back(&AS_LS_LS_LS); + } + + assert(nonAssignmentOperators->size() < elements); + sort(nonAssignmentOperators->begin(), nonAssignmentOperators->end(), sortOnLength); +} + +/** + * Build the vector of header non-paren headers. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp. + * NOTE: Non-paren headers should also be included in the headers vector. + * + * @param nonParenHeaders a reference to the std::vector to be built. + * @param fileType language identifier. + * + */ +void ASResource::buildNonParenHeaders(std::vector* nonParenHeaders, int fileType, bool beautifier) +{ + const size_t elements = 20; + nonParenHeaders->reserve(elements); + + nonParenHeaders->emplace_back(&AS_ELSE); + nonParenHeaders->emplace_back(&AS_DO); + nonParenHeaders->emplace_back(&AS_TRY); + nonParenHeaders->emplace_back(&AS_CATCH); // can be paren or non-paren + nonParenHeaders->emplace_back(&AS_CASE); // can be paren or non-paren + nonParenHeaders->emplace_back(&AS_DEFAULT); + nonParenHeaders->emplace_back(&AS_QFOREVER); // QT + nonParenHeaders->emplace_back(&AS_FOREVER); // Boost + + if (fileType == C_TYPE || fileType == OBJC_TYPE) + { + nonParenHeaders->emplace_back(&_AS_TRY); // __try + nonParenHeaders->emplace_back(&_AS_FINALLY); // __finally + } + if (fileType == JAVA_TYPE) + { + nonParenHeaders->emplace_back(&AS_FINALLY); + } + + if (fileType == SHARP_TYPE) + { + nonParenHeaders->emplace_back(&AS_FINALLY); + nonParenHeaders->emplace_back(&AS_GET); + nonParenHeaders->emplace_back(&AS_SET); + nonParenHeaders->emplace_back(&AS_ADD); + nonParenHeaders->emplace_back(&AS_REMOVE); + } + + if (beautifier) + { + if (fileType == C_TYPE) + { + nonParenHeaders->emplace_back(&AS_TEMPLATE); + } + if (fileType == JAVA_TYPE) + { + nonParenHeaders->emplace_back(&AS_STATIC); + } + } + + assert(nonParenHeaders->size() < elements); + sort(nonParenHeaders->begin(), nonParenHeaders->end(), sortOnName); +} + +/** + * Build the vector of operators. + * Used by ONLY ASFormatter.cpp + * + * @param operators a reference to the vector to be built. + */ +void ASResource::buildOperators(std::vector* operators, int fileType) +{ + const size_t elements = 50; + operators->reserve(elements); + + operators->emplace_back(&AS_PLUS_ASSIGN); + operators->emplace_back(&AS_MINUS_ASSIGN); + operators->emplace_back(&AS_MULT_ASSIGN); + operators->emplace_back(&AS_DIV_ASSIGN); + operators->emplace_back(&AS_MOD_ASSIGN); + operators->emplace_back(&AS_OR_ASSIGN); + operators->emplace_back(&AS_AND_ASSIGN); + operators->emplace_back(&AS_XOR_ASSIGN); + operators->emplace_back(&AS_EQUAL); + operators->emplace_back(&AS_PLUS_PLUS); + operators->emplace_back(&AS_MINUS_MINUS); + operators->emplace_back(&AS_NOT_EQUAL); + operators->emplace_back(&AS_GR_EQUAL); + operators->emplace_back(&AS_GR_GR_GR_ASSIGN); + operators->emplace_back(&AS_GR_GR_ASSIGN); + operators->emplace_back(&AS_GR_GR_GR); + operators->emplace_back(&AS_GR_GR); + operators->emplace_back(&AS_LS_EQUAL); + operators->emplace_back(&AS_LS_LS_LS_ASSIGN); + operators->emplace_back(&AS_LS_LS_ASSIGN); + operators->emplace_back(&AS_LS_LS_LS); + operators->emplace_back(&AS_LS_LS); + operators->emplace_back(&AS_QUESTION_QUESTION); + operators->emplace_back(&AS_LAMBDA); + operators->emplace_back(&AS_ARROW); + operators->emplace_back(&AS_AND); + operators->emplace_back(&AS_OR); + operators->emplace_back(&AS_SCOPE_RESOLUTION); + operators->emplace_back(&AS_PLUS); + operators->emplace_back(&AS_MINUS); + operators->emplace_back(&AS_MULT); + operators->emplace_back(&AS_DIV); + operators->emplace_back(&AS_MOD); + operators->emplace_back(&AS_QUESTION); + operators->emplace_back(&AS_COLON); + operators->emplace_back(&AS_ASSIGN); + operators->emplace_back(&AS_LS); + operators->emplace_back(&AS_GR); + operators->emplace_back(&AS_NOT); + operators->emplace_back(&AS_BIT_OR); + operators->emplace_back(&AS_BIT_AND); + operators->emplace_back(&AS_BIT_NOT); + operators->emplace_back(&AS_BIT_XOR); + if (fileType == C_TYPE) + { + operators->emplace_back(&AS_GCC_MIN_ASSIGN); + operators->emplace_back(&AS_GCC_MAX_ASSIGN); + operators->emplace_back(&AS_SPACESHIP); + } + if (fileType == JS_TYPE) + { + operators->emplace_back(&AS_EQUAL_JS); + } + if (fileType == SHARP_TYPE) + { + operators->emplace_back(&AS_COALESCE_CS); + } + assert(operators->size() < elements); + sort(operators->begin(), operators->end(), sortOnLength); +} + +/** + * Build the vector of pre-block statements. + * Used by ONLY ASBeautifier.cpp + * NOTE: Cannot be both a header and a preBlockStatement. + * + * @param preBlockStatements a reference to the vector to be built. + */ +void ASResource::buildPreBlockStatements(std::vector* preBlockStatements, int fileType) +{ + const size_t elements = 10; + preBlockStatements->reserve(elements); + + preBlockStatements->emplace_back(&AS_CLASS); + if (fileType == C_TYPE || fileType == OBJC_TYPE) + { + preBlockStatements->emplace_back(&AS_STRUCT); + preBlockStatements->emplace_back(&AS_UNION); + preBlockStatements->emplace_back(&AS_NAMESPACE); + preBlockStatements->emplace_back(&AS_MODULE); // for CORBA IDL + preBlockStatements->emplace_back(&AS_INTERFACE); // for CORBA IDL + } + if (fileType == JAVA_TYPE) + { + preBlockStatements->emplace_back(&AS_INTERFACE); + preBlockStatements->emplace_back(&AS_THROWS); + } + if (fileType == SHARP_TYPE) + { + preBlockStatements->emplace_back(&AS_INTERFACE); + preBlockStatements->emplace_back(&AS_NAMESPACE); + preBlockStatements->emplace_back(&AS_WHERE); + preBlockStatements->emplace_back(&AS_STRUCT); + } + + assert(preBlockStatements->size() < elements); + sort(preBlockStatements->begin(), preBlockStatements->end(), sortOnName); +} + +/** + * Build the vector of pre-command headers. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp. + * NOTE: Cannot be both a header and a preCommandHeader. + * + * A preCommandHeader is in a function definition between + * the closing paren and the opening brace. + * e.g. in "void foo() const {}", "const" is a preCommandHeader. + */ +void ASResource::buildPreCommandHeaders(std::vector* preCommandHeaders, int fileType) +{ + const size_t elements = 10; + preCommandHeaders->reserve(elements); + + if (fileType == C_TYPE || fileType == OBJC_TYPE) + { + preCommandHeaders->emplace_back(&AS_CONST); + preCommandHeaders->emplace_back(&AS_FINAL); + preCommandHeaders->emplace_back(&AS_INTERRUPT); + preCommandHeaders->emplace_back(&AS_NOEXCEPT); + preCommandHeaders->emplace_back(&AS_OVERRIDE); + preCommandHeaders->emplace_back(&AS_VOLATILE); + preCommandHeaders->emplace_back(&AS_SEALED); // Visual C only + } + + if (fileType == OBJC_TYPE) + { + preCommandHeaders->emplace_back(&AS_AUTORELEASEPOOL); // Obj-C only + } + + if (fileType == JAVA_TYPE) + { + preCommandHeaders->emplace_back(&AS_THROWS); + } + + if (fileType == SHARP_TYPE) + { + preCommandHeaders->emplace_back(&AS_WHERE); + } + + assert(preCommandHeaders->size() < elements); + sort(preCommandHeaders->begin(), preCommandHeaders->end(), sortOnName); +} + +/** + * Build the vector of pre-definition headers. + * Used by ONLY ASFormatter.cpp + * NOTE: Do NOT add 'enum' here. It is an array type brace. + * NOTE: Do NOT add 'extern' here. Do not want an extra indent. + * + * @param preDefinitionHeaders a reference to the vector to be built. + */ +void ASResource::buildPreDefinitionHeaders(std::vector* preDefinitionHeaders, int fileType) +{ + const size_t elements = 10; + preDefinitionHeaders->reserve(elements); + + preDefinitionHeaders->emplace_back(&AS_CLASS); + if (fileType == C_TYPE || fileType == OBJC_TYPE) + { + preDefinitionHeaders->emplace_back(&AS_STRUCT); + preDefinitionHeaders->emplace_back(&AS_UNION); + preDefinitionHeaders->emplace_back(&AS_NAMESPACE); + preDefinitionHeaders->emplace_back(&AS_MODULE); // for CORBA IDL + preDefinitionHeaders->emplace_back(&AS_INTERFACE); // for CORBA IDL + } + if (fileType == JAVA_TYPE) + { + preDefinitionHeaders->emplace_back(&AS_INTERFACE); + } + if (fileType == SHARP_TYPE) + { + preDefinitionHeaders->emplace_back(&AS_STRUCT); + preDefinitionHeaders->emplace_back(&AS_INTERFACE); + preDefinitionHeaders->emplace_back(&AS_NAMESPACE); + } + + assert(preDefinitionHeaders->size() < elements); + sort(preDefinitionHeaders->begin(), preDefinitionHeaders->end(), sortOnName); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * ASBase Functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// check if a specific line position contains a header. +const std::string* ASBase::findHeader(std::string_view line, int i, + const std::vector* possibleHeaders) const +{ + assert(isCharPotentialHeader(line, i)); + // check the word + size_t maxHeaders = possibleHeaders->size(); + for (size_t p = 0; p < maxHeaders; p++) + { + const std::string* header = (*possibleHeaders)[p]; + const size_t wordEnd = i + header->length(); + if (wordEnd > line.length()) + continue; + int result = (line.compare(i, header->length(), *header)); + if (result > 0) + continue; + if (result < 0) + break; + // check that this is not part of a longer word + if (wordEnd == line.length()) + return header; + if (isLegalNameChar(line[wordEnd])) + continue; + const char peekChar = peekNextChar(line, wordEnd - 1); + // is not a header if part of a definition + if (peekChar == ',' || peekChar == ')') + break; + // the following accessor definitions are NOT headers + // goto default; is NOT a header + // default(int) keyword in C# is NOT a header + if ((header == &ASResource::AS_GET + || header == &ASResource::AS_SET + || header == &ASResource::AS_DEFAULT) + && (peekChar == '.' || peekChar == ';' || peekChar == '(' || peekChar == '=')) + break; + return header; + } + return nullptr; +} + +// check if a specific line position contains a keyword. +bool ASBase::findKeyword(std::string_view line, int i, std::string_view keyword) const +{ + assert(isCharPotentialHeader(line, i)); + // check the word + const size_t keywordLength = keyword.length(); + const size_t wordEnd = i + keywordLength; + if (wordEnd > line.length()) + return false; + if (line.compare(i, keywordLength, keyword) != 0) + return false; + // check that this is not part of a longer word + if (wordEnd == line.length()) + { + //std::cerr << "findKeyword t1 \n"; + return true; + } + + if (isLegalNameChar(line[wordEnd])) + return false; + // is not a keyword if part of a definition + const char peekChar = peekNextChar(line, (int) wordEnd - 1); + if (peekChar == ',' || peekChar == ')') + return false; + + //std::cerr << "findKeyword t2 \n"; + return true; +} + +// check if a specific line position contains an operator. +const std::string* ASBase::findOperator(std::string_view line, int i, + const std::vector* possibleOperators) const +{ + assert(isCharPotentialOperator(line[i])); + // find the operator in the vector + // the vector contains the LONGEST operators first + // must loop thru the entire vector + size_t maxOperators = possibleOperators->size(); + for (size_t p = 0; p < maxOperators; p++) + { + const size_t wordEnd = i + (*(*possibleOperators)[p]).length(); + if (wordEnd > line.length()) + continue; + if (line.compare(i, (*(*possibleOperators)[p]).length(), *(*possibleOperators)[p]) == 0) + return (*possibleOperators)[p]; + } + return nullptr; +} + +// get the current word on a line +// index must point to the beginning of the word +std::string_view ASBase::getCurrentWord(std::string_view line, size_t index) const +{ + //assert(isCharPotentialHeader(line, index)); + size_t lineLength = line.length(); + size_t i; + for (i = index; i < lineLength; i++) + { + if (!isLegalNameChar(line[i]) + || ( (isCStyle() || isJavaStyle()) && i > index && line[i] == '.') + ) + break; + } + return line.substr(index, i - index); +} + +// check if a specific character can be used in a legal variable/method/class name +bool ASBase::isLegalNameChar(char ch) const +{ + if (std::isblank(ch)) + return false; + if ((unsigned char) ch > 127) + return false; + return (isalnum((unsigned char) ch) + || ch == '_' + || (!isSharpStyle() && ch == '.' ) + || (isJavaStyle() && ch == '$') + || (isSharpStyle() && ch == '@')); // may be used as a prefix +} + +// check if a specific character can be part of a header +bool ASBase::isCharPotentialHeader(std::string_view line, size_t i) const +{ + assert(!std::isblank(line[i])); + char prevCh = ' '; + if (i > 0) + prevCh = line[i - 1]; + if (i > 1 && line[i - 2] == '\\') + prevCh = ' '; + if (!isLegalNameChar(prevCh) && isLegalNameChar(line[i])) + return true; + return false; +} + +// check if a specific character can be part of an operator +bool ASBase::isCharPotentialOperator(char ch) const +{ + assert(!std::isblank(ch)); + if ((unsigned) ch > 127) + return false; + return (ispunct((unsigned char) ch) + && ch != '{' && ch != '}' + && ch != '(' && ch != ')' + && ch != '[' && ch != ']' + && ch != ';' && ch != ',' + && ch != '#' && ch != '\\' + && ch != '\'' && ch != '\"'); +} + +// check if a specific character is a digit +// NOTE: Visual C isdigit() gives assert error if char > 256 +bool ASBase::isDigit(char ch) const +{ + return (ch >= '0' && ch <= '9'); +} + +// check if a specific character is a digit separator +bool ASBase::isDigitSeparator(std::string_view line, int i) const +{ + assert(line[i] == '\''); + // casting to (unsigned char) eliminates negative characters + // will get a "Debug Assertion Failed" if not cast + bool foundDigitSeparator = i > 0 + && isxdigit((unsigned char) line[i - 1]) + && i < (int) line.length() - 1 + && isxdigit((unsigned char) line[i + 1]); + return foundDigitSeparator; +} + +// peek at the next unread character. +char ASBase::peekNextChar(std::string_view line, int i) const +{ + char ch = ' '; + size_t peekNum = line.find_first_not_of(" \t", i + 1); + if (peekNum == std::string::npos) + return ch; + ch = line[peekNum]; + return ch; +} + +} // end namespace astyle diff --git a/astyle/AStyle/src/astyle.h b/astyle/AStyle/src/astyle.h new file mode 100644 index 0000000..4c261ae --- /dev/null +++ b/astyle/AStyle/src/astyle.h @@ -0,0 +1,1178 @@ +// astyle.h +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +#ifndef ASTYLE_H +#define ASTYLE_H + +// ignore size_t to int conversion warning for now +#ifdef _WIN64 + #pragma warning( disable : 4267 ) +#endif + +//----------------------------------------------------------------------------- +// headers +//----------------------------------------------------------------------------- + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace std +{ +using string_view=std::experimental::fundamentals_v1::string_view; +int isblank(int c){return ::isblank(c);} +} + +#ifdef __GNUC__ + #include // need both string and cstring for GCC +#endif + +#define ASTYLE_VERSION "3.6.13" + +namespace astyle { + +//---------------------------------------------------------------------------- +// definitions +//---------------------------------------------------------------------------- + +enum FileType { C_TYPE = 0, JAVA_TYPE = 1, SHARP_TYPE = 2, JS_TYPE = 3, OBJC_TYPE = 4, GSC_TYPE = 5, INVALID_TYPE = -1 }; + +/* The enums below are not recognized by 'vectors' in Microsoft Visual C++ + V5 when they are part of a namespace!!! Use Visual C++ V6 or higher. +*/ +enum FormatStyle +{ + STYLE_NONE, + STYLE_ALLMAN, + STYLE_JAVA, + STYLE_KR, + STYLE_STROUSTRUP, + STYLE_WHITESMITH, + STYLE_VTK, + STYLE_RATLIFF, + STYLE_GNU, + STYLE_LINUX, + STYLE_HORSTMANN, + STYLE_1TBS, + STYLE_GOOGLE, + STYLE_MOZILLA, + STYLE_WEBKIT, + STYLE_PICO, + STYLE_LISP +}; + +enum BraceMode +{ + NONE_MODE, + ATTACH_MODE, + BREAK_MODE, + LINUX_MODE, + RUN_IN_MODE // broken braces +}; + +// maximum single value for size_t is 32,768 (total value of 65,535) +enum BraceType : size_t +{ + NULL_TYPE = 0, + NAMESPACE_TYPE = 1, // also a DEFINITION_TYPE + CLASS_TYPE = 2, // also a DEFINITION_TYPE + STRUCT_TYPE = 4, // also a DEFINITION_TYPE + INTERFACE_TYPE = 8, // also a DEFINITION_TYPE + DEFINITION_TYPE = 16, + COMMAND_TYPE = 32, + ARRAY_NIS_TYPE = 64, // also an ARRAY_TYPE + ENUM_TYPE = 128, // also an ARRAY_TYPE + INIT_TYPE = 256, // also an ARRAY_TYPE + ARRAY_TYPE = 512, + EXTERN_TYPE = 1024, // extern "C", not a command type extern + EMPTY_BLOCK_TYPE = 2048, // also a SINGLE_LINE_TYPE + BREAK_BLOCK_TYPE = 4096, // also a SINGLE_LINE_TYPE + SINGLE_LINE_TYPE = 8192 +}; + +enum MinConditional +{ + MINCOND_ZERO, + MINCOND_ONE, + MINCOND_TWO, + MINCOND_ONEHALF, + MINCOND_END +}; + +enum ObjCColonPad +{ + COLON_PAD_NO_CHANGE, + COLON_PAD_NONE, + COLON_PAD_ALL, + COLON_PAD_AFTER, + COLON_PAD_BEFORE +}; + +enum PointerAlign +{ + PTR_ALIGN_NONE, + PTR_ALIGN_TYPE, + PTR_ALIGN_MIDDLE, + PTR_ALIGN_NAME +}; + +enum ReferenceAlign +{ + REF_ALIGN_NONE = PTR_ALIGN_NONE, + REF_ALIGN_TYPE = PTR_ALIGN_TYPE, + REF_ALIGN_MIDDLE = PTR_ALIGN_MIDDLE, + REF_ALIGN_NAME = PTR_ALIGN_NAME, + REF_SAME_AS_PTR +}; + +enum FileEncoding +{ + ENCODING_8BIT, // includes UTF-8 without BOM + UTF_8BOM, // UTF-8 with BOM + UTF_16BE, + UTF_16LE, // Windows default + UTF_32BE, + UTF_32LE +}; + +enum LineEndFormat +{ + LINEEND_DEFAULT, // Use line break that matches most of the file + LINEEND_WINDOWS, + LINEEND_LINUX, + LINEEND_MACOLD +}; + +enum NegationPaddingMode +{ + NEGATION_PAD_NO_CHANGE, + NEGATION_PAD_AFTER, + NEGATION_PAD_BEFORE +}; + +enum IncludeDirectivePaddingMode +{ + INCLUDE_PAD_NO_CHANGE, + INCLUDE_PAD_NONE, + INCLUDE_PAD_AFTER +}; + +//----------------------------------------------------------------------------- +// Class ASSourceIterator +// A pure virtual class is used by ASFormatter and ASBeautifier instead of +// ASStreamIterator. This allows programs using AStyle as a plug-in to define +// their own ASStreamIterator. The ASStreamIterator class must inherit +// this class. +//----------------------------------------------------------------------------- + +class ASSourceIterator +{ +public: + ASSourceIterator() = default; + virtual ~ASSourceIterator() = default; + virtual std::streamoff getPeekStart() const = 0; + virtual int getStreamLength() const = 0; + virtual bool hasMoreLines() const = 0; + virtual std::string nextLine(bool emptyLineWasDeleted) = 0; + virtual std::string peekNextLine() = 0; + virtual void peekReset() = 0; + virtual std::streamoff tellg() = 0; +}; + +//----------------------------------------------------------------------------- +// Class ASPeekStream +// A small class using RAII to peek ahead in the ASSourceIterator stream +// and to reset the ASSourceIterator pointer in the destructor. +// It enables a return from anywhere in the method. +//----------------------------------------------------------------------------- + +class ASPeekStream +{ +private: + ASSourceIterator* sourceIterator; + bool needReset; // reset sourceIterator to the original position + +public: + explicit ASPeekStream(ASSourceIterator* sourceIterator_) + { sourceIterator = sourceIterator_; needReset = false; } + + ~ASPeekStream() + { if (needReset) sourceIterator->peekReset(); } + + bool hasMoreLines() const + { return sourceIterator->hasMoreLines(); } + + std::string peekNextLine() + { needReset = true; return sourceIterator->peekNextLine(); } +}; + + +//----------------------------------------------------------------------------- +// Class ASResource +//----------------------------------------------------------------------------- + +class ASResource +{ +public: + static void buildAssignmentOperators(std::vector* assignmentOperators); + static void buildCastOperators(std::vector* castOperators); + static void buildHeaders(std::vector* headers, int fileType, bool beautifier = false); + static void buildIndentableMacros(std::vector* >* indentableMacros); + static void buildIndentableHeaders(std::vector* indentableHeaders); + static void buildNonAssignmentOperators(std::vector* nonAssignmentOperators, int fileType); + static void buildNonParenHeaders(std::vector* nonParenHeaders, int fileType, bool beautifier = false); + static void buildOperators(std::vector* operators, int fileType); + static void buildPreBlockStatements(std::vector* preBlockStatements, int fileType); + static void buildPreCommandHeaders(std::vector* preCommandHeaders, int fileType); + static void buildPreDefinitionHeaders(std::vector* preDefinitionHeaders, int fileType); + +public: + static const std::string AS_IF, AS_ELSE; + static const std::string AS_DO, AS_WHILE; + static const std::string AS_FOR; + static const std::string AS_SWITCH, AS_CASE, AS_DEFAULT; + static const std::string AS_TRY, AS_CATCH, AS_THROW, AS_THROWS, AS_FINALLY, AS_USING; + static const std::string _AS_TRY, _AS_FINALLY, _AS_EXCEPT; + static const std::string AS_PUBLIC, AS_PROTECTED, AS_PRIVATE; + static const std::string AS_CLASS, AS_STRUCT, AS_TYPEDEF_STRUCT, AS_UNION, AS_INTERFACE, AS_NAMESPACE; + static const std::string AS_MODULE; + static const std::string AS_END; + static const std::string AS_SELECTOR; + static const std::string AS_EXTERN, AS_ENUM; + static const std::string AS_FINAL, AS_OVERRIDE; + static const std::string AS_STATIC, AS_CONST, AS_SEALED, AS_VOLATILE, AS_NEW, AS_DELETE; + static const std::string AS_NOEXCEPT, AS_INTERRUPT, AS_AUTORELEASEPOOL; + static const std::string AS_WHERE, AS_LET, AS_SYNCHRONIZED; + static const std::string AS_OPERATOR, AS_TEMPLATE; + static const std::string AS_OPEN_PAREN, AS_CLOSE_PAREN; + static const std::string AS_OPEN_BRACE, AS_CLOSE_BRACE; + static const std::string AS_OPEN_LINE_COMMENT, AS_OPEN_COMMENT, AS_CLOSE_COMMENT; + static const std::string AS_GSC_OPEN_COMMENT, AS_GSC_CLOSE_COMMENT; + static const std::string AS_OPEN_CONFLICT, AS_MIDDLE_CONFLICT, AS_CLOSE_CONFLICT; + static const std::string AS_BAR_DEFINE, AS_BAR_INCLUDE, AS_BAR_IF, AS_BAR_EL, AS_BAR_ENDIF; + static const std::string AS_AUTO, AS_RETURN; + static const std::string AS_CIN, AS_COUT, AS_CERR, AS_MAPPING; + static const std::string AS_ASSIGN, AS_PLUS_ASSIGN, AS_MINUS_ASSIGN, AS_MULT_ASSIGN; + static const std::string AS_DIV_ASSIGN, AS_MOD_ASSIGN, AS_XOR_ASSIGN, AS_OR_ASSIGN, AS_AND_ASSIGN; + static const std::string AS_GR_GR_ASSIGN, AS_LS_LS_ASSIGN, AS_GR_GR_GR_ASSIGN, AS_LS_LS_LS_ASSIGN; + static const std::string AS_GCC_MIN_ASSIGN, AS_GCC_MAX_ASSIGN, AS_SPACESHIP, AS_EQUAL_JS, AS_COALESCE_CS; + static const std::string AS_EQUAL, AS_PLUS_PLUS, AS_MINUS_MINUS, AS_NOT_EQUAL, AS_GR_EQUAL; + static const std::string AS_LS_EQUAL, AS_LS_LS_LS, AS_LS_LS, AS_GR_GR_GR, AS_GR_GR; + static const std::string AS_QUESTION_QUESTION, AS_LAMBDA; + static const std::string AS_ARROW, AS_AND, AS_OR; + static const std::string AS_SCOPE_RESOLUTION; + static const std::string AS_PLUS, AS_MINUS, AS_MULT, AS_DIV, AS_MOD, AS_GR, AS_LS; + static const std::string AS_NOT, AS_BIT_XOR, AS_BIT_OR, AS_BIT_AND, AS_BIT_NOT; + static const std::string AS_QUESTION, AS_COLON, AS_SEMICOLON, AS_COMMA, AS_DOT; + static const std::string AS_ASM, AS__ASM__, AS_MS_ASM, AS_MS__ASM; + static const std::string AS_QFOREACH, AS_QFOREVER, AS_FOREVER; + static const std::string AS_FOREACH, AS_LOCK, AS_UNSAFE, AS_FIXED; + static const std::string AS_GET, AS_SET, AS_ADD, AS_REMOVE; + static const std::string AS_DELEGATE, AS_UNCHECKED; + static const std::string AS_CONST_CAST, AS_DYNAMIC_CAST, AS_REINTERPRET_CAST, AS_STATIC_CAST; + static const std::string AS_NS_DURING, AS_NS_HANDLER; +}; // Class ASResource + +//----------------------------------------------------------------------------- +// Class ASBase +// Functions definitions are at the end of ASResource.cpp. +//----------------------------------------------------------------------------- + +class ASBase +{ +private: + // all variables should be set by the "init" function + int baseFileType = C_TYPE; // a value from enum FileType + +protected: + ASBase() = default; + +protected: // inline functions + void init(int fileTypeArg) { baseFileType = fileTypeArg; } + bool isCStyle() const { return baseFileType == C_TYPE || baseFileType == OBJC_TYPE || baseFileType == GSC_TYPE; } + bool isJavaStyle() const { return baseFileType == JAVA_TYPE; } + bool isSharpStyle() const { return baseFileType == SHARP_TYPE; } + bool isJSStyle() const { return baseFileType == JS_TYPE; } + bool isObjCStyle() const { return baseFileType == OBJC_TYPE; } + bool isGSCStyle() const { return baseFileType == GSC_TYPE; } + +protected: // functions definitions are at the end of ASResource.cpp + const std::string* findHeader(std::string_view line, int i, + const std::vector* possibleHeaders) const; + bool findKeyword(std::string_view line, int i, std::string_view keyword) const; + const std::string* findOperator(std::string_view line, int i, + const std::vector* possibleOperators) const; + std::string_view getCurrentWord(std::string_view, size_t index) const; + bool isDigit(char ch) const; + bool isLegalNameChar(char ch) const; + bool isCharPotentialHeader(std::string_view line, size_t i) const; + bool isCharPotentialOperator(char ch) const; + bool isDigitSeparator(std::string_view line, int i) const; + char peekNextChar(std::string_view line, int i) const; +}; // Class ASBase + +//----------------------------------------------------------------------------- +// Class ASBeautifier +//----------------------------------------------------------------------------- + +class ASBeautifier : protected ASBase +{ +public: + ASBeautifier(); + virtual ~ASBeautifier(); + ASBeautifier(const ASBeautifier& other); + ASBeautifier& operator=(ASBeautifier const&) = delete; + ASBeautifier(ASBeautifier&&) = delete; + ASBeautifier& operator=(ASBeautifier&&) = delete; + virtual void init(ASSourceIterator* iter); + + virtual std::string beautify(const std::string& originalLine); + void setCaseIndent(bool state); + void setClassIndent(bool state); + void setContinuationIndentation(int indent = 1); + void setCStyle(); + void setDefaultTabLength(); + void setEmptyLineFill(bool state); + void setForceTabXIndentation(int length); + void setAfterParenIndent(bool state); + void setJavaStyle(); + void setJSStyle(); + void setObjCStyle(); + void setSharpStyle(); + void setGSCStyle(); + + void setLabelIndent(bool state); + void setMaxContinuationIndentLength(int max); + void setMaxInStatementIndentLength(int max); + void setMinConditionalIndentOption(int min); + void setMinConditionalIndentLength(); + void setModeManuallySet(bool state); + void setModifierIndent(bool state); + void setNamespaceIndent(bool state); + void setAlignMethodColon(bool state); + void setSpaceIndentation(int length = 4); + void setSwitchIndent(bool state); + void setTabIndentation(int length = 4, bool forceTabs = false); + void setPreprocDefineIndent(bool state); + void setPreprocConditionalIndent(bool state); + void setSqueezeWhitespace(bool state); + void setPreserveWhitespace(bool state); + void setLambdaIndentation(bool state); + int getBeautifierFileType() const; + int getFileType() const; + int getIndentLength() const; + int getTabLength() const; + std::string getIndentString() const; + std::string getNextWord(const std::string& line, size_t currPos) const; + bool getAlignMethodColon() const; + bool getBraceIndent() const; + bool getBlockIndent() const; + bool getCaseIndent() const; + bool getClassIndent() const; + bool getEmptyLineFill() const; + bool getForceTabIndentation() const; + bool getModeManuallySet() const; + bool getModifierIndent() const; + bool getNamespaceIndent() const; + bool getPreprocDefineIndent() const; + bool getSwitchIndent() const; + +protected: + void deleteBeautifierVectors(); + int getNextProgramCharDistance(std::string_view line, int i) const; + int indexOf(const std::vector& container, const std::string* element) const; + void setBlockIndent(bool state); + void setBraceIndent(bool state); + void setBraceIndentVtk(bool state); + std::string extractPreprocessorStatement(std::string_view line) const; + std::string trim(std::string_view str) const; + std::string rtrim(std::string_view str) const; + bool isNumericVariable(std::string_view word) const; + bool isGitConflictMarker(std::string_view line) const; + bool lineStartsWithNumericType(std::string_view line) const; + + + // variables set by ASFormatter - must be updated in activeBeautifierStack + int inLineNumber; + int runInIndentContinuation; + int nonInStatementBrace; + int objCColonAlignSubsequent; // for subsequent lines not counting indent + int bracesNestingLevel; + int bracesNestingLevelOfStruct; + + bool lineCommentNoBeautify; + bool isElseHeaderIndent; + bool isCaseHeaderCommentIndent; + bool isNonInStatementArray; + bool isSharpAccessor; + bool isSharpDelegate; + bool isInExternC; + bool isInBeautifySQL; + bool isInIndentableStruct; + bool isInIndentablePreproc; + bool lambdaIndicator; + int lambdaDepth; + int lastLambdaDepth; + bool preserveWhitespace; + + +private: // functions + void adjustObjCMethodDefinitionIndentation(std::string_view line_); + void adjustObjCMethodCallIndentation(std::string_view line_); + void adjustParsedLineIndentation(size_t iPrelim, bool isInExtraHeaderIndent); + void computePreliminaryIndentation(); + void parseCurrentLine(std::string_view line); + void popLastContinuationIndent(); + void processPreprocessor(std::string_view preproc, std::string_view line); + void registerContinuationIndent(std::string_view line, int i, int spaceIndentCount_, + int tabIncrementIn, int minIndent, bool updateParenStack); + void registerContinuationIndentColon(std::string_view line, int i, int tabIncrementIn); + void initVectors(); + void initTempStacksContainer(std::vector*>*& container, + std::vector*>* value); + void clearObjCMethodDefinitionAlignment(); + void deleteBeautifierContainer(std::vector*& container); + void deleteTempStacksContainer(std::vector*>*& container); + int adjustIndentCountForBreakElseIfComments() const; + int computeObjCColonAlignment(std::string_view line, int colonAlignPosition) const; + int convertTabToSpaces(int i, int tabIncrementIn) const; + int findObjCColonAlignment(std::string_view line) const; + int getContinuationIndentAssign(std::string_view line, size_t currPos) const; + int getContinuationIndentComma(std::string_view line, size_t currPos) const; + int getObjCFollowingKeyword(std::string_view line, int bracePos) const; + bool isIndentedPreprocessor(std::string_view line, size_t currPos) const; + bool isLineEndComment(std::string_view line, int startPos) const; + bool isPreprocessorConditionalCplusplus(std::string_view line) const; + bool isInPreprocessorUnterminatedComment(std::string_view line); + bool isTopLevel() const; + bool statementEndsWithComma(std::string_view line, int index) const; + + std::string getIndentedSpaceEquivalent(std::string_view line_) const; + std::string preLineWS(int lineIndentCount, int lineSpaceIndentCount); + template void deleteContainer(T& container); + template void initContainer(T& container, T value); + std::vector*>* copyTempStacks(const ASBeautifier& other) const; + std::pair computePreprocessorIndent(); + + bool handleHeaderSection(std::string_view line, size_t* i, bool closingBraceReached, bool *haveCaseIndent); + bool handleColonSection(std::string_view line, size_t* i, bool tabIncrementIn, char* ch); + void handleEndOfStatement(size_t i, bool *closingBraceReached, char* ch); + void handleParens(std::string_view line, size_t i, bool tabIncrementIn, bool * isInOperator, char ch); + void handleClosingParen(std::string_view line, size_t i, bool tabIncrementIn); + void handlePotentialHeaderSection(std::string_view line, size_t* i, bool tabIncrementIn, bool *isInOperator); + void handlePotentialOperatorSection(std::string_view line, size_t* i, bool tabIncrementIn, bool haveAssignmentThisLine, bool isInOperator); + +private: // variables + int beautifierFileType; + std::vector* headers; + std::vector* nonParenHeaders; + std::vector* preBlockStatements; + std::vector* preCommandHeaders; + std::vector* assignmentOperators; + std::vector* nonAssignmentOperators; + std::vector* indentableHeaders; + + std::vector* waitingBeautifierStack; + std::vector* activeBeautifierStack; + std::vector* waitingBeautifierStackLengthStack; + std::vector* activeBeautifierStackLengthStack; + std::vector* headerStack; + std::vector* >* tempStacks; + std::vector* parenDepthStack; + std::vector* blockStatementStack; + std::vector* parenStatementStack; + std::vector* braceBlockStateStack; + std::vector* continuationIndentStack; + std::vector* continuationIndentStackSizeStack; + std::vector* parenIndentStack; + std::vector >* preprocIndentStack; + std::vector* lambdaDepthStack; + std::vector > squeezeWSStack; + + ASSourceIterator* sourceIterator; + const std::string* currentHeader; + const std::string* previousLastLineHeader; + const std::string* probationHeader; + const std::string* lastLineHeader; + std::string indentString; + std::string verbatimDelimiter; + bool isInQuote; + bool isInVerbatimQuote; + bool haveLineContinuationChar; + bool isInAsm; + bool isInAsmOneLine; + bool isInAsmBlock; + bool isInComment; + bool isInPreprocessorComment; + bool isInRunInComment; + bool isInCase; + bool isInQuestion; + bool isContinuation; + bool isInHeader; + bool isInTemplate; + bool isInDefine; + bool isInDefineDefinition; + bool classIndent; + bool isIndentModeOff; + bool isInClassHeader; // is in a class before the opening brace + bool isInClassHeaderTab; // is in an indentable class header line + bool isInClassInitializer; // is in a class after the ':' initializer + bool isInClass; // is in a class after the opening brace + bool isInObjCMethodDefinition; + bool isInObjCMethodCall; + bool isInObjCMethodCallFirst; + bool isImmediatelyPostObjCMethodDefinition; + bool isImmediatelyPostObjCMethodCall; + bool isInIndentablePreprocBlock; + bool isInObjCInterface; + bool isInEnum; + bool isInEnumTypeID; + bool isInStruct; + bool isInLet; + bool isInTrailingReturnType; + bool modifierIndent; + bool switchIndent; + bool caseIndent; + bool namespaceIndent; + bool blockIndent; + bool braceIndent; + bool braceIndentVtk; + bool shouldIndentAfterParen; + bool labelIndent; + bool shouldIndentPreprocDefine; + bool isInConditional; + bool isModeManuallySet; + bool shouldForceTabIndentation; + bool emptyLineFill; + bool backslashEndsPrevLine; + bool lineOpensWithLineComment; + bool lineOpensWithComment; + bool lineStartsInComment; + bool blockCommentNoIndent; + bool blockCommentNoBeautify; + bool previousLineProbationTab; + bool lineBeginsWithOpenBrace; + bool lineBeginsWithCloseBrace; + bool lineBeginsWithComma; + bool lineIsCommentOnly; + bool lineIsLineCommentOnly; + bool shouldIndentBracedLine; + bool isInSwitch; + bool foundPreCommandHeader; + bool foundPreCommandMacro; + bool shouldAlignMethodColon; + bool shouldIndentPreprocConditional; + bool squeezeWhitespace; + + bool attemptLambdaIndentation; + + bool isInAssignment; + bool isInInitializerList; + bool isInMultiLineString; + + int indentCount; + int spaceIndentCount; + int spaceIndentObjCMethodAlignment; + int bracePosObjCMethodAlignment; + int colonIndentObjCMethodAlignment; + int lineOpeningBlocksNum; + int lineClosingBlocksNum; + int fileType; + int minConditionalOption; + int minConditionalIndent; + int parenDepth; + int indentLength; + int tabLength; + int continuationIndent; + int blockTabCount; + int maxContinuationIndent; + int classInitializerIndents; + int templateDepth; + int squareBracketCount; + int prevFinalLineSpaceIndentCount; + int prevFinalLineIndentCount; + int defineIndentCount; + int preprocBlockIndent; + int lambdaStartIndent; + int lambdaEndIndent; + size_t quoteContinuationIndent; + char quoteChar; + char prevNonSpaceCh; + char currentNonSpaceCh; + char currentNonLegalCh; + char prevNonLegalCh; +}; // Class ASBeautifier + +//----------------------------------------------------------------------------- +// Class ASEnhancer +//----------------------------------------------------------------------------- + +// TODO rewrite methods to return altered strings + +class ASEnhancer : protected ASBase +{ +public: // functions + ASEnhancer() = default; + void init(int, int, int, bool, bool, bool, bool, bool, bool, bool, + std::vector* >*); + void enhance(std::string& line, bool isInNamespace, bool isInPreprocessor, bool isInSQL); + +private: // functions + void convertForceTabIndentToSpaces(std::string& line) const; + void convertSpaceIndentToForceTab(std::string& line) const; + size_t findCaseColon(std::string_view line, size_t caseIndex) const; + int indentLine(std::string& line, int indent) const; + bool isBeginDeclareSectionSQL(std::string_view line, size_t index) const; + bool isEndDeclareSectionSQL(std::string_view line, size_t index) const; + bool isOneLineBlockReached(std::string_view line, int startChar) const; + void parseCurrentLine(std::string& line, bool isInPreprocessor, bool isInSQL); + size_t processSwitchBlock(std::string& line, size_t index); + int unindentLine(std::string& line, int unindent) const; + +private: + // options from command line or options file + int indentLength; + int tabLength; + bool useTabs; + bool forceTab; + bool namespaceIndent; + bool caseIndent; + bool preprocBlockIndent; + bool preprocDefineIndent; + bool emptyLineFill; + + // parsing variables + int lineNumber; + bool isInQuote; + bool isInComment; + char quoteChar; + + // unindent variables + int braceCount; + int switchDepth; + int eventPreprocDepth; + bool lookingForCaseBrace; + bool unindentNextLine; + bool shouldUnindentLine; + bool shouldUnindentComment; + + // struct used by ParseFormattedLine function + // contains variables used to unindent the case blocks + struct SwitchVariables + { + int switchBraceCount; + int unindentDepth; + bool unindentCase; + }; + + SwitchVariables sw; // switch variables struct + std::vector switchStack; // stack std::vector of switch variables + + // event table variables + bool nextLineIsEventIndent; // begin event table indent is reached + bool isInEventTable; // need to indent an event table + std::vector* >* indentableMacros; + + // SQL variables + bool nextLineIsDeclareIndent; // begin declare section indent is reached + bool isInDeclareSection; // need to indent a declare section + +}; // Class ASEnhancer + +//----------------------------------------------------------------------------- +// Class ASFormatter +//----------------------------------------------------------------------------- + +class ASFormatter : public ASBeautifier +{ +public: // functions + ASFormatter(); + ~ASFormatter() override; + ASFormatter(const ASFormatter&) = delete; + ASFormatter& operator=(ASFormatter const&) = delete; + ASFormatter(ASFormatter&&) = delete; + ASFormatter& operator=(ASFormatter&&) = delete; + void init(ASSourceIterator* si) override; + + bool hasMoreLines() const; + void extracted(); + std::string nextLine(); + LineEndFormat getLineEndFormat() const; + bool getIsLineReady() const; + void setFormattingStyle(FormatStyle style); + void setAddBracesMode(bool state); + void setAddOneLineBracesMode(bool state); + void setRemoveBracesMode(bool state); + void setAttachClass(bool state); + void setAttachClosingWhile(bool state); + void setAttachExternC(bool state); + void setAttachNamespace(bool state); + void setAttachInline(bool state); + void setBraceFormatMode(BraceMode mode); + void setBreakAfterMode(bool state); + void setBreakClosingHeaderBracesMode(bool state); + void setBreakBlocksMode(bool state); + void setBreakClosingHeaderBlocksMode(bool state); + void setBreakElseIfsMode(bool state); + void setBreakOneLineBlocksMode(bool state); + void setBreakOneLineHeadersMode(bool state); + void setBreakOneLineStatementsMode(bool state); + void setMethodPrefixPaddingMode(bool state); + void setMethodPrefixUnPaddingMode(bool state); + void setReturnTypePaddingMode(bool state); + void setReturnTypeUnPaddingMode(bool state); + void setParamTypePaddingMode(bool state); + void setParamTypeUnPaddingMode(bool state); + void setCloseTemplatesMode(bool state); + void setCommaPaddingMode(bool state); + void setDeleteEmptyLinesMode(bool state); + void setBreakReturnType(bool state); + void setBreakReturnTypeDecl(bool state); + void setAttachReturnType(bool state); + void setAttachReturnTypeDecl(bool state); + void setIndentCol1CommentsMode(bool state); + void setLineEndFormat(LineEndFormat fmt); + void setMaxCodeLength(int max); + void setObjCColonPaddingMode(ObjCColonPad mode); + void setOperatorPaddingMode(bool state); + void setNegationPaddingMode(NegationPaddingMode mode); + void setIncludeDirectivePaddingMode(IncludeDirectivePaddingMode mode); + + + void setParensOutsidePaddingMode(bool state); + void setParensFirstPaddingMode(bool state); + + void setEmptyParensPaddingMode(bool state); + + void setParensInsidePaddingMode(bool state); + void setParensHeaderPaddingMode(bool state); + void setParensUnPaddingMode(bool state); + + void setBracketsOutsidePaddingMode(bool state); + void setBracketsInsidePaddingMode(bool state); + void setBracketsUnPaddingMode(bool state); + + void setPointerAlignment(PointerAlign alignment); + void setPreprocBlockIndent(bool state); + void setReferenceAlignment(ReferenceAlign alignment); + void setStripCommentPrefix(bool state); + void setTabSpaceConversionMode(bool state); + size_t getChecksumIn() const; + size_t getChecksumOut() const; + int getChecksumDiff() const; + int getFormatterFileType() const; + // retained for compatibility with release 2.06 + // "Brackets" have been changed to "Braces" in 3.0 + // they are referenced only by the old "bracket" options + void setAddBracketsMode(bool state); + void setAddOneLineBracketsMode(bool state); + void setRemoveBracketsMode(bool state); + void setBreakClosingHeaderBracketsMode(bool state); + void setSqueezeEmptyLinesNumber(int); + +private: // functions + template void deleteContainer(T& container); + template void initContainer(T& container, T value); + char peekNextChar() const; + BraceType getBraceType(); + bool adjustChecksumIn(int adjustment); + bool computeChecksumIn(std::string_view currentLine_); + bool computeChecksumOut(std::string_view beautifiedLine); + bool addBracesToStatement(); + bool removeBracesFromStatement(); + bool commentAndHeaderFollows(); + bool getNextChar(); + bool getNextLine(bool emptyLineWasDeleted = false); + bool isArrayOperator() const; + bool isBeforeComment() const; + bool isBeforeAnyComment() const; + bool isBeforeAnyLineEndComment(int startPos) const; + bool isBeforeMultipleLineEndComments(int startPos) const; + bool isBraceType(BraceType a, BraceType b) const; + bool isClassInitializer() const; + bool isClosingHeader(const std::string* header) const; + bool isCurrentBraceBroken() const; + bool isDereferenceOrAddressOf() const; + bool isExecSQL(std::string_view line, size_t index) const; + bool isEmptyLine(std::string_view line) const; + bool isExternC() const; + bool isMultiStatementLine() const; + bool isNextWordSharpNonParenHeader(int startChar) const; + bool isNonInStatementArrayBrace() const; + bool isOkToSplitFormattedLine(); + bool isPointerOrReference() const; + bool isPointerOrReferenceCentered() const; + bool isPointerOrReferenceVariable(std::string_view word) const; + bool isPointerToPointer(std::string_view line, int currPos) const; + bool isSharpStyleWithParen(const std::string* header) const; + bool isStructAccessModified(const std::string& firstLine, size_t index) const; + bool isIndentablePreprocessorBlock(const std::string& firstLine, size_t index); + bool isNDefPreprocStatement(std::string_view nextLine_, std::string_view preproc) const; + bool isUnaryOperator() const; + bool isUniformInitializerBrace() const; + bool isImmediatelyPostCast() const; + bool isInExponent() const; + bool isInSwitchStatement() const; + bool isNextCharOpeningBrace(int startChar) const; + bool isOkToBreakBlock(BraceType braceType) const; + bool isOperatorPaddingDisabled() const; + bool pointerSymbolFollows() const; + int findObjCColonAlignment() const; + int getCurrentLineCommentAdjustment(); + int getNextLineCommentAdjustment(); + int isOneLineBlockReached(std::string_view line, int startChar) const; + void adjustComments(); + void appendChar(char ch, bool canBreakLine); + void appendCharInsideComments(); + void appendClosingHeader(); + void appendOperator(std::string_view sequence, bool canBreakLine = true); + void appendSequence(std::string_view sequence, bool canBreakLine = true); + void appendSpacePad(); + void appendSpaceAfter(); + void breakLine(bool isSplitLine = false); + void buildLanguageVectors(); + void updateFormattedLineSplitPoints(char appendedChar); + void updateFormattedLineSplitPointsOperator(std::string_view sequence); + void checkIfTemplateOpener(); + void clearFormattedLineSplitPoints(); + void convertTabToSpaces(); + void deleteContainer(std::vector*& container); + void findReturnTypeSplitPoint(const std::string& firstLine); + void formatArrayRunIn(); + void formatRunIn(); + void formatArrayBraces(BraceType braceType, bool isOpeningArrayBrace); + void formatClosingBrace(BraceType braceType); + void formatCommentBody(); + void formatCommentOpener(); + void formatCommentCloser(); + void formatLineCommentBody(); + void formatLineCommentOpener(); + void formatOpeningBrace(BraceType braceType); + void formatQuoteBody(); + void formatQuoteOpener(); + void formatPointerOrReference(); + void formatPointerOrReferenceCast(); + void formatPointerOrReferenceToMiddle(); + void formatPointerOrReferenceToName(); + void formatPointerOrReferenceToType(); + void fixOptionVariableConflicts(); + void goForward(int i); + void isLineBreakBeforeClosingHeader(); + void initContainer(std::vector*& container, std::vector* value); + void initNewLine(); + void padObjCMethodColon(); + void padObjCMethodPrefix(); + void padObjCParamType(); + void padObjCReturnType(); + void padOperators(const std::string* newOperator); + void padParensOrBrackets(char openDelim, char closeDelim, bool padFirstParen); + void processPreprocessor(); + void resetEndOfStatement(); + void setAttachClosingBraceMode(bool state); + void stripCommentPrefix(); + void testForTimeToSplitFormattedLine(); + void trimContinuationLine(); + void updateFormattedLineSplitPointsPointerOrReference(size_t index); + size_t findFormattedLineSplitPoint() const; + size_t findNextChar(std::string_view line, char searchChar, int searchStart = 0) const; + const std::string* checkForHeaderFollowingComment(std::string_view firstLine) const; + const std::string* getFollowingOperator() const; + std::string getPreviousWord(const std::string& line, int currPos, bool allowDots = false) const; + std::string peekNextText(std::string_view firstLine, + bool endOnEmptyLine = false, + const std::shared_ptr& streamArg = nullptr) const; + + bool handleImmediatelyPostHeaderSection(); + bool handlePassedSemicolonSection(); + void handleAttachedReturnTypes(); + void handleClosedBracesOrParens(); + void handleBraces(); + void handleBreakLine(); + bool handlePotentialHeader(const std::string*&); + void handleEndOfBlock(); + void handleColonSection(); + void handlePotentialHeaderPart2(); + void handlePotentialOperator(const std::string*&); + void handleParens(); + void handleOpenParens(); + + void formatFirstOpenBrace(BraceType braceType); + void formatOpenBrace(); + void formatCloseBrace(BraceType braceType); + + +private: // variables + int formatterFileType; + std::vector* headers; + std::vector* nonParenHeaders; + std::vector* preDefinitionHeaders; + std::vector* preCommandHeaders; + std::vector* operators; + std::vector* assignmentOperators; + std::vector* castOperators; + std::vector* >* indentableMacros; // for ASEnhancer + + ASSourceIterator* sourceIterator; + ASEnhancer* enhancer; + + std::vector* preBraceHeaderStack; + std::vector* braceTypeStack; + std::vector* parenStack; + std::vector* structStack; + std::vector* questionMarkStack; + + std::string currentLine; + std::string formattedLine; + std::string readyFormattedLine; + std::string verbatimDelimiter; + const std::string* currentHeader; + const std::string* previousHeader; + char currentChar; + char previousChar; + char previousNonWSChar; + char previousCommandChar; + char quoteChar; + std::streamoff preprocBlockEnd; + int charNum; + int runInIndentChars; + int nextLineSpacePadNum; + int objCColonAlign; + int preprocBraceTypeStackSize; + int spacePadNum; + int tabIncrementIn; + int templateDepth; + int squareBracketCount; + int parenthesesCount; + + size_t squeezeEmptyLineNum; + size_t squeezeEmptyLineCount; + + size_t checksumIn; + size_t checksumOut; + size_t currentLineFirstBraceNum; // first brace location on currentLine + size_t formattedLineCommentNum; // comment location on formattedLine + size_t leadingSpaces; + size_t maxCodeLength; + size_t methodAttachCharNum; + size_t methodAttachLineNum; + size_t methodBreakCharNum; + size_t methodBreakLineNum; + + // possible split points + size_t maxSemi; // probably a 'for' statement + size_t maxAndOr; // probably an 'if' statement + size_t maxComma; + size_t maxParen; + size_t maxWhiteSpace; + size_t maxSemiPending; + size_t maxAndOrPending; + size_t maxCommaPending; + size_t maxParenPending; + size_t maxWhiteSpacePending; + + size_t previousReadyFormattedLineLength; + FormatStyle formattingStyle; + BraceMode braceFormatMode; + BraceType previousBraceType; + PointerAlign pointerAlignment; + ReferenceAlign referenceAlignment; + ObjCColonPad objCColonPadMode; + LineEndFormat lineEnd; + NegationPaddingMode negationPadMode; + IncludeDirectivePaddingMode includeDirectivePaddingMode; + + bool isVirgin; + bool isInVirginLine; + bool shouldPadCommas; + bool shouldPadOperators; + bool shouldPadParensOutside; + bool shouldPadFirstParen; + bool shouldPadEmptyParens; + bool shouldPadParensInside; + bool shouldPadHeader; + bool shouldStripCommentPrefix; + bool shouldUnPadParens; + bool shouldConvertTabs; + bool shouldIndentCol1Comments; + bool shouldIndentPreprocBlock; + bool shouldCloseTemplates; + bool shouldAttachExternC; + bool shouldAttachNamespace; + bool shouldAttachClass; + bool shouldAttachClosingWhile; + bool shouldAttachInline; + bool isInLineComment; + bool isInComment; + bool isInCommentStartLine; + bool noTrimCommentContinuation; + bool isInPreprocessor; + bool isInPreprocessorDefineDef; + bool isInPreprocessorBeautify; + bool isInTemplate; + bool doesLineStartComment; + bool lineEndsInCommentOnly; + bool lineIsCommentOnly; + bool lineIsLineCommentOnly; + bool lineIsEmpty; + bool isImmediatelyPostCommentOnly; + bool isImmediatelyPostEmptyLine; + bool isInClassInitializer; + bool isInQuote; + bool isInVerbatimQuote; + bool checkInterpolation; + bool haveLineContinuationChar; + bool isInQuoteContinuation; + bool isHeaderInMultiStatementLine; + bool isSpecialChar; + bool isNonParenHeader; + bool foundQuestionMark; + bool foundPreDefinitionHeader; + bool foundNamespaceHeader; + bool foundClassHeader; + bool foundStructHeader; + bool foundInterfaceHeader; + bool foundPreCommandHeader; + bool foundPreCommandMacro; + bool foundTrailingReturnType; + bool foundCastOperator; + bool isInLineBreak; + bool endOfAsmReached; + bool endOfCodeReached; + bool lineCommentNoIndent; + bool isFormattingModeOff; + bool isInEnum; + bool isInStruct; + bool isInContinuedPreProc; + bool isInExecSQL; + bool isInAsm; + bool isInAsmOneLine; + bool isInAsmBlock; + bool isLineReady; + bool elseHeaderFollowsComments; + bool caseHeaderFollowsComments; + bool isPreviousBraceBlockRelated; + bool isInPotentialCalculation; + bool isCharImmediatelyPostComment; + bool isPreviousCharPostComment; + bool isCharImmediatelyPostLineComment; + bool isCharImmediatelyPostOpenBlock; + bool isCharImmediatelyPostCloseBlock; + bool isCharImmediatelyPostTemplate; + bool isCharImmediatelyPostReturn; + bool isCharImmediatelyPostThrow; + bool isCharImmediatelyPostNewDelete; + bool isCharImmediatelyPostOperator; + bool isCharImmediatelyPostPointerOrReference; + bool isInObjCMethodDefinition; + bool isInObjCInterface; + bool isInObjCReturnType; + bool isInObjCParam; + bool isInObjCSelector; + bool breakCurrentOneLineBlock; + bool shouldRemoveNextClosingBrace; + bool isInBraceRunIn; + bool returnTypeChecked; + bool currentLineBeginsWithBrace; + bool attachClosingBraceMode; + bool shouldBreakOneLineBlocks; + bool shouldBreakOneLineHeaders; + bool shouldBreakOneLineStatements; + bool shouldBreakClosingHeaderBraces; + bool shouldBreakElseIfs; + bool shouldBreakLineAfterLogical; + int shouldAddBraces; + bool shouldAddOneLineBraces; + bool shouldRemoveBraces; + bool shouldPadMethodColon; + bool shouldPadMethodPrefix; + bool shouldReparseCurrentChar; + bool shouldUnPadMethodPrefix; + bool shouldPadReturnType; + bool shouldUnPadReturnType; + bool shouldPadParamType; + bool shouldUnPadParamType; + bool shouldDeleteEmptyLines; + bool shouldBreakReturnType; + bool shouldBreakReturnTypeDecl; + bool shouldAttachReturnType; + bool shouldAttachReturnTypeDecl; + bool shouldPadBracketsOutside; + bool shouldPadBracketsInside; + bool shouldUnPadBrackets; + bool needHeaderOpeningBrace; + bool shouldBreakLineAtNextChar; + bool shouldKeepLineUnbroken; + bool passedSemicolon; + bool passedColon; + bool isImmediatelyPostNonInStmt; + bool isCharImmediatelyPostNonInStmt; + bool isImmediatelyPostComment; + bool isImmediatelyPostLineComment; + bool isImmediatelyPostEmptyBlock; + bool isImmediatelyPostObjCMethodPrefix; + bool isImmediatelyPostPreprocessor; + bool isImmediatelyPostReturn; + bool isImmediatelyPostThrow; + bool isImmediatelyPostNewDelete; + bool isImmediatelyPostOperator; + bool isImmediatelyPostTemplate; + bool isImmediatelyPostPointerOrReference; + bool shouldBreakBlocks; + bool shouldBreakClosingHeaderBlocks; + bool isPrependPostBlockEmptyLineRequested; + bool isAppendPostBlockEmptyLineRequested; + bool isIndentablePreprocessor; + bool isIndentablePreprocessorBlck; + bool prependEmptyLine; + bool appendOpeningBrace; + bool foundClosingHeader; + bool isInHeader; + bool isImmediatelyPostHeader; + bool isInCase; + bool isFirstPreprocConditional; + bool processedFirstConditional; + bool isJavaStaticConstructor; + bool isInAllocator; + bool isInMultlineStatement; + int isInExplicitBlock; + +private: // inline functions + // append the CURRENT character (currentChar) to the current formatted line. + void appendCurrentChar(bool canBreakLine = true) + { appendChar(currentChar, canBreakLine); } + + // check if a specific sequence exists in the current placement of the current line + bool isSequenceReached(std::string_view sequence) const + { + return currentLine.compare(charNum, sequence.length(), sequence.data()) == 0; + } + + // call ASBase::findHeader for the current character + const std::string* findHeader(const std::vector* headers_) + { return ASBase::findHeader(currentLine, charNum, headers_); } + + // call ASBase::findOperator for the current character + const std::string* findOperator(const std::vector* operators_) + { return ASBase::findOperator(currentLine, charNum, operators_); } +}; // Class ASFormatter + +//----------------------------------------------------------------------------- +// astyle namespace global declarations +//----------------------------------------------------------------------------- +// sort comparison functions for ASResource +bool sortOnLength(const std::string* a, const std::string* b); +bool sortOnName(const std::string* a, const std::string* b); + +} // namespace astyle + +// end of astyle namespace -------------------------------------------------- + +#endif // closes ASTYLE_H diff --git a/astyle/AStyle/src/astyle_main.cpp b/astyle/AStyle/src/astyle_main.cpp new file mode 100644 index 0000000..d344c04 --- /dev/null +++ b/astyle/AStyle/src/astyle_main.cpp @@ -0,0 +1,4116 @@ +// astyle_main.cpp +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * AStyle_main source file map. + * This source file contains several classes. + * They are arranged as follows. + * --------------------------------------- + * namespace astyle { + * ASStreamIterator methods + * ASConsole methods + * // Windows specific + * // Linux specific + * ASLibrary methods + * // Windows specific + * // Linux specific + * ASOptions methods + * ASEncoding methods + * } // end of astyle namespace + * Global Area --------------------------- + * Java Native Interface functions + * AStyleMainUtf16 entry point + * AStyleMain entry point + * AStyleGetVersion entry point + * main entry point + * --------------------------------------- + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +//----------------------------------------------------------------------------- +// headers +//----------------------------------------------------------------------------- + +#include "astyle_main.h" +#include "astyle.h" + +#include +#include +#include // needed by some compilers +#include +#include +#include +#include + +// includes for recursive getFileNames() function +#ifdef _WIN32 + #undef UNICODE // use ASCII windows functions + #include + + #include + #include + +#else + #include + +#endif + +//----------------------------------------------------------------------------- +// declarations +//----------------------------------------------------------------------------- + +// turn off MinGW automatic file globbing +// this CANNOT be in the astyle namespace +#ifndef ASTYLE_LIB + int _CRT_glob = 0; +#endif + +//---------------------------------------------------------------------------- +// astyle namespace +//---------------------------------------------------------------------------- + +namespace astyle { +// +// console build variables +#ifndef ASTYLE_LIB + char g_fileSeparator = std::filesystem::path::preferred_separator; + #ifdef _WIN32 + bool g_isCaseSensitive = false; // Windows IS NOT case sensitive + #else + bool g_isCaseSensitive = true; // Linux IS case sensitive + #endif // _WIN32 +#endif // ASTYLE_LIB + +// java library build variables +#ifdef ASTYLE_JNI + JNIEnv* g_env; + jobject g_obj; + jmethodID g_mid; +#endif + +const char* g_version = ASTYLE_VERSION; + +//----------------------------------------------------------------------------- +// ASStreamIterator class +// typename will be stringstream for AStyle +// it could be istream or wxChar for plug-ins +//----------------------------------------------------------------------------- + +template +ASStreamIterator::ASStreamIterator(T* in) +{ + inStream = in; + buffer.reserve(200); + + peekStart = 0; + prevLineDeleted = false; + checkForEmptyLine = false; + // get length of stream + inStream->seekg(0, inStream->end); + streamLength = inStream->tellg(); + inStream->seekg(0, inStream->beg); +} + +template +ASStreamIterator::~ASStreamIterator() = default; + +/** +* get the length of the input stream. +* streamLength variable is set by the constructor. +* +* @return length of the input file stream, converted to an int. +*/ +template +int ASStreamIterator::getStreamLength() const +{ + return static_cast(streamLength); +} + +/** + * read the input stream, delete any end of line characters, + * and build a string that contains the input line. + * + * @return string containing the next input line minus any end of line characters + */ +template +std::string ASStreamIterator::nextLine(bool emptyLineWasDeleted) +{ + // verify that the current position is correct + assert(peekStart == 0); + + // a deleted line may be replaced if break-blocks is requested + // this sets up the compare to check for a replaced empty line + if (prevLineDeleted) + { + prevLineDeleted = false; + checkForEmptyLine = true; + } + if (!emptyLineWasDeleted) + prevBuffer = buffer; + else + prevLineDeleted = true; + + // read the next record + buffer.clear(); + char ch; + inStream->get(ch); + + while (!inStream->eof() && ch != '\n' && ch != '\r') + { + buffer.append(1, ch); + inStream->get(ch); + } + + if (inStream->eof()) + { + return buffer; + } + + + lastOutputEOL.clear(); + lastOutputEOL.append(1, ch); + + char peekCh = (char)inStream->peek(); + + // find input end-of-line characters + if (!inStream->eof()) + { + if (ch == '\r') // CR+LF is windows otherwise Mac OS 9 + { + if (peekCh == '\n') + { + lastOutputEOL.append(1, peekCh); + inStream->get(); + } + } + else // LF is Linux, allow for improbable LF/CR + { + if (peekCh == '\r') + { + lastOutputEOL.append(1, peekCh); + inStream->get(); + } + } + } + else + { + inStream->clear(); + } + + return buffer; +} + +// save the current position and get the next line +// this can be called for multiple reads +// when finished peeking you MUST call peekReset() +// call this function from ASFormatter ONLY +template +std::string ASStreamIterator::peekNextLine() +{ + assert(hasMoreLines()); + std::string nextLine_; + char ch; + + if (!peekStart) + peekStart = inStream->tellg(); + + // read the next record + inStream->get(ch); + while (!inStream->eof() && ch != '\n' && ch != '\r') + { + nextLine_.append(1, ch); + inStream->get(ch); + } + + if (inStream->eof()) + { + return nextLine_; + } + + int peekCh = inStream->peek(); + + // remove end-of-line characters + if (!inStream->eof()) + { + if ((peekCh == '\n' || peekCh == '\r') && peekCh != ch) + inStream->get(); + } + + return nextLine_; +} + +// reset current position and EOF for peekNextLine() +template +void ASStreamIterator::peekReset() +{ + assert(peekStart != 0); + inStream->clear(); + inStream->seekg(peekStart); + peekStart = 0; +} + +// save the last input line after input has reached EOF +template +void ASStreamIterator::saveLastInputLine() +{ + assert(inStream->eof()); + prevBuffer = buffer; +} + +// return position of the get pointer +template +std::streamoff ASStreamIterator::tellg() +{ + return inStream->tellg(); +} + + +//----------------------------------------------------------------------------- +// ASConsole class +// main function will be included only in the console build +//----------------------------------------------------------------------------- + +#ifndef ASTYLE_LIB + +ASConsole::ASConsole(ASFormatter& formatterArg) : formatter(formatterArg) +{ + errorStream = &std::cerr; + // command line options + isRecursive = false; + isDryRun = false; + rejectDryRunWithFormat = false; + noBackup = false; + preserveDate = false; + isVerbose = false; + isQuiet = false; + isFormattedOnly = false; + ignoreExcludeErrors = false; + ignoreExcludeErrorsDisplay = false; + useAscii = false; + // other variables + bypassBrowserOpen = false; + hasWildcard = false; + filesAreIdentical = true; + acceptEmptyFileList = false; + origSuffix = ".orig"; + mainDirectoryLength = 0; + filesFormatted = 0; + filesUnchanged = 0; + linesOut = 0; +} + + + +// check files for 16 or 32 bit encoding +// the file must have a Byte Order Mark (BOM) +// NOTE: some string functions don't work with NULLs (e.g. length()) +FileEncoding ASConsole::detectEncoding(const char* data, size_t dataSize) const +{ + if (dataSize >= 3 && memcmp(data, "\xEF\xBB\xBF", 3) == 0) + { + return UTF_8BOM; + } + + if (dataSize >= 4) + { + if (memcmp(data, "\x00\x00\xFE\xFF", 4) == 0) + { + return UTF_32BE; + } + else if (memcmp(data, "\xFF\xFE\x00\x00", 4) == 0) + { + return UTF_32LE; + } + } + + if (dataSize >= 2) + { + if (memcmp(data, "\xFE\xFF", 2) == 0) + { + return UTF_16BE; + } + else if (memcmp(data, "\xFF\xFE", 2) == 0) + { + return UTF_16LE; + } + } + return ENCODING_8BIT; +} + +// error exit without a message +void ASConsole::error() const +{ + (*errorStream) << _("Artistic Style has terminated\n") << '\n'; + exit(EXIT_FAILURE); +} + +// error exit with a message +void ASConsole::error(const char* why, const char* what) const +{ + (*errorStream) << why << ' ' << what << '\n'; + error(); +} + +/** + * If no files have been given, use cin for input and cout for output. + * + * This is used to format text for text editors. + * Do NOT display any console messages when this function is used. + */ +void ASConsole::formatCinToCout() +{ + // check for files from --stdin= and --stdout= + if (!stdPathIn.empty()) + { + if (!freopen(stdPathIn.c_str(), "r", stdin)) + error("Cannot open input file", stdPathIn.c_str()); + } + if (!stdPathOut.empty()) + { + if (!freopen(stdPathOut.c_str(), "w", stdout)) + error("Cannot open output file", stdPathOut.c_str()); + + } + // Using cin.tellg() causes problems with both Windows and Linux. + // The Windows problem occurs when the input is not Windows line-ends. + // The tellg() will be out of sequence with the get() statements. + // The Linux cin.tellg() will return -1 (invalid). + // Copying the input sequentially to a stringstream before + // formatting solves the problem for both. + std::istream* inStream = &std::cin; + + // enforce binary mode to avoid auto conversion of \n to \r\n +#ifdef _WIN32 + _setmode( _fileno( stdout ), _O_BINARY ); + _setmode( _fileno( stdin ), _O_BINARY ); +#endif + + std::stringstream outStream; + char ch; + inStream->get(ch); + while (!inStream->eof() && !inStream->fail()) + { + outStream.put(ch); + inStream->get(ch); + } + ASStreamIterator streamIterator(&outStream); + + initializeOutputEOL(formatter.getLineEndFormat()); + formatter.init(&streamIterator); + + while (formatter.hasMoreLines()) + { + std::cout << formatter.nextLine(); + + if (LINEEND_DEFAULT == formatter.getLineEndFormat()) + { + outputEOL = streamIterator.getLastOutputEOL(); + } + + if (formatter.hasMoreLines()) + { + std::cout << outputEOL; + } + else + { + // this can happen if the file if missing a closing brace and break-blocks is requested + if (formatter.getIsLineReady()) + { + std::cout << outputEOL; + std::cout << formatter.nextLine(); + } + } + } + std::cout.flush(); +} + +/** + * Open input file, format it, and close the output. + * + * @param fileName_ The path and name of the file to be processed. + */ +void ASConsole::formatFile(const std::string& fileName_) +{ + std::stringstream in; + std::ostringstream out; + FileEncoding encoding = readFile(fileName_, in); + + // Unless a specific language mode has been set, set the language mode + // according to the file's suffix. + if (!formatter.getModeManuallySet()) + { + if (stringEndsWith(fileName_, std::string(".java"))) + formatter.setJavaStyle(); + else if (stringEndsWith(fileName_, std::string(".cs"))) + formatter.setSharpStyle(); + else if (stringEndsWith(fileName_, std::string(".gsc")) || stringEndsWith(fileName_, std::string(".ghc"))) + formatter.setGSCStyle(); + else + formatter.setCStyle(); + } + + // set line end format + std::string nextLine; // next output line + filesAreIdentical = true; // input and output files are identical + + initializeOutputEOL(formatter.getLineEndFormat()); + // do this AFTER setting the file mode + ASStreamIterator streamIterator(&in); + formatter.init(&streamIterator); + + // format the file + while (formatter.hasMoreLines()) + { + nextLine = formatter.nextLine(); + out << nextLine; + linesOut++; + + if (LINEEND_DEFAULT == formatter.getLineEndFormat()) + { + outputEOL = streamIterator.getLastOutputEOL(); + } + + if (formatter.hasMoreLines()) + { + out << outputEOL; + } + else + { + streamIterator.saveLastInputLine(); // to compare the last input line + // this can happen if the file if missing a closing brace and break-blocks is requested + if (formatter.getIsLineReady()) + { + out << outputEOL; + nextLine = formatter.nextLine(); + out << nextLine; + linesOut++; + streamIterator.saveLastInputLine(); + } + } + + if (filesAreIdentical) + { + if (streamIterator.checkForEmptyLine) + { + filesAreIdentical = (nextLine.find_first_not_of(" \t") == std::string::npos); + } + else + { + filesAreIdentical = streamIterator.compareToInputBuffer(nextLine) && + (LINEEND_DEFAULT == formatter.getLineEndFormat() || streamIterator.getLastOutputEOL() == outputEOL); + } + streamIterator.checkForEmptyLine = false; + } + } + + // remove targetDirectory from filename if required by print + std::string displayName(fileName_); + if (hasWildcard) + displayName = fileName_.substr(targetDirectory.length() + 1); + + // if file has changed, write the new file + if (!filesAreIdentical) + { + if (!isDryRun) + writeFile(fileName_, encoding, out); + printMsg(_("Formatted %s\n"), displayName); + filesFormatted++; + } + else + { + if (!isFormattedOnly) + printMsg(_("Unchanged %s\n"), displayName); + filesUnchanged++; + } + + assert(formatter.getChecksumDiff() == 0); +} + +/** + * Searches for a file named fileName_ in the current directory. If it is not + * found, recursively searches for fileName_ in the current directory's parent + * directories, returning the location of the first instance of fileName_ + * found. If fileName_ is not found, an empty string is returned. + * + * @param fileName_ The filename the function should attempt to locate. + * @return The full path to fileName_ in the current directory or + * nearest parent directory if found, otherwise an empty + * string. + */ +std::string ASConsole::findProjectOptionFilePath(const std::string& fileName_) const +{ + std::string parent; + + if (!fileNameVector.empty()) + { + std::string first = fileNameVector.front(); + + if (first.find_first_of("*?") != std::string::npos) + { + // First item has wildcards - get rid of them for now + size_t endPath = first.find_last_of(g_fileSeparator); + + if (endPath != std::string::npos) + { + first.erase(endPath + 1, std::string::npos); + } + else + { + first = "."; + first.push_back(g_fileSeparator); + } + } + + parent = getFullPathName(first); + if (parent[parent.size()] != g_fileSeparator) + { + parent.push_back(g_fileSeparator); + } + + } + else if (!stdPathIn.empty()) + { + parent = getFullPathName(stdPathIn); + } + else + { + parent = getFullPathName(getCurrentDirectory(fileName_)); + if (!parent.empty()) + { + parent.push_back(g_fileSeparator); + } + } + // remove filename from path + size_t endPath = parent.find_last_of(g_fileSeparator); + if (endPath != std::string::npos) + parent = parent.substr(0, endPath + 1); + + while (!parent.empty()) + { + std::string filepath = parent + fileName_; + if (fileExists(filepath.c_str())) + return filepath; + if (fileName_ == ".astylerc") + { + filepath = parent + "_astylerc"; + if (fileExists(filepath.c_str())) + return filepath; + } + parent = getParentDirectory(parent); + } + return std::string(); +} + +// for unit testing +std::vector ASConsole::getExcludeHitsVector() const +{ return excludeHitsVector; } + +// for unit testing +std::vector ASConsole::getExcludeVector() const +{ return excludeVector; } + +// for unit testing +std::vector ASConsole::getFileName() const +{ return fileName; } + +// for unit testing +std::vector ASConsole::getFileNameVector() const +{ return fileNameVector; } + +// for unit testing +std::vector ASConsole::getFileOptionsVector() const +{ return fileOptionsVector; } + +// for unit testing +bool ASConsole::getFilesAreIdentical() const +{ return filesAreIdentical; } + +// for unit testing +int ASConsole::getFilesFormatted() const +{ return filesFormatted; } + +// for unit testing +bool ASConsole::getIgnoreExcludeErrors() const +{ return ignoreExcludeErrors; } + +// for unit testing +bool ASConsole::getIgnoreExcludeErrorsDisplay() const +{ return ignoreExcludeErrorsDisplay; } + +// for unit testing +bool ASConsole::getIsDryRun() const +{ return isDryRun; } + +// for unit testing +bool ASConsole::getIsFormattedOnly() const +{ return isFormattedOnly; } + +// for unit testing +std::string ASConsole::getLanguageID() const +{ return localizer.getLanguageID(); } + +// for unit testing +bool ASConsole::getIsQuiet() const +{ return isQuiet; } + +// for unit testing +bool ASConsole::getIsRecursive() const +{ return isRecursive; } + +// for unit testing +bool ASConsole::getIsVerbose() const +{ return isVerbose; } + +// for unit testing +bool ASConsole::getNoBackup() const +{ return noBackup; } + +// for unit testing +std::string ASConsole::getOptionFileName() const +{ return optionFileName; } + +// for unit testing +std::vector ASConsole::getOptionsVector() const +{ return optionsVector; } + +// for unit testing +std::string ASConsole::getOrigSuffix() const +{ return origSuffix; } + +// for unit testing +bool ASConsole::getPreserveDate() const +{ return preserveDate; } + +// for unit testing +std::string ASConsole::getProjectOptionFileName() const +{ + assert(!projectOptionFileName.empty()); + // remove the directory path + size_t start = projectOptionFileName.find_last_of(g_fileSeparator); + if (start == std::string::npos) + start = 0; + return projectOptionFileName.substr(start + 1); +} + +// for unit testing +std::vector ASConsole::getProjectOptionsVector() const +{ return projectOptionsVector; } + +// for unit testing +std::string ASConsole::getStdPathIn() const +{ return stdPathIn; } + +// for unit testing +std::string ASConsole::getStdPathOut() const +{ return stdPathOut; } + +// for unit testing +void ASConsole::setBypassBrowserOpen(bool state) +{ bypassBrowserOpen = state; } + +// for unit testing +std::ostream* ASConsole::getErrorStream() const +{ + return errorStream; +} + +void ASConsole::setErrorStream(std::ostream* errStreamPtr) +{ + errorStream = errStreamPtr; +} + +// build a vector of argv options +// the program path argv[0] is excluded +std::vector ASConsole::getArgvOptions(int argc, char** argv) +{ + if (argc > 0) + astyleExePath = getFullPathName(argv[0]); + std::vector argvOptions; + for (int i = 1; i < argc; i++) + { + argvOptions.emplace_back(argv[i]); + } + return argvOptions; +} + +std::string ASConsole::getParam(const std::string& arg, const char* op) +{ + return arg.substr(strlen(op)); +} + +void ASConsole::getTargetFilenames(std::string& targetFilename_, + std::vector& targetFilenameVector) const +{ + size_t beg = 0; + size_t sep = 0; + while (beg < targetFilename_.length()) + { + // find next target + sep = targetFilename_.find_first_of(",;", beg); + if (sep == std::string::npos) + sep = targetFilename_.length(); + std::string fileExtension = targetFilename_.substr(beg, sep - beg); + beg = sep + 1; + // remove whitespace + while (!fileExtension.empty() + && (fileExtension[0] == ' ' || fileExtension[0] == '\t')) + fileExtension = fileExtension.erase(0, 1); + while (!fileExtension.empty() + && (fileExtension[fileExtension.length() - 1] == ' ' + || fileExtension[fileExtension.length() - 1] == '\t')) + fileExtension = fileExtension.erase(fileExtension.length() - 1, 1); + if (!fileExtension.empty()) + targetFilenameVector.emplace_back(fileExtension); + } + if (targetFilenameVector.empty()) + { + fprintf(stderr, _("Missing filename in %s\n"), targetFilename_.c_str()); + error(); + } +} + +// initialize output end of line +void ASConsole::initializeOutputEOL(LineEndFormat lineEndFormat) +{ + assert(lineEndFormat == LINEEND_DEFAULT + || lineEndFormat == LINEEND_WINDOWS + || lineEndFormat == LINEEND_LINUX + || lineEndFormat == LINEEND_MACOLD); + + outputEOL = "\n"; + + if (lineEndFormat == LINEEND_WINDOWS) + outputEOL = "\r\n"; + else if (lineEndFormat == LINEEND_LINUX) + outputEOL = "\n"; + else if (lineEndFormat == LINEEND_MACOLD) + outputEOL = "\r"; +} + +// read a file into the stringstream 'in' +FileEncoding ASConsole::readFile(const std::string& fileName_, std::stringstream& in) const +{ + const int blockSize = 65536; // 64 KB + std::ifstream fin(fileName_.c_str(), std::ios::binary); + if (!fin) + error("Cannot open file", fileName_.c_str()); + char* data = new (std::nothrow) char[blockSize]; + if (data == nullptr) + error("Cannot allocate memory to open file", fileName_.c_str()); + fin.read(data, blockSize); + if (fin.bad()) + error("Cannot read file", fileName_.c_str()); + auto dataSize = static_cast(fin.gcount()); + FileEncoding encoding = detectEncoding(data, dataSize); + if (encoding == UTF_32BE || encoding == UTF_32LE) + error(_("Cannot process UTF-32 encoding"), fileName_.c_str()); + bool firstBlock = true; + bool isBigEndian = (encoding == UTF_16BE); + + while (dataSize != 0) + { + if (encoding == UTF_16LE || encoding == UTF_16BE) + { + // convert utf-16 to utf-8 + size_t utf8Size = encode.utf8LengthFromUtf16(data, dataSize, isBigEndian); + char* utf8Out = new (std::nothrow) char[utf8Size]; + if (utf8Out == nullptr) + error("Cannot allocate memory for utf-8 conversion", fileName_.c_str()); + size_t utf8Len = encode.utf16ToUtf8(data, dataSize, isBigEndian, firstBlock, utf8Out); + assert(utf8Len <= utf8Size); + in << std::string(utf8Out, utf8Len); + delete[] utf8Out; + } + else + in << std::string(data, dataSize); + fin.read(data, blockSize); + if (fin.bad()) + error("Cannot read file", fileName_.c_str()); + dataSize = static_cast(fin.gcount()); + firstBlock = false; + } + fin.close(); + delete[] data; + return encoding; +} + +void ASConsole::setIgnoreExcludeErrors(bool state) +{ ignoreExcludeErrors = state; } + +void ASConsole::setIgnoreExcludeErrorsAndDisplay(bool state) +{ ignoreExcludeErrors = state; ignoreExcludeErrorsDisplay = state; } + +void ASConsole::setIsFormattedOnly(bool state) +{ isFormattedOnly = state; } + +void ASConsole::setIsQuiet(bool state) +{ isQuiet = state; } + +void ASConsole::setIsRecursive(bool state) +{ isRecursive = state; } + +void ASConsole::setIsDryRun(bool state) +{ isDryRun = state; } + +void ASConsole::setRejectFormatWithDryRun(bool state) +{ rejectDryRunWithFormat = state; } + +void ASConsole::setIsVerbose(bool state) +{ isVerbose = state; } + +void ASConsole::setNoBackup(bool state) +{ noBackup = state; } + +void ASConsole::setOptionFileName(const std::string& name) +{ optionFileName = name; } + +void ASConsole::setOrigSuffix(const std::string& suffix) +{ origSuffix = suffix; } + +void ASConsole::setPreserveDate(bool state) +{ preserveDate = state; } + +void ASConsole::setProjectOptionFileName(const std::string& optfilepath) +{ projectOptionFileName = optfilepath; } + +void ASConsole::setStdPathIn(const std::string& path) +{ stdPathIn = path; } + +void ASConsole::setStdPathOut(const std::string& path) +{ stdPathOut = path; } + +void ASConsole::setAcceptEmptyInputFileList(bool state) +{ acceptEmptyFileList = state; } + + +/** + * Function to get the current directory using C++17 filesystem. + * + * @return The path of the current directory + */ +std::string ASConsole::getCurrentDirectory(const std::string& fileName_) const +{ + std::string currentDir = std::filesystem::current_path().string(); + if (currentDir.empty()) + error("Cannot find file", fileName_.c_str()); + return currentDir; +} + + +/** + * Function to resolve wildcards and recurse into sub directories using C++17 filesystem. + * The fileName vector is filled with the path and names of files to process. + * + * @param directory The path of the directory to be processed. + * @param wildcards A vector of wildcards to be processed (e.g. *.cpp). + */ +void ASConsole::getFileNames(const std::string& directory, const std::vector& wildcards) +{ + // If directory doesn't exist, handle error + if (!std::filesystem::exists(directory)) + error(_("Cannot open directory"), directory.c_str()); + + // Save files and subdirectories + std::vector subDirectory; + + for (const auto& entry : std::filesystem::directory_iterator(directory)) { + const std::string entryPath = entry.path().string(); + const std::string entryName = entry.path().filename().string(); + + // Skip hidden or read-only files + if (entryName[0] == '.' || !isWriteable(entry.path())) + continue; + + // If it's a directory and recursive is enabled + if (std::filesystem::is_directory(entry) && isRecursive) { + if (isPathExcluded(entryPath)) + printMsg(_("Exclude %s\n"), entryPath.substr(mainDirectoryLength)); + else + subDirectory.emplace_back(entryPath); + continue; + } + + // If it's a regular file + if (std::filesystem::is_regular_file(entry)) { + // Check exclude before wildcard to avoid "unmatched exclude" error + bool isExcluded = isPathExcluded(entryPath); + // Save file name if wildcard match + for (const std::string& wildcard : wildcards) { + if (wildcmp(wildcard.c_str(), entryName.c_str())) { + if (isExcluded) + printMsg(_("Exclude %s\n"), entryPath.substr(mainDirectoryLength)); + else + fileName.emplace_back(entryPath); + break; + } + } + } + } + + // Recurse into subdirectories + if (subDirectory.size() > 1) + std::sort(subDirectory.begin(), subDirectory.end()); + for (const auto& subDir : subDirectory) { + getFileNames(subDir, wildcards); + } +} + +/** + * Function to format a number according to the current locale using C++17. + * This formats positive integers only, no float. + * + * @param num The number to be formatted. + * @param lcid The LCID of the locale to be used for testing (Windows only). + * @return The formatted number. + */ +std::string ASConsole::getNumberFormat(int num, size_t /*lcid*/) const +{ + #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__) + // Compilers that don't support C++ locales should still support this assert. + // The C locale should be set but not the C++. + // This function is not necessary if the C++ locale is set. + // The locale().name() return value is not portable to all compilers. + assert(std::locale().name() == "C"); + #endif + + // convert num to a string + std::stringstream alphaNum; + alphaNum << num; + std::string number = alphaNum.str(); + if (useAscii) + return number; + + // Get C locale information + struct lconv* lc = localeconv(); + if (lc == nullptr) + return number; + + // Format the number using the grouping and separator from the locale + return getNumberFormat(num, lc->grouping, lc->thousands_sep); +} + +/** + * Helper function to check if a file is writable using C++17 filesystem. + * + * @param entry The filesystem entry to check + * @return true if the file is writable + */ +bool ASConsole::isWriteable(const std::filesystem::path& path) const +{ +#ifdef _WIN32 + // Windows implementation + DWORD attributes = GetFileAttributes(path.string().c_str()); + return !(attributes & FILE_ATTRIBUTE_READONLY); +#else + // Linux implementation + struct stat statbuf; + stat(path.c_str(), &statbuf); + return (statbuf.st_mode & S_IWUSR); +#endif +} + +/** + * Helper function to format a number according to the current locale. + * This formats positive integers only, no float. + * + * @param num The number to be formatted. + * @param groupingArg The grouping string from the locale. + * @param separator The thousands group separator from the locale. + * @return The formatted number. + */ +std::string ASConsole::getNumberFormat(int num, const char* groupingArg, const char* separator) const +{ + // convert num to a string + std::stringstream alphaNum; + alphaNum << num; + std::string number = alphaNum.str(); + // format the number from right to left + std::string formattedNum; + size_t ig = 0; // grouping index + int grouping = groupingArg[ig]; + int i = number.length(); + // check for no grouping + if (grouping == 0) + grouping = number.length(); + while (i > 0) + { + // extract a group of numbers + std::string group; + if (i < grouping) + group = number; + else + group = number.substr(i - grouping); + // update formatted number + formattedNum.insert(0, group); + i -= grouping; + if (i < 0) + i = 0; + if (i > 0) + formattedNum.insert(0, separator); + number.erase(i); + // update grouping + if (groupingArg[ig] != '\0' + && groupingArg[ig + 1] != '\0') + grouping = groupingArg[++ig]; + } + return formattedNum; +} + +#ifdef _WIN32 // Windows specific + +/** + * WINDOWS function to display the last system error. + */ +void ASConsole::displayLastError() +{ + LPSTR msgBuf; + DWORD lastError = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + nullptr, + lastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPSTR) &msgBuf, + 0, + nullptr + ); + // Display the string. + (*errorStream) << "Error (" << lastError << ") " << msgBuf << std::endl; + // Free the buffer. + LocalFree(msgBuf); +} + + +/** + * WINDOWS function to check for a HOME directory + * + * @param absPath The path to be evaluated. + * @returns true if absPath is HOME or is an invalid absolute + * path, false otherwise. + */ +bool ASConsole::isHomeOrInvalidAbsPath(const std::string& absPath) const +{ + const char* const env = getenv("USERPROFILE"); + if (env == nullptr) + return true; + + return (absPath.compare(env) == 0); +} + +/** + * WINDOWS function to open a HTML file in the default browser. + */ +void ASConsole::launchDefaultBrowser(const char* filePathIn /*nullptr*/) const +{ + struct stat statbuf; + const char* envPaths[] = { "PROGRAMFILES(X86)", "PROGRAMFILES" }; + size_t pathsLen = sizeof(envPaths) / sizeof(envPaths[0]); + std::string htmlDefaultPath; + for (size_t i = 0; i < pathsLen; i++) + { + const char* const envPath = getenv(envPaths[i]); + if (envPath == nullptr) + continue; + htmlDefaultPath = envPath; + if (htmlDefaultPath.length() > 0 + && htmlDefaultPath[htmlDefaultPath.length() - 1] == g_fileSeparator) + htmlDefaultPath.erase(htmlDefaultPath.length() - 1); + htmlDefaultPath.append("\\AStyle\\doc"); + if (stat(htmlDefaultPath.c_str(), &statbuf) == 0 && statbuf.st_mode & S_IFDIR) + break; + } + htmlDefaultPath.append("\\"); + + // build file path + std::string htmlFilePath; + if (filePathIn == nullptr) + htmlFilePath = htmlDefaultPath + "astyle.html"; + else + { + if (strpbrk(filePathIn, "\\/") == nullptr) + htmlFilePath = htmlDefaultPath + filePathIn; + else + htmlFilePath = filePathIn; + } + standardizePath(htmlFilePath); + if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG)) + { + printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str()); + return; + } + + SHELLEXECUTEINFO sei = { sizeof(sei), {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }; + sei.fMask = SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "open"; + sei.lpFile = htmlFilePath.c_str(); + sei.nShow = SW_SHOWNORMAL; + + // browser open will be bypassed in test programs + printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str()); + if (!bypassBrowserOpen) + { + int ret = ShellExecuteEx(&sei); + if (!ret) + error(_("Command execute failure"), htmlFilePath.c_str()); + } +} + +#else // Linux specific + +// Linux implementation now uses the cross-platform version defined in the Windows section + + + +// Linux implementation now uses the cross-platform version defined in the Windows section + + +// LINUX function to get the documentation file path prefix +// from the executable file path. +// Return the documentation path prefix or an empty std::string if failed. +std::string ASConsole::getHtmlInstallPrefix() const +{ + std::string astyleHtmlPrefix = astyleExePath; + size_t end = astyleHtmlPrefix.find("/bin/"); + if (end == std::string::npos) + return ""; + astyleHtmlPrefix = astyleHtmlPrefix.substr(0, end); + return astyleHtmlPrefix; +} + +// Linux implementation now uses the cross-platform version defined earlier + +/** + * LINUX function to check for a HOME directory + * + * @param absPath The path to be evaluated. + * @returns true if absPath is HOME or is an invalid absolute + * path, false otherwise. + */ +bool ASConsole::isHomeOrInvalidAbsPath(const std::string& absPath) const +{ + const char* const env = getenv("HOME"); + + if (env == nullptr) + return true; + + return (absPath.compare(env) == 0 || absPath == "/" ); +} + +/** + * LINUX function to open a HTML file in the default browser. + * Use xdg-open from freedesktop.org cross-desktop compatibility suite xdg-utils. + * see http://portland.freedesktop.org/wiki/ + * This is installed on most modern distributions. + */ +void ASConsole::launchDefaultBrowser(const char* filePathIn /*nullptr*/) const +{ +#ifdef __APPLE__ + std::string htmlDefaultPrefix = "/usr/local"; +#else + std::string htmlDefaultPrefix = "/usr"; +#endif + std::string htmlDefaultPath = htmlDefaultPrefix + "/share/doc/astyle/html/"; + std::string htmlDefaultFile = "astyle.html"; + std::string htmlFilePath; + struct stat statbuf; + + // build html path + if (filePathIn == nullptr) + { + std::string htmlPrefix = getHtmlInstallPrefix(); + if (htmlPrefix.empty()) + htmlFilePath = htmlDefaultPrefix + htmlDefaultPath + htmlDefaultFile; + else + htmlFilePath = htmlPrefix + htmlDefaultPath + htmlDefaultFile; + } + else + { + if (strpbrk(filePathIn, "\\/") == nullptr) + htmlFilePath = htmlDefaultPath + filePathIn; + else + htmlFilePath = filePathIn; + } + standardizePath(htmlFilePath); + if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG)) + { + printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str()); + return; + } + + // get search paths + const char* const envPaths = getenv("PATH"); + if (envPaths == nullptr) + error("Cannot read PATH environment variable", ""); + + // find xdg-open (usually in /usr/bin) + // Mac uses open instead +#ifdef __APPLE__ + const char* fileOpen = "open"; +#else + const char* fileOpen = "xdg-open"; +#endif + + std::string searchPath; + std::string pathsStr(envPaths); + std::istringstream pathStream(pathsStr); + std::string token; + + while (std::getline(pathStream, token, ':')) { + if (!token.empty()) { + searchPath = token; + if (searchPath.back() != g_fileSeparator) + searchPath.append(1, g_fileSeparator); + searchPath.append(fileOpen); + + if (std::filesystem::exists(searchPath) && + std::filesystem::is_regular_file(searchPath)) + break; + } + searchPath.clear(); + } + + if (searchPath.empty()) + error(_("Command is not installed"), fileOpen); + + // browser open will be bypassed in test programs + printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str()); + if (!bypassBrowserOpen) + { + execlp(fileOpen, fileOpen, htmlFilePath.c_str(), nullptr); + // execlp will NOT return if successful + error(_("Command execute failure"), fileOpen); + } +} + +#endif // _WIN32 + + +// function to get the full path name from the relative path name +// Return the full path name or an empty std::string if failed. +std::string ASConsole::getFullPathName(const std::string& relativePath) const +{ + return std::filesystem::absolute(std::filesystem::path(relativePath)).string(); +} + +/** + * Returns the parent directory of absPath. If absPath is not a valid absolute + * path or if it does not have a parent, an empty string is returned. + * + * @param absPath The initial directory. + * @return The parent directory of absPath, or an empty string if + * one cannot be found. + */ +std::string ASConsole::getParentDirectory(const std::string& absPath) const +{ + if (isHomeOrInvalidAbsPath(absPath)) + { + return std::string(); + } + size_t offset = absPath.size() - 1; + if (absPath[absPath.size() - 1] == g_fileSeparator) + { + offset -= 1; + } + size_t idx = absPath.rfind(g_fileSeparator, offset); + if (idx == std::string::npos) + { + return std::string(); + } + std::string str = absPath.substr(0, idx + 1); + return str; +} + +// get individual file names from the command-line file path +void ASConsole::getFilePaths(const std::string& filePath) +{ + fileName.clear(); + targetDirectory = std::string(); + targetFilename = std::string(); + std::vector targetFilenameVector; + + // separate directory and file name + size_t separator = filePath.find_last_of(g_fileSeparator); + if (separator == std::string::npos) + { + // if no directory is present, use the currently active directory + targetDirectory = getCurrentDirectory(filePath); + targetFilename = filePath; + mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator + } + else + { + targetDirectory = filePath.substr(0, separator); + targetFilename = filePath.substr(separator + 1); + mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator + } + + if (targetFilename.empty()) + { + fprintf(stderr, _("Missing filename in %s\n"), filePath.c_str()); + error(); + } + + // check filename for wildcards + hasWildcard = false; + if (targetFilename.find_first_of("*?") != std::string::npos) + hasWildcard = true; + + // If the filename is not quoted on Linux, bash will replace the + // wildcard instead of passing it to the program. + if (isRecursive && !hasWildcard) + { + fprintf(stderr, "%s\n", _("Recursive option with no wildcard")); +#ifndef _WIN32 + fprintf(stderr, "%s\n", _("Did you intend quote the filename")); +#endif + error(); + } + + bool hasMultipleTargets = false; + if (targetFilename.find_first_of(",;") != std::string::npos) + hasMultipleTargets = true; + + // display directory name for wildcard processing + if (hasWildcard) + { + printSeparatingLine(); + printMsg(_("Directory %s\n"), targetDirectory + g_fileSeparator + targetFilename); + } + + // clear exclude hits vector + size_t excludeHitsVectorSize = excludeHitsVector.size(); + for (size_t ix = 0; ix < excludeHitsVectorSize; ix++) + excludeHitsVector[ix] = false; + + // create a std::vector of paths and file names to process + if (hasWildcard || isRecursive || hasMultipleTargets) + { + getTargetFilenames(targetFilename, targetFilenameVector); + getFileNames(targetDirectory, targetFilenameVector); + } + else + { + + // verify a single file is not a directory (needed on Linux) + std::string entryFilepath = targetDirectory + g_fileSeparator + targetFilename; + + struct stat statbuf; + if (stat(entryFilepath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG)) + fileName.emplace_back(entryFilepath); + } + + // check for unprocessed excludes + bool excludeErr = false; + for (size_t ix = 0; ix < excludeHitsVector.size(); ix++) + { + if (!excludeHitsVector[ix]) + { + excludeErr = true; + if (!ignoreExcludeErrorsDisplay) + { + if (ignoreExcludeErrors) + printMsg(_("Exclude (unmatched) %s\n"), excludeVector[ix]); + else + fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str()); + } + else + { + if (!ignoreExcludeErrors) + fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str()); + } + } + } + + if (excludeErr && !ignoreExcludeErrors) + { + if (hasWildcard && !isRecursive) + fprintf(stderr, "%s\n", _("Did you intend to use --recursive")); + error(); + } + + // check if files were found (probably an input error if not) + if (fileName.empty() && !acceptEmptyFileList) + { + fprintf(stderr, _("No file to process %s\n"), filePath.c_str()); + if (hasWildcard && !isRecursive) + fprintf(stderr, "%s\n", _("Did you intend to use --recursive")); + error(); + } + + if (hasWildcard) + printSeparatingLine(); +} + +// Check if a file exists +bool ASConsole::fileExists(const char* file) const +{ + struct stat buf; + return (stat(file, &buf) == 0); +} + +bool ASConsole::fileNameVectorIsEmpty() const +{ + return fileNameVector.empty(); +} + +bool ASConsole::isOption(const std::string& arg, const char* op) +{ + return arg == op; +} + +bool ASConsole::isOption(const std::string& arg, const char* a, const char* b) +{ + return (isOption(arg, a) || isOption(arg, b)); +} + +bool ASConsole::isParamOption(const std::string& arg, const char* option) +{ + bool retVal = arg.compare(0, strlen(option), option) == 0; + // if comparing for short option, 2nd char of arg must be numeric + if (retVal && strlen(option) == 1 && arg.length() > 1) + if (!isdigit((unsigned char) arg[1])) + retVal = false; + return retVal; +} + +// compare a path to the exclude std::vector +// used for both directories and filenames +// updates the g_excludeHitsVector +// return true if a match +bool ASConsole::isPathExcluded(const std::string& subPath) +{ + for (size_t i = 0; i < excludeVector.size(); ++i) + { + std::string exclude = excludeVector[i]; + + if (subPath.length() < exclude.length()) + continue; + + size_t compareStart = subPath.length() - exclude.length(); + + if (compareStart > 0 && subPath[compareStart - 1] != g_fileSeparator) + continue; + + std::string compare = subPath.substr(compareStart); + + if (!g_isCaseSensitive) + { + std::transform(compare.begin(), compare.end(), compare.begin(), ::tolower); + std::transform(exclude.begin(), exclude.end(), exclude.begin(), ::tolower); + } + + if (compare == exclude) + { + excludeHitsVector[i] = true; + return true; + } + } + return false; +} + + +void ASConsole::printHelp() const +{ + std::cout << '\n'; + std::cout << " Artistic Style " << g_version << '\n'; + std::cout << " Maintained by: Andre Simon, Jim Pattee\n"; + std::cout << " Original Author: Tal Davidson\n"; + std::cout << '\n'; + std::cout << "Usage:\n"; + std::cout << "------\n"; + std::cout << " astyle [OPTIONS] File1 File2 File3 [...]\n"; + std::cout << '\n'; + std::cout << " astyle [OPTIONS] < Original > Beautified\n"; + std::cout << '\n'; + std::cout << " When indenting a specific file, the resulting indented file RETAINS\n"; + std::cout << " the original file-name. The original pre-indented file is renamed,\n"; + std::cout << " with a suffix of \'.orig\' added to the original filename.\n"; + std::cout << '\n'; + std::cout << " Wildcards (* and ?) may be used in the filename.\n"; + std::cout << " A \'recursive\' option can process directories recursively.\n"; + std::cout << " Multiple file extensions may be separated by a comma.\n"; + std::cout << '\n'; + std::cout << " By default, astyle is set up to indent with four spaces per indent,\n"; + std::cout << " a maximal indentation of 40 spaces inside continuous statements,\n"; + std::cout << " a minimum indentation of eight spaces inside conditional statements,\n"; + std::cout << " and NO formatting options.\n"; + std::cout << '\n'; + std::cout << "Options:\n"; + std::cout << "--------\n"; + std::cout << " This program follows the usual GNU command line syntax.\n"; + std::cout << " Long options (starting with '--') must be written one at a time.\n"; + std::cout << " Short options (starting with '-') may be appended together.\n"; + std::cout << " Thus, -bps4 is the same as -b -p -s4.\n"; + std::cout << '\n'; + std::cout << "Option Files:\n"; + std::cout << "-------------\n"; + std::cout << " Artistic Style looks for a default option file and/or a project\n"; + std::cout << " option file in the following order:\n"; + std::cout << " 1. The command line options have precedence.\n"; + std::cout << " 2. The project option file has precedence over the default file\n"; + std::cout << " o the file name indicated by the --project= command line option.\n"; + std::cout << " o the file named .astylerc or _ astylerc.\n"; + std::cout << " o the file name identified by ARTISTIC_STYLE_PROJECT_OPTIONS.\n"; + std::cout << " o the file is disabled by --project=none on the command line.\n"; + std::cout << " 3. The default option file that can be used for all projects.\n"; + std::cout << " o the file path indicated by the --options= command line option.\n"; + std::cout << " o the file path indicated by ARTISTIC_STYLE_OPTIONS.\n"; + std::cout << " o the file named .astylerc in the HOME directory (for Linux).\n"; + std::cout << " o the file name astylerc in the APPDATA directory (for Windows).\n"; + std::cout << " o the file is disabled by --project=none on the command line.\n"; + std::cout << " Long options within the option files may be written without '--'.\n"; + std::cout << " Line-end comments begin with a '#'.\n"; + std::cout << '\n'; + std::cout << "Disable Formatting:\n"; + std::cout << "-------------------\n"; + std::cout << " Disable Block\n"; + std::cout << " Blocks of code can be disabled with the comment tags *INDENT-OFF*\n"; + std::cout << " and *INDENT-ON*. It must be contained in a one-line comment.\n"; + std::cout << '\n'; + std::cout << " Disable Line\n"; + std::cout << " Padding of operators can be disabled on a single line using the\n"; + std::cout << " comment tag *NOPAD*. It must be contained in a line-end comment.\n"; + std::cout << '\n'; + std::cout << "Brace Style Options:\n"; + std::cout << "--------------------\n"; + std::cout << " default brace style\n"; + std::cout << " If no brace style is requested, the opening braces will not be\n"; + std::cout << " changed and closing braces will be broken from the preceding line.\n"; + std::cout << '\n'; + std::cout << " --style=allman OR --style=bsd OR --style=break OR -A1\n"; + std::cout << " Allman style formatting/indenting.\n"; + std::cout << " Broken braces.\n"; + std::cout << '\n'; + std::cout << " --style=java OR --style=attach OR -A2\n"; + std::cout << " Java style formatting/indenting.\n"; + std::cout << " Attached braces.\n"; + std::cout << '\n'; + std::cout << " --style=kr OR --style=k&r OR --style=k/r OR -A3\n"; + std::cout << " Kernighan & Ritchie style formatting/indenting.\n"; + std::cout << " Linux braces.\n"; + std::cout << '\n'; + std::cout << " --style=stroustrup OR -A4\n"; + std::cout << " Stroustrup style formatting/indenting.\n"; + std::cout << " Linux braces, with broken closing headers.\n"; + std::cout << '\n'; + std::cout << " --style=whitesmith OR -A5\n"; + std::cout << " Whitesmith style formatting/indenting.\n"; + std::cout << " Broken, indented braces.\n"; + std::cout << " Indented class blocks and switch blocks.\n"; + std::cout << '\n'; + std::cout << " --style=vtk OR -A15\n"; + std::cout << " VTK style formatting/indenting.\n"; + std::cout << " Broken, indented braces except for the opening braces.\n"; + std::cout << '\n'; + std::cout << " --style=ratliff OR --style=banner OR -A6\n"; + std::cout << " Ratliff style formatting/indenting.\n"; + std::cout << " Attached, indented braces.\n"; + std::cout << '\n'; + std::cout << " --style=gnu OR -A7\n"; + std::cout << " GNU style formatting/indenting.\n"; + std::cout << " Broken braces, indented blocks.\n"; + std::cout << '\n'; + std::cout << " --style=linux OR --style=knf OR -A8\n"; + std::cout << " Linux style formatting/indenting.\n"; + std::cout << " Linux braces, minimum conditional indent is one-half indent.\n"; + std::cout << '\n'; + std::cout << " --style=horstmann OR --style=run-in OR -A9\n"; + std::cout << " Horstmann style formatting/indenting.\n"; + std::cout << " Run-in braces, indented switches.\n"; + std::cout << '\n'; + std::cout << " --style=1tbs OR --style=otbs OR -A10\n"; + std::cout << " One True Brace Style formatting/indenting.\n"; + std::cout << " Linux braces, add braces to all conditionals.\n"; + std::cout << '\n'; + std::cout << " --style=google OR -A14\n"; + std::cout << " Google style formatting/indenting.\n"; + std::cout << " Attached braces, indented class modifiers.\n"; + std::cout << '\n'; + std::cout << " --style=mozilla OR -A16\n"; + std::cout << " Mozilla style formatting/indenting.\n"; + std::cout << " Linux braces, with broken braces for structs and enums,\n"; + std::cout << " and attached braces for namespaces.\n"; + std::cout << '\n'; + std::cout << " --style=webkit OR -A17\n"; + std::cout << " WebKit style formatting/indenting.\n"; + std::cout << " Linux braces, with attached closing headers.\n"; + std::cout << '\n'; + std::cout << " --style=pico OR -A11\n"; + std::cout << " Pico style formatting/indenting.\n"; + std::cout << " Run-in opening braces and attached closing braces.\n"; + std::cout << " Uses keep one line blocks and keep one line statements.\n"; + std::cout << '\n'; + std::cout << " --style=lisp OR -A12\n"; + std::cout << " Lisp style formatting/indenting.\n"; + std::cout << " Attached opening braces and attached closing braces.\n"; + std::cout << " Uses keep one line statements.\n"; + std::cout << '\n'; + std::cout << "Tab Options:\n"; + std::cout << "------------\n"; + std::cout << " default indent option\n"; + std::cout << " If no indentation option is set, the default\n"; + std::cout << " option of 4 spaces per indent will be used.\n"; + std::cout << '\n'; + std::cout << " --indent=spaces=# OR -s#\n"; + std::cout << " Indent using # spaces per indent. Not specifying #\n"; + std::cout << " will result in a default of 4 spaces per indent.\n"; + std::cout << '\n'; + std::cout << " --indent=tab OR --indent=tab=# OR -t OR -t#\n"; + std::cout << " Indent using tab characters, assuming that each\n"; + std::cout << " indent is # spaces long. Not specifying # will result\n"; + std::cout << " in a default assumption of 4 spaces per indent.\n"; + std::cout << '\n'; + std::cout << " --indent=force-tab=# OR -T#\n"; + std::cout << " Indent using tab characters, assuming that each\n"; + std::cout << " indent is # spaces long. Force tabs to be used in areas\n"; + std::cout << " AStyle would prefer to use spaces.\n"; + std::cout << '\n'; + std::cout << " --indent=force-tab-x=# OR -xT#\n"; + std::cout << " Allows the tab length to be set to a length that is different\n"; + std::cout << " from the indent length. This may cause the indentation to be\n"; + std::cout << " a mix of both spaces and tabs. This option sets the tab length.\n"; + std::cout << '\n'; + std::cout << "Brace Modify Options:\n"; + std::cout << "---------------------\n"; + std::cout << " --attach-namespaces OR -xn\n"; + std::cout << " Attach braces to a namespace statement.\n"; + std::cout << '\n'; + std::cout << " --attach-classes OR -xc\n"; + std::cout << " Attach braces to a class statement.\n"; + std::cout << '\n'; + std::cout << " --attach-inlines OR -xl\n"; + std::cout << " Attach braces to class inline function definitions.\n"; + std::cout << '\n'; + std::cout << " --attach-extern-c OR -xk\n"; + std::cout << " Attach braces to an extern \"C\" statement.\n"; + std::cout << '\n'; + std::cout << " --attach-closing-while OR -xV\n"; + std::cout << " Attach closing while of do-while to the closing brace.\n"; + std::cout << '\n'; + std::cout << "Indentation Options:\n"; + std::cout << "--------------------\n"; + std::cout << " --indent-classes OR -C\n"; + std::cout << " Indent 'class' blocks so that the entire block is indented.\n"; + std::cout << '\n'; + std::cout << " --indent-modifiers OR -xG\n"; + std::cout << " Indent 'class' access modifiers, 'public:', 'protected:' or\n"; + std::cout << " 'private:', one half indent. The rest of the class is not\n"; + std::cout << " indented. \n"; + std::cout << '\n'; + std::cout << " --indent-switches OR -S\n"; + std::cout << " Indent 'switch' blocks, so that the inner 'case XXX:'\n"; + std::cout << " headers are indented in relation to the switch block.\n"; + std::cout << '\n'; + std::cout << " --indent-cases OR -K\n"; + std::cout << " Indent case blocks from the 'case XXX:' headers.\n"; + std::cout << " Case statements not enclosed in blocks are NOT indented.\n"; + std::cout << '\n'; + std::cout << " --indent-namespaces OR -N\n"; + std::cout << " Indent the contents of namespace blocks.\n"; + std::cout << '\n'; + std::cout << " --indent-after-parens OR -xU\n"; + std::cout << " Indent, instead of align, continuation lines following lines\n"; + std::cout << " that contain an opening paren '(' or an assignment '='. \n"; + std::cout << '\n'; + std::cout << " --indent-continuation=# OR -xt#\n"; + std::cout << " Indent continuation lines an additional # indents.\n"; + std::cout << " The valid values are 0 thru 4 indents.\n"; + std::cout << " The default value is 1 indent.\n"; + std::cout << '\n'; + std::cout << " --indent-labels OR -L\n"; + std::cout << " Indent labels so that they appear one indent less than\n"; + std::cout << " the current indentation level, rather than being\n"; + std::cout << " flushed completely to the left (which is the default).\n"; + std::cout << '\n'; + std::cout << " --indent-preproc-block OR -xW\n"; + std::cout << " Indent preprocessor blocks at brace level 0.\n"; + std::cout << " Without this option the preprocessor block is not indented.\n"; + std::cout << '\n'; + std::cout << " --indent-preproc-cond OR -xw\n"; + std::cout << " Indent preprocessor conditional statements #if/#else/#endif\n"; + std::cout << " to the same level as the source code.\n"; + std::cout << '\n'; + std::cout << " --indent-preproc-define OR -w\n"; + std::cout << " Indent multi-line preprocessor #define statements.\n"; + std::cout << '\n'; + std::cout << " --indent-col1-comments OR -Y\n"; + std::cout << " Indent line comments that start in column one.\n"; + std::cout << '\n'; + std::cout << " --indent-lambda\n"; + std::cout << " Indent C++ lambda functions (experimental)\n"; + std::cout << '\n'; + std::cout << " --min-conditional-indent=# OR -m#\n"; + std::cout << " Indent a minimal # spaces in a continuous conditional\n"; + std::cout << " belonging to a conditional header.\n"; + std::cout << " The valid values are:\n"; + std::cout << " 0 - no minimal indent.\n"; + std::cout << " 1 - indent at least one additional indent.\n"; + std::cout << " 2 - indent at least two additional indents.\n"; + std::cout << " 3 - indent at least one-half an additional indent.\n"; + std::cout << " The default value is 2, two additional indents.\n"; + std::cout << '\n'; + std::cout << " --max-continuation-indent=# OR -M#\n"; + std::cout << " Indent a maximal # spaces in a continuation line,\n"; + std::cout << " relative to the previous line.\n"; + std::cout << " The minimum and default value is 40.\n"; + std::cout << '\n'; + std::cout << "Padding Options:\n"; + std::cout << "----------------\n"; + std::cout << " --break-blocks OR -f\n"; + std::cout << " Insert empty lines around unrelated blocks, labels, classes, ...\n"; + std::cout << '\n'; + std::cout << " --break-blocks=all OR -F\n"; + std::cout << " Like --break-blocks, except also insert empty lines\n"; + std::cout << " around closing headers (e.g. 'else', 'catch', ...), structs,\n"; + std::cout << " and functions.\n"; + std::cout << '\n'; + std::cout << " --pad-oper OR -p\n"; + std::cout << " Insert space padding around operators.\n"; + std::cout << '\n'; + std::cout << " --pad-comma OR -xg\n"; + std::cout << " Insert space padding after commas.\n"; + std::cout << '\n'; + + std::cout << " --pad-include\n"; + std::cout << " Insert space padding after include directives.\n"; + std::cout << '\n'; + std::cout << " --pad-include=none\n"; + std::cout << " Remove space padding after include directives.\n"; + std::cout << '\n'; + + std::cout << " --pad-negation\n"; + std::cout << " Insert space padding after negations.\n"; + std::cout << '\n'; + std::cout << " --pad-negation=before\n"; + std::cout << " Insert space padding also before negations.\n"; + std::cout << '\n'; + std::cout << " --pad-paren OR -P\n"; + std::cout << " Insert space padding around parenthesis on both the outside\n"; + std::cout << " and the inside.\n"; + std::cout << '\n'; + std::cout << " --pad-paren-out OR -d\n"; + std::cout << " Insert space padding around parenthesis on the outside only.\n"; + std::cout << '\n'; + std::cout << " --pad-first-paren-out OR -xd\n"; + std::cout << " Insert space padding around first parenthesis in a series on\n"; + std::cout << " the outside only.\n"; + std::cout << '\n'; + std::cout << " --pad-paren-in OR -D\n"; + std::cout << " Insert space padding around parenthesis on the inside only.\n"; + std::cout << '\n'; + std::cout << " --pad-empty-paren OR -xo\n"; + std::cout << " Apply padding to empty pairs of parentheses; combine with other\n"; + std::cout << " parenthesis padding options.\n"; + std::cout << '\n'; + std::cout << " --pad-header OR -H\n"; + std::cout << " Insert space padding after paren headers (e.g. 'if', 'for'...).\n"; + std::cout << '\n'; + std::cout << " --unpad-paren OR -U\n"; + std::cout << " Remove unnecessary space padding around parenthesis. This\n"; + std::cout << " can be used in combination with the 'pad' options above.\n"; + std::cout << '\n'; + + std::cout << " --pad-brackets\n"; + std::cout << " Insert space padding around square brackets on both the outside\n"; + std::cout << " and the inside.\n"; + std::cout << '\n'; + std::cout << " --pad-brackets-in\n"; + std::cout << " Insert space padding around square brackets on the inside only.\n"; + std::cout << '\n'; + std::cout << " --pad-brackets-out\n"; + std::cout << " Insert space padding around square brackets on the outside only.\n"; + std::cout << '\n'; + std::cout << " --unpad-brackets\n"; + std::cout << " Remove unnecessary space padding around square brackets.\n"; + std::cout << '\n'; + + std::cout << " --delete-empty-lines OR -xe\n"; + std::cout << " Delete empty lines within a function or method.\n"; + std::cout << " It will NOT delete lines added by the break-blocks options.\n"; + std::cout << '\n'; + std::cout << " --fill-empty-lines OR -E\n"; + std::cout << " Fill empty lines with the white space of their\n"; + std::cout << " previous lines.\n"; + std::cout << '\n'; + std::cout << " --squeeze-lines=#\n"; + std::cout << " Remove superfluous empty lines exceeding the given number.\n"; + std::cout << '\n'; + std::cout << " --squeeze-ws\n"; + std::cout << " Remove superfluous whitespace.\n"; + std::cout << '\n'; + std::cout << " --preserve-ws\n"; + std::cout << " Preserve whitespace near comma operators if squeeze-ws is not set.\n"; + std::cout << '\n'; + std::cout << " --align-pointer=type OR -k1\n"; + std::cout << " --align-pointer=middle OR -k2\n"; + std::cout << " --align-pointer=name OR -k3\n"; + std::cout << " Attach a pointer or reference operator (*, &, or ^) to either\n"; + std::cout << " the operator type (left), middle, or operator name (right).\n"; + std::cout << " To align the reference separately use --align-reference.\n"; + std::cout << '\n'; + std::cout << " --align-reference=none OR -W0\n"; + std::cout << " --align-reference=type OR -W1\n"; + std::cout << " --align-reference=middle OR -W2\n"; + std::cout << " --align-reference=name OR -W3\n"; + std::cout << " Attach a reference operator (&) to either\n"; + std::cout << " the operator type (left), middle, or operator name (right).\n"; + std::cout << " If not set, follow pointer alignment.\n"; + std::cout << '\n'; + std::cout << "Formatting Options:\n"; + std::cout << "-------------------\n"; + std::cout << " --break-closing-braces OR -y\n"; + std::cout << " Break braces before closing headers (e.g. 'else', 'catch', ...).\n"; + std::cout << " Use with --style=java, --style=kr, --style=stroustrup,\n"; + std::cout << " --style=linux, or --style=1tbs.\n"; + std::cout << '\n'; + std::cout << " --break-elseifs OR -e\n"; + std::cout << " Break 'else if()' statements into two different lines.\n"; + std::cout << '\n'; + std::cout << " --break-one-line-headers OR -xb\n"; + std::cout << " Break one line headers (e.g. 'if', 'while', 'else', ...) from a\n"; + std::cout << " statement residing on the same line.\n"; + std::cout << '\n'; + std::cout << " --add-braces OR -j\n"; + std::cout << " Add braces to unbraced one line conditional statements.\n"; + std::cout << '\n'; + std::cout << " --add-one-line-braces OR -J\n"; + std::cout << " Add one line braces to unbraced one line conditional\n"; + std::cout << " statements.\n"; + std::cout << '\n'; + std::cout << " --remove-braces OR -xj\n"; + std::cout << " Remove braces from a braced one line conditional statements.\n"; + std::cout << '\n'; + std::cout << " --break-return-type OR -xB\n"; + std::cout << " --break-return-type-decl OR -xD\n"; + std::cout << " Break the return type from the function name. Options are\n"; + std::cout << " for the function definitions and the function declarations.\n"; + std::cout << '\n'; + std::cout << " --attach-return-type OR -xf\n"; + std::cout << " --attach-return-type-decl OR -xh\n"; + std::cout << " Attach the return type to the function name. Options are\n"; + std::cout << " for the function definitions and the function declarations.\n"; + std::cout << '\n'; + std::cout << " --keep-one-line-blocks OR -O\n"; + std::cout << " Don't break blocks residing completely on one line.\n"; + std::cout << '\n'; + std::cout << " --keep-one-line-statements OR -o\n"; + std::cout << " Don't break lines containing multiple statements into\n"; + std::cout << " multiple single-statement lines.\n"; + std::cout << '\n'; + std::cout << " --convert-tabs OR -c\n"; + std::cout << " Convert tabs to the appropriate number of spaces.\n"; + std::cout << '\n'; + std::cout << " --close-templates OR -xy\n"; + std::cout << " Close ending angle brackets on template definitions.\n"; + std::cout << '\n'; + std::cout << " --remove-comment-prefix OR -xp\n"; + std::cout << " Remove the leading '*' prefix on multi-line comments and\n"; + std::cout << " indent the comment text one indent.\n"; + std::cout << '\n'; + std::cout << " --max-code-length=# OR -xC#\n"; + std::cout << " --break-after-logical OR -xL\n"; + std::cout << " max-code-length=# will break the line if it exceeds more than\n"; + std::cout << " # characters. The valid values are 50 thru 200.\n"; + std::cout << " If the line contains logical conditionals they will be placed\n"; + std::cout << " first on the new line. The option break-after-logical will\n"; + std::cout << " cause the logical conditional to be placed last on the\n"; + std::cout << " previous line.\n"; + std::cout << '\n'; + std::cout << " --mode=c\n"; + std::cout << " Indent a C or C++ source file (this is the default).\n"; + std::cout << '\n'; + std::cout << " --mode=java\n"; + std::cout << " Indent a Java source file.\n"; + std::cout << '\n'; + std::cout << " --mode=cs\n"; + std::cout << " Indent a C# source file.\n"; + std::cout << '\n'; + std::cout << " --mode=objc\n"; + std::cout << " Indent an Objective-C source file.\n"; + std::cout << '\n'; + std::cout << " --mode=js\n"; + std::cout << " Indent a JavaScript source file.\n"; + std::cout << '\n'; + std::cout << " --mode=gsc\n"; + std::cout << " Indent a GSC source file.\n"; + std::cout << '\n'; + std::cout << "Objective-C Options:\n"; + std::cout << "--------------------\n"; + std::cout << " --pad-method-prefix OR -xQ\n"; + std::cout << " Insert space padding after the '-' or '+' Objective-C\n"; + std::cout << " method prefix.\n"; + std::cout << '\n'; + std::cout << " --unpad-method-prefix OR -xR\n"; + std::cout << " Remove all space padding after the '-' or '+' Objective-C\n"; + std::cout << " method prefix.\n"; + std::cout << '\n'; + std::cout << " --pad-return-type OR -xq\n"; + std::cout << " Insert space padding after the Objective-C return type.\n"; + std::cout << '\n'; + std::cout << " --unpad-return-type OR -xr\n"; + std::cout << " Remove all space padding after the Objective-C return type.\n"; + std::cout << '\n'; + std::cout << " --pad-param-type OR -xS\n"; + std::cout << " Insert space padding after the Objective-C param type.\n"; + std::cout << '\n'; + std::cout << " --unpad-param-type OR -xs\n"; + std::cout << " Remove all space padding after the Objective-C param type.\n"; + std::cout << '\n'; + std::cout << " --align-method-colon OR -xM\n"; + std::cout << " Align the colons in an Objective-C method definition.\n"; + std::cout << '\n'; + std::cout << " --pad-method-colon=none OR -xP\n"; + std::cout << " --pad-method-colon=all OR -xP1\n"; + std::cout << " --pad-method-colon=after OR -xP2\n"; + std::cout << " --pad-method-colon=before OR -xP3\n"; + std::cout << " Add or remove space padding before or after the colons in an\n"; + std::cout << " Objective-C method call.\n"; + std::cout << '\n'; + std::cout << "Other Options:\n"; + std::cout << "--------------\n"; + std::cout << " --suffix=####\n"; + std::cout << " Append the suffix #### instead of '.orig' to original filename.\n"; + std::cout << '\n'; + std::cout << " --suffix=none OR -n\n"; + std::cout << " Do not retain a backup of the original file.\n"; + std::cout << '\n'; + std::cout << " --recursive OR -r OR -R\n"; + std::cout << " Process subdirectories recursively.\n"; + std::cout << '\n'; + std::cout << " --dry-run\n"; + std::cout << " Perform a trial run with no changes made to check for formatting.\n"; + std::cout << '\n'; + std::cout << " --error-on-changes\n"; + std::cout << " With --dry-run: Report any file reformat as error.\n"; + std::cout << '\n'; + std::cout << " --exclude=####\n"; + std::cout << " Specify a file or directory #### to be excluded from processing.\n"; + std::cout << '\n'; + std::cout << " --ignore-exclude-errors OR -i\n"; + std::cout << " Allow processing to continue if there are errors in the exclude=####\n"; + std::cout << " options. It will display the unmatched excludes.\n"; + std::cout << '\n'; + std::cout << " --ignore-exclude-errors-x OR -xi\n"; + std::cout << " Allow processing to continue if there are errors in the exclude=####\n"; + std::cout << " options. It will NOT display the unmatched excludes.\n"; + std::cout << '\n'; + std::cout << " --accept-empty-list\n"; + std::cout << " With wildcard or -R: Do not report an empty input file list as error.\n"; + std::cout << '\n'; + std::cout << " --errors-to-stdout OR -X\n"; + std::cout << " Print errors and help information to standard-output rather than\n"; + std::cout << " to standard-error.\n"; + std::cout << '\n'; + std::cout << " --preserve-date OR -Z\n"; + std::cout << " Preserve the original file's date and time modified. The time\n"; + std::cout << " modified will be changed a few micro seconds to force a compile.\n"; + std::cout << '\n'; + std::cout << " --verbose OR -v\n"; + std::cout << " Verbose mode. Extra informational messages will be displayed.\n"; + std::cout << '\n'; + std::cout << " --formatted OR -Q\n"; + std::cout << " Formatted display mode. Display only the files that have been\n"; + std::cout << " formatted.\n"; + std::cout << '\n'; + std::cout << " --quiet OR -q\n"; + std::cout << " Quiet mode. Suppress all output except error messages.\n"; + std::cout << '\n'; + std::cout << " --lineend=windows OR -z1\n"; + std::cout << " --lineend=linux OR -z2\n"; + std::cout << " --lineend=macold OR -z3\n"; + std::cout << " Force use of the specified line end style. Valid options\n"; + std::cout << " are windows (CRLF), linux (LF), and macold (CR).\n"; + std::cout << '\n'; + std::cout << "Command Line Only:\n"; + std::cout << "------------------\n"; + std::cout << " --options=####\n"; + std::cout << " --options=none\n"; + std::cout << " Specify a default option file #### to read and use.\n"; + std::cout << " It must contain a file path and a file name.\n"; + std::cout << " 'none' disables the default option file.\n"; + std::cout << '\n'; + std::cout << " --project\n"; + std::cout << " --project=####\n"; + std::cout << " --project=none\n"; + std::cout << " Specify a project option file #### to read and use.\n"; + std::cout << " It must contain a file name only, without a directory path.\n"; + std::cout << " The file should be included in the project top-level directory.\n"; + std::cout << " The default file name is .astylerc or _astylerc.\n"; + std::cout << " 'none' disables the project or environment variable file.\n"; + std::cout << '\n'; + std::cout << " --ascii OR -I\n"; + std::cout << " The displayed output will be ascii characters only.\n"; + std::cout << '\n'; + std::cout << " --version OR -V\n"; + std::cout << " Print version number.\n"; + std::cout << '\n'; + std::cout << " --help OR -h OR -?\n"; + std::cout << " Print this help message.\n"; + std::cout << '\n'; + std::cout << " --html OR -! (deprecated)\n"; + std::cout << " Open the HTML help file \"astyle.html\" in the default browser.\n"; + std::cout << " The documentation must be installed in the standard install path.\n"; + std::cout << '\n'; + std::cout << " --html=#### (deprecated)\n"; + std::cout << " Open a HTML help file in the default browser using the file path\n"; + std::cout << " ####. The path may include a directory path and a file name, or a\n"; + std::cout << " file name only. Paths containing spaces must be enclosed in quotes.\n"; + std::cout << '\n'; + std::cout << " --stdin=####\n"; + std::cout << " Use the file path #### as input to single file formatting.\n"; + std::cout << " This is a replacement for redirection.\n"; + std::cout << '\n'; + std::cout << " --stdout=####\n"; + std::cout << " Use the file path #### as output from single file formatting.\n"; + std::cout << " This is a replacement for redirection.\n"; + std::cout << '\n'; + std::cout << '\n'; +} + +/** + * Process files in the fileNameVector. + */ +void ASConsole::processFiles() +{ + if (isVerbose) + printVerboseHeader(); + + clock_t startTime = clock(); // start time of file formatting + + // loop thru input fileNameVector and process the files + for (const std::string& fileNameVectorName : fileNameVector) + { + getFilePaths(fileNameVectorName); + + // loop thru fileName vector formatting the files + for (const std::string& file : fileName) + { + if (!stringEndsWith(file, origSuffix)) + formatFile(file); + } + + } + + // files are processed, display stats + if (isVerbose) + printVerboseStats(startTime); + + if (isDryRun && rejectDryRunWithFormat && filesFormatted > 0) + { + exit(EXIT_FAILURE); + } +} + +// process options from the command line and option files +// build the vectors fileNameVector, excludeVector, optionsVector, +// projectOptionsVector and fileOptionsVector +void ASConsole::processOptions(const std::vector& argvOptions) +{ + bool ok = true; + bool optionFileRequired = false; + bool shouldParseOptionFile = true; + bool projectOptionFileRequired = false; + bool shouldParseProjectOptionFile = true; + std::string projectOptionArg; // save for display + + // get command line options + for (std::string arg : argvOptions) + { + if (isOption(arg, "-I") + || isOption(arg, "--ascii")) + { + useAscii = true; + setlocale(LC_ALL, "C"); // use English decimal indicator + localizer.setLanguageFromName("en"); + } + else if (isOption(arg, "--options=none")) + { + optionFileRequired = false; + shouldParseOptionFile = false; + optionFileName = ""; + } + else if (isParamOption(arg, "--options=")) + { + optionFileName = getParam(arg, "--options="); + standardizePath(optionFileName); + optionFileName = getFullPathName(optionFileName); + optionFileRequired = true; + } + else if (isOption(arg, "--project=none")) + { + projectOptionFileRequired = false; + shouldParseProjectOptionFile = false; + setProjectOptionFileName(""); + } + else if (isParamOption(arg, "--project=")) + { + projectOptionFileName = getParam(arg, "--project="); + standardizePath(projectOptionFileName); + projectOptionFileRequired = true; + shouldParseProjectOptionFile = false; + projectOptionArg = projectOptionFileName; + } + else if (isOption(arg, "--project")) + { + projectOptionFileName = ".astylerc"; + projectOptionFileRequired = true; + shouldParseProjectOptionFile = false; + projectOptionArg = projectOptionFileName; + } + else if (isOption(arg, "-h") + || isOption(arg, "--help") + || isOption(arg, "-?")) + { + printHelp(); + exit(EXIT_SUCCESS); + } + else if (isOption(arg, "-!") + || isOption(arg, "--html")) + { + launchDefaultBrowser(); + exit(EXIT_SUCCESS); + } + else if (isParamOption(arg, "--html=")) + { + std::string htmlFilePath = getParam(arg, "--html="); + launchDefaultBrowser(htmlFilePath.c_str()); + exit(EXIT_SUCCESS); + } + else if (isOption(arg, "-V") + || isOption(arg, "--version")) + { + printf("Artistic Style Version %s\n", g_version); + exit(EXIT_SUCCESS); + } + else if (isParamOption(arg, "--stdin=")) + { + std::string path = getParam(arg, "--stdin="); + standardizePath(path); + setStdPathIn(path); + } + else if (isParamOption(arg, "--stdout=")) + { + std::string path = getParam(arg, "--stdout="); + standardizePath(path); + setStdPathOut(path); + } + else if (arg[0] == '-') + { + optionsVector.emplace_back(arg); + } + else // file-name + { + standardizePath(arg); + fileNameVector.emplace_back(arg); + } + } + + // get option file path and name + if (shouldParseOptionFile) + { + if (optionFileName.empty()) + { + const char* const env = getenv("ARTISTIC_STYLE_OPTIONS"); + if (env != nullptr) + { + setOptionFileName(env); + standardizePath(optionFileName); + optionFileName = getFullPathName(optionFileName); + } + } + // for Linux + if (optionFileName.empty()) + { + const char* const env = getenv("HOME"); + if (env != nullptr) + { + std::string name = std::string(env) + "/.astylerc"; + if (fileExists(name.c_str())) + setOptionFileName(name); + } + } + // for Windows + if (optionFileName.empty()) + { + const char* const env = getenv("APPDATA"); + if (env != nullptr) + { + std::string name = std::string(env) + "\\astylerc"; + if (fileExists(name.c_str())) + setOptionFileName(name); + } + } + + } + + // find project option file + if (projectOptionFileRequired) + { + std::string optfilepath = findProjectOptionFilePath(projectOptionFileName); + if (optfilepath.empty() || projectOptionArg.empty()) + error(_("Cannot open project option file"), projectOptionArg.c_str()); + standardizePath(optfilepath); + setProjectOptionFileName(optfilepath); + } + if (shouldParseProjectOptionFile) + { + const char* const env = getenv("ARTISTIC_STYLE_PROJECT_OPTIONS"); + if (env != nullptr) + { + std::string optfilepath = findProjectOptionFilePath(env); + standardizePath(optfilepath); + setProjectOptionFileName(optfilepath); + } + } + + ASOptions options(formatter, *this); + if (!optionFileName.empty()) + { + std::stringstream optionsIn; + if (!fileExists(optionFileName.c_str())) + error(_("Cannot open default option file"), optionFileName.c_str()); + FileEncoding encoding = readFile(optionFileName, optionsIn); + // bypass a BOM, all BOMs have been converted to utf-8 + if (encoding == UTF_8BOM || encoding == UTF_16LE || encoding == UTF_16BE) + { + char buf[4]; + optionsIn.get(buf, 4); + assert(strcmp(buf, "\xEF\xBB\xBF") == 0); + } + options.importOptions(optionsIn, fileOptionsVector); + ok = options.parseOptions(fileOptionsVector); + } + else if (optionFileRequired) + error(_("Cannot open default option file"), optionFileName.c_str()); + + if (!ok) + { + (*errorStream) << options.getOptionErrors(); + (*errorStream) << _("For help on options type 'astyle -h'") << '\n'; + error(); + } + + if (!projectOptionFileName.empty()) + { + std::stringstream projectOptionsIn; + if (!fileExists(projectOptionFileName.c_str())) + error(_("Cannot open project option file"), projectOptionFileName.c_str()); + FileEncoding encoding = readFile(projectOptionFileName, projectOptionsIn); + // bypass a BOM, all BOMs have been converted to utf-8 + if (encoding == UTF_8BOM || encoding == UTF_16LE || encoding == UTF_16BE) + { + char buf[4]; + projectOptionsIn.get(buf, 4); + assert(strcmp(buf, "\xEF\xBB\xBF") == 0); + } + options.importOptions(projectOptionsIn, projectOptionsVector); + ok = options.parseOptions(projectOptionsVector); + } + + if (!ok) + { + (*errorStream) << options.getOptionErrors(); + (*errorStream) << _("For help on options type 'astyle -h'") << '\n'; + error(); + } + + // parse the command line options vector for errors + ok = options.parseOptions(optionsVector); + if (!ok) + { + (*errorStream) << options.getOptionErrors(); + (*errorStream) << _("For help on options type 'astyle -h'") << '\n'; + error(); + } +} + +// remove a file and check for an error +void ASConsole::removeFile(const char* fileName_, const char* errMsg) const +{ + if (remove(fileName_) != 0) + { + if (errno == ENOENT) // no file is OK + errno = 0; + if (errno) + { + perror("errno message"); + error(errMsg, fileName_); + } + } +} + +// rename a file and check for an error +void ASConsole::renameFile(const char* oldFileName, const char* newFileName, const char* errMsg) const +{ + int result = rename(oldFileName, newFileName); + if (result != 0) + { + // if file still exists the remove needs more time - retry + if (errno == EEXIST) + { + errno = 0; + waitForRemove(newFileName); + result = rename(oldFileName, newFileName); + } + if (result != 0) + { + perror("errno message"); + error(errMsg, oldFileName); + } + } +} + +// make sure file separators are correct type (Windows or Linux) +// remove ending file separator +// remove beginning file separator if requested and NOT a complete file path +void ASConsole::standardizePath(std::string& path, bool removeBeginningSeparator /*false*/) const +{ + + // make sure separators are correct type (Windows or Linux) + for (size_t i = 0; i < path.length(); i++) + { + i = path.find_first_of("/\\", i); + if (i == std::string::npos) + break; + path[i] = g_fileSeparator; + } + // remove beginning separator if requested + if (removeBeginningSeparator && (path[0] == g_fileSeparator)) + path.erase(0, 1); +} + +void ASConsole::printMsg(const char* msg, const std::string& data) const +{ + if (isQuiet) + return; + printf(msg, data.c_str()); +} + +void ASConsole::printSeparatingLine() const +{ + std::string line; + for (size_t i = 0; i < 60; i++) + line.append("-"); + printMsg("%s\n", line); +} + +void ASConsole::printVerboseHeader() const +{ + assert(isVerbose); + if (isQuiet) + return; + // get the date + time_t lt; + char str[20]; + lt = time(nullptr); + struct tm* ptr = localtime(<); + strftime(str, 20, "%x", ptr); + // print the header + // 60 is the length of the separator in printSeparatingLine() + std::string header = "Artistic Style " + std::string(g_version); + size_t numSpaces = 60 - header.length() - strlen(str); + header.append(numSpaces, ' '); + header.append(str); + header.append("\n"); + printf("%s", header.c_str()); + // print option files + if (!optionFileName.empty()) + printf(_("Default option file %s\n"), optionFileName.c_str()); + + if (!projectOptionFileName.empty()) + printf(_("Project option file %s\n"), projectOptionFileName.c_str()); +} + +void ASConsole::printVerboseStats(clock_t startTime) const +{ + assert(isVerbose); + if (isQuiet) + return; + if (hasWildcard) + printSeparatingLine(); + std::string formatted = getNumberFormat(filesFormatted); + std::string unchanged = getNumberFormat(filesUnchanged); + printf(_(" %s formatted %s unchanged "), formatted.c_str(), unchanged.c_str()); + + // show processing time + clock_t stopTime = clock(); + double secs = (stopTime - startTime) / double(CLOCKS_PER_SEC); + if (secs < 60) + { + if (secs < 2.0) + printf("%.2f", secs); + else if (secs < 20.0) + printf("%.1f", secs); + else + printf("%.0f", secs); + printf("%s", _(" seconds ")); + } + else + { + // show minutes and seconds if time is greater than one minute + int min = (int) secs / 60; + secs -= min * 60; + // NOTE: lround is not supported by MinGW and Embarcadero + int minsec = int(secs + .5); + printf(_("%d min %d sec "), min, minsec); + } + + std::string lines = getNumberFormat(linesOut); + printf(_("%s lines\n"), lines.c_str()); + printf("\n"); +} + +void ASConsole::sleep(int seconds) const +{ + clock_t endwait; + endwait = clock_t(clock() + seconds * CLOCKS_PER_SEC); + while (clock() < endwait) {} +} + +bool ASConsole::stringEndsWith(std::string_view str, std::string_view suffix) const +{ + return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +void ASConsole::updateExcludeVector(const std::string& suffixParam) +{ + excludeVector.emplace_back(suffixParam); + standardizePath(excludeVector.back(), true); + // do not use emplace_back on vector until supported by macOS + excludeHitsVector.push_back(false); +} + +int ASConsole::waitForRemove(const char* newFileName) const +{ + struct stat stBuf; + int seconds; + // sleep a max of 20 seconds for the remove + for (seconds = 1; seconds <= 20; seconds++) + { + sleep(1); + if (stat(newFileName, &stBuf) != 0) + break; + } + errno = 0; + return seconds; +} + +// From The Code Project http://www.codeproject.com/string/wildcmp.asp +// Written by Jack Handy - jakkhandy@hotmail.com +// Modified to compare case insensitive for Windows +int ASConsole::wildcmp(const char* wild, const char* data) const +{ + const char* cp = nullptr; + const char* mp = nullptr; + bool cmpval; + + while ((*data) && (*wild != '*')) + { + if (!g_isCaseSensitive) + cmpval = (tolower(*wild) != tolower(*data)) && (*wild != '?'); + else + cmpval = (*wild != *data) && (*wild != '?'); + + if (cmpval) + { + return 0; + } + wild++; + data++; + } + + while (*data) + { + if (*wild == '*') + { + if (!*++wild) + { + return 1; + } + mp = wild; + cp = data + 1; + } + else + { + if (!g_isCaseSensitive) + cmpval = (tolower(*wild) == tolower(*data) || (*wild == '?')); + else + cmpval = (*wild == *data) || (*wild == '?'); + + if (cmpval) + { + wild++; + data++; + } + else + { + wild = mp; + data = cp++; + } + } + } + + while (*wild == '*') + { + wild++; + } + return !*wild; +} + +void ASConsole::writeFile(const std::string& fileName_, FileEncoding encoding, std::ostringstream& out) const +{ + // save date accessed and date modified of original file + struct stat stBuf; + bool statErr = false; + if (stat(fileName_.c_str(), &stBuf) == -1) + statErr = true; + + // create a backup + if (!noBackup) + { + std::string origFileName = fileName_ + origSuffix; + removeFile(origFileName.c_str(), "Cannot remove pre-existing backup file"); + renameFile(fileName_.c_str(), origFileName.c_str(), "Cannot create backup file"); + } + + // write the output file + std::ofstream fout(fileName_.c_str(), std::ios::binary | std::ios::trunc); + if (!fout) + error("Cannot open output file", fileName_.c_str()); + if (encoding == UTF_16LE || encoding == UTF_16BE) + { + // convert utf-8 to utf-16 + bool isBigEndian = (encoding == UTF_16BE); + size_t utf16Size = encode.utf16LengthFromUtf8(out.str().c_str(), out.str().length()); + char* utf16Out = new char[utf16Size]; + size_t utf16Len = encode.utf8ToUtf16(const_cast(out.str().c_str()), + out.str().length(), isBigEndian, utf16Out); + assert(utf16Len <= utf16Size); + fout << std::string(utf16Out, utf16Len); + delete[] utf16Out; + } + else + fout << out.str(); + + fout.close(); + + // change date modified to original file date + // Embarcadero must be linked with cw32mt not cw32 + if (preserveDate) + { + if (!statErr) + { + struct utimbuf outBuf; + outBuf.actime = stBuf.st_atime; + // add ticks so 'make' will recognize a change + // Visual Studio 2008 needs more than 1 + outBuf.modtime = stBuf.st_mtime + 10; + if (utime(fileName_.c_str(), &outBuf) == -1) + statErr = true; + } + if (statErr) + { + perror("errno message"); + (*errorStream) << "********* Cannot preserve file date" << '\n'; + } + } +} + +#else // ASTYLE_LIB + +//----------------------------------------------------------------------------- +// ASLibrary class +// used by shared object (DLL) calls +//----------------------------------------------------------------------------- + +char16_t* ASLibrary::formatUtf16(const char16_t* pSourceIn, // the source to be formatted + const char16_t* pOptions, // AStyle options + fpError fpErrorHandler, // error handler function + fpAlloc fpMemoryAlloc) const // memory allocation function) +{ + const char* utf8In = convertUtf16ToUtf8(pSourceIn); + if (utf8In == nullptr) + { + fpErrorHandler(121, "Cannot convert input utf-16 to utf-8."); + return nullptr; + } + const char* utf8Options = convertUtf16ToUtf8(pOptions); + if (utf8Options == nullptr) + { + delete[] utf8In; + fpErrorHandler(122, "Cannot convert options utf-16 to utf-8."); + return nullptr; + } + // call the Artistic Style formatting function + // cannot use the callers memory allocation here + char* utf8Out = AStyleMain(utf8In, + utf8Options, + fpErrorHandler, + ASLibrary::tempMemoryAllocation); + // finished with these + delete[] utf8In; + delete[] utf8Options; + utf8In = nullptr; + utf8Options = nullptr; + // AStyle error has already been sent + if (utf8Out == nullptr) + return nullptr; + // convert text to wide char and return it + char16_t* utf16Out = convertUtf8ToUtf16(utf8Out, fpMemoryAlloc); + delete[] utf8Out; + utf8Out = nullptr; + if (utf16Out == nullptr) + { + fpErrorHandler(123, "Cannot convert output utf-8 to utf-16."); + return nullptr; + } + return utf16Out; +} + +// STATIC method to allocate temporary memory for AStyle formatting. +// The data will be converted before being returned to the calling program. +char* STDCALL ASLibrary::tempMemoryAllocation(unsigned long memoryNeeded) +{ + char* buffer = new (std::nothrow) char[memoryNeeded]; + return buffer; +} + +/** + * Convert utf-8 strings to utf16 strings. + * Memory is allocated by the calling program memory allocation function. + * The calling function must check for errors. + */ +char16_t* ASLibrary::convertUtf8ToUtf16(const char* utf8In, fpAlloc fpMemoryAlloc) const +{ + if (utf8In == nullptr) + return nullptr; + char* data = const_cast(utf8In); + size_t dataSize = strlen(utf8In); + bool isBigEndian = encode.getBigEndian(); + // return size is in number of CHARs, not char16_t + size_t utf16Size = (encode.utf16LengthFromUtf8(data, dataSize) + sizeof(char16_t)); + char* utf16Out = fpMemoryAlloc((long) utf16Size); + if (utf16Out == nullptr) + return nullptr; +#ifdef NDEBUG + encode.utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out); +#else + size_t utf16Len = encode.utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out); + assert(utf16Len == utf16Size); +#endif + assert(utf16Size == (encode.utf16len(reinterpret_cast(utf16Out)) + 1) * sizeof(char16_t)); + return reinterpret_cast(utf16Out); +} + +/** + * Convert utf16 strings to utf-8. + * The calling function must check for errors and delete the + * allocated memory. + */ +char* ASLibrary::convertUtf16ToUtf8(const char16_t* utf16In) const +{ + if (utf16In == nullptr) + return nullptr; + char* data = reinterpret_cast(const_cast(utf16In)); + // size must be in chars + size_t dataSize = encode.utf16len(utf16In) * sizeof(char16_t); + bool isBigEndian = encode.getBigEndian(); + size_t utf8Size = encode.utf8LengthFromUtf16(data, dataSize, isBigEndian) + 1; + char* utf8Out = new (std::nothrow) char[utf8Size]; + if (utf8Out == nullptr) + return nullptr; +#ifdef NDEBUG + encode.utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out); +#else + size_t utf8Len = encode.utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out); + assert(utf8Len == utf8Size); +#endif + assert(utf8Size == strlen(utf8Out) + 1); + return utf8Out; +} + +#endif // ASTYLE_LIB + +//----------------------------------------------------------------------------- +// ASOptions class +// used by both console and library builds +//----------------------------------------------------------------------------- + +#ifdef ASTYLE_LIB +ASOptions::ASOptions(ASFormatter& formatterArg) + : formatter(formatterArg) +{ } +#else +ASOptions::ASOptions(ASFormatter& formatterArg, ASConsole& consoleArg) + : formatter(formatterArg), console(consoleArg) +{ } +#endif + +/** + * parse the options vector + * optionsVector can be either a fileOptionsVector (option file), + * a projectOptionsVector (project option file), + * or an optionsVector (command line) + * + * @return true if no errors, false if errors + */ +bool ASOptions::parseOptions(std::vector& optionsVector) +{ + std::vector::iterator option; + std::string arg; + std::string subArg; + optionErrors.clear(); + + for (option = optionsVector.begin(); option != optionsVector.end(); ++option) + { + arg = *option; + + if (arg.compare(0, 2, "--") == 0) + parseOption(arg.substr(2)); + else if (arg[0] == '-') + { + size_t i; + + for (i = 1; i < arg.length(); ++i) + { + if (i > 1 + && isalpha((unsigned char) arg[i]) + && arg[i - 1] != 'x') + { + // parse the previous option in subArg + parseOption(subArg); + subArg = ""; + } + // append the current option to subArg + subArg.append(1, arg[i]); + } + // parse the last option + parseOption(subArg); + subArg = ""; + } + else + { + parseOption(arg); + subArg = ""; + } + } + if (!optionErrors.str().empty()) + return false; + return true; +} + +void ASOptions::parseOption(const std::string& arg) +{ + if (isOption(arg, "A1", "style=allman") || isOption(arg, "style=bsd") || isOption(arg, "style=break")) + { + formatter.setFormattingStyle(STYLE_ALLMAN); + } + else if (isOption(arg, "A2", "style=java") || isOption(arg, "style=attach")) + { + formatter.setFormattingStyle(STYLE_JAVA); + } + else if (isOption(arg, "A3", "style=k&r") || isOption(arg, "style=kr") || isOption(arg, "style=k/r")) + { + formatter.setFormattingStyle(STYLE_KR); + } + else if (isOption(arg, "A4", "style=stroustrup")) + { + formatter.setFormattingStyle(STYLE_STROUSTRUP); + } + else if (isOption(arg, "A5", "style=whitesmith")) + { + formatter.setFormattingStyle(STYLE_WHITESMITH); + } + else if (isOption(arg, "A15", "style=vtk")) + { + formatter.setFormattingStyle(STYLE_VTK); + } + else if (isOption(arg, "A6", "style=ratliff") || isOption(arg, "style=banner")) + { + formatter.setFormattingStyle(STYLE_RATLIFF); + } + else if (isOption(arg, "A7", "style=gnu")) + { + formatter.setFormattingStyle(STYLE_GNU); + } + else if (isOption(arg, "A8", "style=linux") || isOption(arg, "style=knf")) + { + formatter.setFormattingStyle(STYLE_LINUX); + } + else if (isOption(arg, "A9", "style=horstmann") || isOption(arg, "style=run-in")) + { + formatter.setFormattingStyle(STYLE_HORSTMANN); + } + else if (isOption(arg, "A10", "style=1tbs") || isOption(arg, "style=otbs")) + { + formatter.setFormattingStyle(STYLE_1TBS); + } + else if (isOption(arg, "A14", "style=google")) + { + formatter.setFormattingStyle(STYLE_GOOGLE); + } + else if (isOption(arg, "A16", "style=mozilla")) + { + formatter.setFormattingStyle(STYLE_MOZILLA); + } + else if (isOption(arg, "A17", "style=webkit")) + { + formatter.setFormattingStyle(STYLE_WEBKIT); + } + else if (isOption(arg, "A11", "style=pico")) + { + formatter.setFormattingStyle(STYLE_PICO); + } + else if (isOption(arg, "A12", "style=lisp") || isOption(arg, "style=python")) + { + formatter.setFormattingStyle(STYLE_LISP); + } + // must check for mode=cs before mode=c !!! + else if (isOption(arg, "mode=cs")) + { + formatter.setSharpStyle(); + formatter.setModeManuallySet(true); + } + else if (isOption(arg, "mode=c")) + { + formatter.setCStyle(); + formatter.setModeManuallySet(true); + } + else if (isOption(arg, "mode=java")) + { + formatter.setJavaStyle(); + formatter.setModeManuallySet(true); + } + else if (isOption(arg, "mode=js")) + { + formatter.setJSStyle(); + formatter.setModeManuallySet(true); + } + else if (isOption(arg, "mode=objc")) + { + formatter.setObjCStyle(); + formatter.setModeManuallySet(true); + } + else if (isOption(arg, "mode=ghc") || isOption(arg, "mode=gsc")) + { + formatter.setGSCStyle(); + formatter.setModeManuallySet(true); + } + else if (isParamOption(arg, "t", "indent=tab=")) + { + int spaceNum = 4; + std::string spaceNumParam = getParam(arg, "t", "indent=tab="); + if (!spaceNumParam.empty()) + spaceNum = atoi(spaceNumParam.c_str()); + if (spaceNum < 2 || spaceNum > 20) + isOptionError(arg); + else + { + formatter.setTabIndentation(spaceNum, false); + } + } + else if (isOption(arg, "indent=tab")) + { + formatter.setTabIndentation(4); + } + else if (isParamOption(arg, "T", "indent=force-tab=")) + { + int spaceNum = 4; + std::string spaceNumParam = getParam(arg, "T", "indent=force-tab="); + if (!spaceNumParam.empty()) + spaceNum = atoi(spaceNumParam.c_str()); + if (spaceNum < 2 || spaceNum > 20) + isOptionError(arg); + else + { + formatter.setTabIndentation(spaceNum, true); + } + } + else if (isOption(arg, "indent=force-tab")) + { + formatter.setTabIndentation(4, true); + } + else if (isParamOption(arg, "xT", "indent=force-tab-x=")) + { + int tabNum = 8; + std::string tabNumParam = getParam(arg, "xT", "indent=force-tab-x="); + if (!tabNumParam.empty()) + tabNum = atoi(tabNumParam.c_str()); + if (tabNum < 2 || tabNum > 20) + isOptionError(arg); + else + { + formatter.setForceTabXIndentation(tabNum); + } + } + else if (isOption(arg, "indent=force-tab-x")) + { + formatter.setForceTabXIndentation(8); + } + else if (isParamOption(arg, "s", "indent=spaces=")) + { + int spaceNum = 4; + std::string spaceNumParam = getParam(arg, "s", "indent=spaces="); + if (!spaceNumParam.empty()) + spaceNum = atoi(spaceNumParam.c_str()); + if (spaceNum < 2 || spaceNum > 20) + isOptionError(arg); + else + { + formatter.setSpaceIndentation(spaceNum); + } + } + else if (isOption(arg, "indent=spaces")) + { + formatter.setSpaceIndentation(4); + } + else if (isParamOption(arg, "xt", "indent-continuation=")) + { + int contIndent = 1; + std::string contIndentParam = getParam(arg, "xt", "indent-continuation="); + if (!contIndentParam.empty()) + contIndent = atoi(contIndentParam.c_str()); + if (contIndent < 0) + isOptionError(arg); + else if (contIndent > 4) + isOptionError(arg); + else + formatter.setContinuationIndentation(contIndent); + } + else if (isParamOption(arg, "m", "min-conditional-indent=")) + { + int minIndent = MINCOND_TWO; + std::string minIndentParam = getParam(arg, "m", "min-conditional-indent="); + if (!minIndentParam.empty()) + minIndent = atoi(minIndentParam.c_str()); + if (minIndent >= MINCOND_END) + isOptionError(arg); + else + formatter.setMinConditionalIndentOption(minIndent); + } + else if (isParamOption(arg, "M", "max-continuation-indent=")) + { + int maxIndent = 40; + std::string maxIndentParam = getParam(arg, "M", "max-continuation-indent="); + if (!maxIndentParam.empty()) + maxIndent = atoi(maxIndentParam.c_str()); + if (maxIndent < 40) + isOptionError(arg); + else + formatter.setMaxContinuationIndentLength(maxIndent); + } + else if (isOption(arg, "N", "indent-namespaces")) + { + formatter.setNamespaceIndent(true); + } + else if (isOption(arg, "C", "indent-classes")) + { + formatter.setClassIndent(true); + } + else if (isOption(arg, "xG", "indent-modifiers")) + { + formatter.setModifierIndent(true); + } + else if (isOption(arg, "S", "indent-switches")) + { + formatter.setSwitchIndent(true); + } + else if (isOption(arg, "K", "indent-cases")) + { + formatter.setCaseIndent(true); + } + else if (isOption(arg, "xU", "indent-after-parens")) + { + formatter.setAfterParenIndent(true); + } + else if (isOption(arg, "L", "indent-labels")) + { + formatter.setLabelIndent(true); + } + else if (isOption(arg, "xW", "indent-preproc-block")) + { + formatter.setPreprocBlockIndent(true); + } + else if (isOption(arg, "w", "indent-preproc-define")) + { + formatter.setPreprocDefineIndent(true); + } + else if (isOption(arg, "xw", "indent-preproc-cond")) + { + formatter.setPreprocConditionalIndent(true); + } + else if (isOption(arg, "y", "break-closing-braces")) + { + formatter.setBreakClosingHeaderBracesMode(true); + } + else if (isOption(arg, "O", "keep-one-line-blocks")) + { + formatter.setBreakOneLineBlocksMode(false); + } + else if (isOption(arg, "o", "keep-one-line-statements")) + { + formatter.setBreakOneLineStatementsMode(false); + } + else if (isOption(arg, "P", "pad-paren")) + { + formatter.setParensOutsidePaddingMode(true); + formatter.setParensInsidePaddingMode(true); + } + else if (isOption(arg, "d", "pad-paren-out")) + { + formatter.setParensOutsidePaddingMode(true); + } + else if (isOption(arg, "xd", "pad-first-paren-out")) + { + formatter.setParensFirstPaddingMode(true); + } + else if (isOption(arg, "xo", "pad-empty-paren")) + { + formatter.setEmptyParensPaddingMode(true); + } + else if (isOption(arg, "D", "pad-paren-in")) + { + formatter.setParensInsidePaddingMode(true); + } + else if (isOption(arg, "H", "pad-header")) + { + formatter.setParensHeaderPaddingMode(true); + } + else if (isOption(arg, "U", "unpad-paren")) + { + formatter.setParensUnPaddingMode(true); + } + else if (isOption(arg, "p", "pad-oper")) + { + formatter.setOperatorPaddingMode(true); + } + else if (isOption(arg, "pad-negation")) + { + formatter.setNegationPaddingMode(NEGATION_PAD_AFTER); + } + else if (isOption(arg, "pad-negation=before")) + { + formatter.setNegationPaddingMode(NEGATION_PAD_BEFORE); + } + else if (isOption(arg, "pad-include")) + { + formatter.setIncludeDirectivePaddingMode(INCLUDE_PAD_AFTER); + } + else if (isOption(arg, "pad-include=none")) + { + formatter.setIncludeDirectivePaddingMode(INCLUDE_PAD_NONE); + } + else if (isOption(arg, "xg", "pad-comma")) + { + formatter.setCommaPaddingMode(true); + } + else if (isOption(arg, "xe", "delete-empty-lines")) + { + formatter.setDeleteEmptyLinesMode(true); + } + else if (isParamOption(arg, "squeeze-lines=")) + { + int keepEmptyLines = 2; + std::string keepEmptyLinesParam = getParam(arg, "squeeze-lines="); + if (!keepEmptyLinesParam.empty()) + keepEmptyLines = atoi(keepEmptyLinesParam.c_str()); + if (keepEmptyLines < 1) + isOptionError(arg); + else + formatter.setSqueezeEmptyLinesNumber(keepEmptyLines); + } + else if (isOption(arg, "E", "fill-empty-lines")) + { + formatter.setEmptyLineFill(true); + } + else if (isOption(arg, "c", "convert-tabs")) + { + formatter.setTabSpaceConversionMode(true); + } + else if (isOption(arg, "xy", "close-templates")) + { + formatter.setCloseTemplatesMode(true); + } + else if (isOption(arg, "F", "break-blocks=all")) + { + formatter.setBreakBlocksMode(true); + formatter.setBreakClosingHeaderBlocksMode(true); + } + else if (isOption(arg, "f", "break-blocks")) + { + formatter.setBreakBlocksMode(true); + } + else if (isOption(arg, "e", "break-elseifs")) + { + formatter.setBreakElseIfsMode(true); + } + else if (isOption(arg, "xb", "break-one-line-headers")) + { + formatter.setBreakOneLineHeadersMode(true); + } + else if (isOption(arg, "j", "add-braces")) + { + formatter.setAddBracesMode(true); + } + else if (isOption(arg, "J", "add-one-line-braces")) + { + formatter.setAddOneLineBracesMode(true); + } + else if (isOption(arg, "xj", "remove-braces")) + { + formatter.setRemoveBracesMode(true); + } + else if (isOption(arg, "Y", "indent-col1-comments")) + { + formatter.setIndentCol1CommentsMode(true); + } + else if (isOption(arg, "align-pointer=type")) + { + formatter.setPointerAlignment(PTR_ALIGN_TYPE); + } + else if (isOption(arg, "align-pointer=middle")) + { + formatter.setPointerAlignment(PTR_ALIGN_MIDDLE); + } + else if (isOption(arg, "align-pointer=name")) + { + formatter.setPointerAlignment(PTR_ALIGN_NAME); + } + else if (isParamOption(arg, "k")) + { + int align = 0; + std::string styleParam = getParam(arg, "k"); + if (!styleParam.empty()) + align = atoi(styleParam.c_str()); + if (align < 1 || align > 3) + isOptionError(arg); + else if (align == 1) + formatter.setPointerAlignment(PTR_ALIGN_TYPE); + else if (align == 2) + formatter.setPointerAlignment(PTR_ALIGN_MIDDLE); + else if (align == 3) + formatter.setPointerAlignment(PTR_ALIGN_NAME); + } + else if (isOption(arg, "align-reference=none")) + { + formatter.setReferenceAlignment(REF_ALIGN_NONE); + } + else if (isOption(arg, "align-reference=type")) + { + formatter.setReferenceAlignment(REF_ALIGN_TYPE); + } + else if (isOption(arg, "align-reference=middle")) + { + formatter.setReferenceAlignment(REF_ALIGN_MIDDLE); + } + else if (isOption(arg, "align-reference=name")) + { + formatter.setReferenceAlignment(REF_ALIGN_NAME); + } + else if (isParamOption(arg, "W")) + { + int align = 0; + std::string styleParam = getParam(arg, "W"); + if (!styleParam.empty()) + align = atoi(styleParam.c_str()); + if (align < 0 || align > 3) + isOptionError(arg); + else if (align == 0) + formatter.setReferenceAlignment(REF_ALIGN_NONE); + else if (align == 1) + formatter.setReferenceAlignment(REF_ALIGN_TYPE); + else if (align == 2) + formatter.setReferenceAlignment(REF_ALIGN_MIDDLE); + else if (align == 3) + formatter.setReferenceAlignment(REF_ALIGN_NAME); + } + else if (isParamOption(arg, "max-code-length=")) + { + int maxLength = 50; + std::string maxLengthParam = getParam(arg, "max-code-length="); + if (!maxLengthParam.empty()) + maxLength = atoi(maxLengthParam.c_str()); + if (maxLength < 50) + isOptionError(arg); + else if (maxLength > 200) + isOptionError(arg); + else + formatter.setMaxCodeLength(maxLength); + } + else if (isParamOption(arg, "xC")) + { + int maxLength = 50; + std::string maxLengthParam = getParam(arg, "xC"); + if (!maxLengthParam.empty()) + maxLength = atoi(maxLengthParam.c_str()); + if (maxLength > 200) + isOptionError(arg); + else + formatter.setMaxCodeLength(maxLength); + } + else if (isOption(arg, "xL", "break-after-logical")) + { + formatter.setBreakAfterMode(true); + } + else if (isOption(arg, "xc", "attach-classes")) + { + formatter.setAttachClass(true); + } + else if (isOption(arg, "xV", "attach-closing-while")) + { + formatter.setAttachClosingWhile(true); + } + else if (isOption(arg, "xk", "attach-extern-c")) + { + formatter.setAttachExternC(true); + } + else if (isOption(arg, "xn", "attach-namespaces")) + { + formatter.setAttachNamespace(true); + } + else if (isOption(arg, "xl", "attach-inlines")) + { + formatter.setAttachInline(true); + } + else if (isOption(arg, "xp", "remove-comment-prefix")) + { + formatter.setStripCommentPrefix(true); + } + else if (isOption(arg, "xB", "break-return-type")) + { + formatter.setBreakReturnType(true); + } + else if (isOption(arg, "xD", "break-return-type-decl")) + { + formatter.setBreakReturnTypeDecl(true); + } + else if (isOption(arg, "xf", "attach-return-type")) + { + formatter.setAttachReturnType(true); + } + else if (isOption(arg, "xh", "attach-return-type-decl")) + { + formatter.setAttachReturnTypeDecl(true); + } + // To avoid compiler limit of blocks nested too deep. + else if (!parseOptionContinued(arg)) + { + isOptionError(arg); + } +} // End of parseOption function + +// Continuation of parseOption. +// To avoid compiler limit of blocks nested too deep. +// Return 'true' if the option was found and processed. +// Return 'false' if the option was not found. +bool ASOptions::parseOptionContinued(const std::string& arg) +{ + // Objective-C options + if (isOption(arg, "xQ", "pad-method-prefix")) + { + formatter.setMethodPrefixPaddingMode(true); + } + else if (isOption(arg, "xR", "unpad-method-prefix")) + { + formatter.setMethodPrefixUnPaddingMode(true); + } + else if (isOption(arg, "xq", "pad-return-type")) + { + formatter.setReturnTypePaddingMode(true); + } + else if (isOption(arg, "xr", "unpad-return-type")) + { + formatter.setReturnTypeUnPaddingMode(true); + } + else if (isOption(arg, "xS", "pad-param-type")) + { + formatter.setParamTypePaddingMode(true); + } + else if (isOption(arg, "xs", "unpad-param-type")) + { + formatter.setParamTypeUnPaddingMode(true); + } + else if (isOption(arg, "xM", "align-method-colon")) + { + formatter.setAlignMethodColon(true); + } + else if (isOption(arg, "xP0", "pad-method-colon=none")) + { + formatter.setObjCColonPaddingMode(COLON_PAD_NONE); + } + else if (isOption(arg, "xP1", "pad-method-colon=all")) + { + formatter.setObjCColonPaddingMode(COLON_PAD_ALL); + } + else if (isOption(arg, "xP2", "pad-method-colon=after")) + { + formatter.setObjCColonPaddingMode(COLON_PAD_AFTER); + } + else if (isOption(arg, "xP3", "pad-method-colon=before")) + { + formatter.setObjCColonPaddingMode(COLON_PAD_BEFORE); + } + +#ifdef ASTYLE_LIB + // End of options used by GUI ///////////////////////////////////////////////////////////////// + else + { + return false; + } + return true; +#else + // Options used by only console /////////////////////////////////////////////////////////////// + else if (isOption(arg, "n", "suffix=none")) + { + console.setNoBackup(true); + } + else if (isParamOption(arg, "suffix=")) + { + std::string suffixParam = getParam(arg, "suffix="); + if (!suffixParam.empty()) + { + console.setOrigSuffix(suffixParam); + } + } + else if (isParamOption(arg, "exclude=")) + { + std::string suffixParam = getParam(arg, "exclude="); + if (!suffixParam.empty()) + console.updateExcludeVector(suffixParam); + } + else if (isOption(arg, "r", "R") || isOption(arg, "recursive")) + { + console.setIsRecursive(true); + } + else if (isOption(arg, "dry-run")) + { + console.setIsDryRun(true); + } + else if (isOption(arg, "error-on-changes")) + { + console.setRejectFormatWithDryRun(true); + } + else if (isOption(arg, "Z", "preserve-date")) + { + console.setPreserveDate(true); + } + else if (isOption(arg, "v", "verbose")) + { + console.setIsVerbose(true); + } + else if (isOption(arg, "Q", "formatted")) + { + console.setIsFormattedOnly(true); + } + else if (isOption(arg, "q", "quiet")) + { + console.setIsQuiet(true); + } + else if (isOption(arg, "i", "ignore-exclude-errors")) + { + console.setIgnoreExcludeErrors(true); + } + else if (isOption(arg, "accept-empty-list")) + { + console.setAcceptEmptyInputFileList(true); + } + else if (isOption(arg, "xi", "ignore-exclude-errors-x")) + { + console.setIgnoreExcludeErrorsAndDisplay(true); + } + else if (isOption(arg, "X", "errors-to-stdout")) + { + console.setErrorStream(&std::cout); + } + else if (isOption(arg, "lineend=windows")) + { + formatter.setLineEndFormat(LINEEND_WINDOWS); + } + else if (isOption(arg, "lineend=linux")) + { + formatter.setLineEndFormat(LINEEND_LINUX); + } + else if (isOption(arg, "lineend=macold")) + { + formatter.setLineEndFormat(LINEEND_MACOLD); + } + else if (isParamOption(arg, "z")) + { + int lineendType = 0; + std::string lineendParam = getParam(arg, "z"); + if (!lineendParam.empty()) + lineendType = atoi(lineendParam.c_str()); + if (lineendType < 1 || lineendType > 3) + isOptionError(arg); + else if (lineendType == 1) + formatter.setLineEndFormat(LINEEND_WINDOWS); + else if (lineendType == 2) + formatter.setLineEndFormat(LINEEND_LINUX); + else if (lineendType == 3) + formatter.setLineEndFormat(LINEEND_MACOLD); + } + else if (isOption(arg, "squeeze-ws")) + { + formatter.setSqueezeWhitespace(true); + } + else if (isOption(arg, "preserve-ws")) + { + formatter.setPreserveWhitespace(true); + } + else if (isOption(arg, "pad-brackets")) + { + formatter.setBracketsOutsidePaddingMode(true); + formatter.setBracketsInsidePaddingMode(true); + } + else if (isOption(arg, "pad-brackets-in")) + { + formatter.setBracketsInsidePaddingMode(true); + } + else if (isOption(arg, "pad-brackets-out")) + { + formatter.setBracketsOutsidePaddingMode(true); + } + else if (isOption(arg, "unpad-brackets")) + { + formatter.setBracketsUnPaddingMode(true); + } + else if (isOption(arg, "indent-lambda")) + { + formatter.setLambdaIndentation(true); + } + else + { + return false; + } + return true; +#endif +} // End of parseOptionContinued function + +// Parse options from the option file. +void ASOptions::importOptions(std::stringstream& in, std::vector& optionsVector) +{ + char ch; + bool isInQuote = false; + char quoteChar = ' '; + std::string currentToken; + + while (in) + { + currentToken = ""; + do + { + in.get(ch); + if (in.eof()) + break; + // treat '#' as line comments + if (ch == '#') + while (in) + { + in.get(ch); + if (ch == '\n' || ch == '\r') + break; + } + + // break options on new-lines, tabs, commas, or spaces + // remove quotes from output + if (in.eof() || ch == '\n' || ch == '\r' || ch == '\t' || ch == ',') + break; + if (ch == ' ' && !isInQuote) + break; + if (ch == quoteChar && isInQuote) + break; + if (ch == '"' || ch == '\'') + { + isInQuote = true; + quoteChar = ch; + continue; + } + currentToken.append(1, ch); + } + while (in); + + if (!currentToken.empty()) + optionsVector.emplace_back(currentToken); + isInQuote = false; + } +} + +std::string ASOptions::getOptionErrors() const +{ + return optionErrors.str(); +} + +std::string ASOptions::getParam(const std::string& arg, const char* op) +{ + return arg.substr(strlen(op)); +} + +std::string ASOptions::getParam(const std::string& arg, const char* op1, const char* op2) +{ + return isParamOption(arg, op1) ? getParam(arg, op1) : getParam(arg, op2); +} + +bool ASOptions::isOption(const std::string& arg, const char* op) +{ + return arg == op; +} + +bool ASOptions::isOption(const std::string& arg, const char* op1, const char* op2) +{ + return (isOption(arg, op1) || isOption(arg, op2)); +} + +void ASOptions::isOptionError(const std::string& arg) +{ + if (optionErrors.str().empty()) + optionErrors << "Invalid Artistic Style options:" << '\n'; // need main error message + optionErrors << "\t" << arg << '\n'; +} + +bool ASOptions::isParamOption(const std::string& arg, const char* option) +{ + bool retVal = arg.compare(0, strlen(option), option) == 0; + // if comparing for short option, 2nd char of arg must be numeric + if (retVal && strlen(option) == 1 && arg.length() > 1) + if (!isdigit((unsigned char) arg[1])) + retVal = false; + return retVal; +} + +bool ASOptions::isParamOption(const std::string& arg, const char* option1, const char* option2) +{ + return isParamOption(arg, option1) || isParamOption(arg, option2); +} + +//---------------------------------------------------------------------------- +// ASEncoding class +//---------------------------------------------------------------------------- + +// Return true if an int is big endian. +bool ASEncoding::getBigEndian() const +{ + char16_t word = 0x0001; + char* byte = reinterpret_cast(&word); + return (byte[0] ? false : true); +} + +// Swap the two low order bytes of a 16 bit integer value. +int ASEncoding::swap16bit(int value) const +{ + return (((value & 0xff) << 8) | ((value & 0xff00) >> 8)); +} + +// Return the length of a utf-16 C string. +// The length is in number of char16_t. +size_t ASEncoding::utf16len(const utf16* utf16In) const +{ + size_t length = 0; + while (*utf16In++ != '\0') + length++; + return length; +} + +// Adapted from SciTE UniConversion.cxx. +// Copyright 1998-2001 by Neil Hodgson +// Modified for Artistic Style by Jim Pattee. +// Compute the length of an output utf-8 file given a utf-16 file. +// Input inLen is the size in BYTES (not wchar_t). +size_t ASEncoding::utf8LengthFromUtf16(const char* utf16In, size_t inLen, bool isBigEndian) const +{ + size_t len = 0; + size_t wcharLen = (inLen / 2) + (inLen % 2); + const auto* uptr = reinterpret_cast(utf16In); + for (size_t i = 0; i < wcharLen;) + { + size_t uch = isBigEndian ? swap16bit(uptr[i]) : uptr[i]; + if (uch < 0x80) + len++; + else if (uch < 0x800) + len += 2; + else if ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_LEAD_LAST)) + { + len += 4; + i++; + } + else + len += 3; + i++; + } + return len; +} + +// Adapted from SciTE Utf8_16.cxx. +// Copyright (C) 2002 Scott Kirkwood. +// Modified for Artistic Style by Jim Pattee. +// Convert a utf-8 file to utf-16. +size_t ASEncoding::utf8ToUtf16(char* utf8In, size_t inLen, bool isBigEndian, char* utf16Out) const +{ + int nCur = 0; + auto* pRead = reinterpret_cast(utf8In); + auto* pCur = reinterpret_cast(utf16Out); + const ubyte* pEnd = pRead + inLen; + const utf16* pCurStart = pCur; + eState state = eStart; + + // the BOM will automatically be converted to utf-16 + while (pRead < pEnd) + { + switch (state) + { + case eStart: + if ((0xF0 & *pRead) == 0xF0) + { + nCur = (0x7 & *pRead) << 18; + state = eSecondOf4Bytes; + } + else if ((0xE0 & *pRead) == 0xE0) + { + nCur = (~0xE0 & *pRead) << 12; + state = ePenultimate; + } + else if ((0xC0 & *pRead) == 0xC0) + { + nCur = (~0xC0 & *pRead) << 6; + state = eFinal; + } + else + { + nCur = *pRead; + state = eStart; + } + break; + case eSecondOf4Bytes: + nCur |= (0x3F & *pRead) << 12; + state = ePenultimate; + break; + case ePenultimate: + nCur |= (0x3F & *pRead) << 6; + state = eFinal; + break; + case eFinal: + nCur |= (0x3F & *pRead); + state = eStart; + break; + // no default case is needed + } + ++pRead; + + if (state == eStart) + { + int codePoint = nCur; + if (codePoint >= SURROGATE_FIRST_VALUE) + { + codePoint -= SURROGATE_FIRST_VALUE; + int lead = (codePoint >> 10) + SURROGATE_LEAD_FIRST; + *pCur++ = static_cast(isBigEndian ? swap16bit(lead) : lead); + int trail = (codePoint & 0x3ff) + SURROGATE_TRAIL_FIRST; + *pCur++ = static_cast(isBigEndian ? swap16bit(trail) : trail); + } + else + *pCur++ = static_cast(isBigEndian ? swap16bit(codePoint) : codePoint); + } + } + // return value is the output length in BYTES (not wchar_t) + return (pCur - pCurStart) * 2; +} + +// Adapted from SciTE UniConversion.cxx. +// Copyright 1998-2001 by Neil Hodgson +// Modified for Artistic Style by Jim Pattee. +// Compute the length of an output utf-16 file given a utf-8 file. +// Return value is the size in BYTES (not wchar_t). +size_t ASEncoding::utf16LengthFromUtf8(const char* utf8In, size_t len) const +{ + size_t ulen = 0; + size_t charLen; + for (size_t i = 0; i < len;) + { + auto ch = static_cast(utf8In[i]); + if (ch < 0x80) + charLen = 1; + else if (ch < 0x80 + 0x40 + 0x20) + charLen = 2; + else if (ch < 0x80 + 0x40 + 0x20 + 0x10) + charLen = 3; + else + { + charLen = 4; + ulen++; + } + i += charLen; + ulen++; + } + // return value is the length in bytes (not wchar_t) + return ulen * 2; +} + +// Adapted from SciTE Utf8_16.cxx. +// Copyright (C) 2002 Scott Kirkwood. +// Modified for Artistic Style by Jim Pattee. +// Convert a utf-16 file to utf-8. +size_t ASEncoding::utf16ToUtf8(char* utf16In, size_t inLen, bool isBigEndian, + bool firstBlock, char* utf8Out) const +{ + int nCur16 = 0; + int nCur = 0; + auto* pRead = reinterpret_cast(utf16In); + auto* pCur = reinterpret_cast(utf8Out); + const ubyte* pEnd = pRead + inLen; + const ubyte* pCurStart = pCur; + static eState state = eStart; // state is retained for subsequent blocks + if (firstBlock) + state = eStart; + + // the BOM will automatically be converted to utf-8 + while (pRead < pEnd) + { + switch (state) + { + case eStart: + if (pRead >= pEnd) + { + ++pRead; + break; + } + if (isBigEndian) + { + nCur16 = static_cast(*pRead++ << 8); + nCur16 |= static_cast(*pRead); + } + else + { + nCur16 = *pRead++; + nCur16 |= static_cast(*pRead << 8); + } + if (nCur16 >= SURROGATE_LEAD_FIRST && nCur16 <= SURROGATE_LEAD_LAST) + { + ++pRead; + int trail; + if (isBigEndian) + { + trail = static_cast(*pRead++ << 8); + trail |= static_cast(*pRead); + } + else + { + trail = *pRead++; + trail |= static_cast(*pRead << 8); + } + nCur16 = (((nCur16 & 0x3ff) << 10) | (trail & 0x3ff)) + SURROGATE_FIRST_VALUE; + } + ++pRead; + + if (nCur16 < 0x80) + { + nCur = static_cast(nCur16 & 0xFF); + state = eStart; + } + else if (nCur16 < 0x800) + { + nCur = static_cast(0xC0 | (nCur16 >> 6)); + state = eFinal; + } + else if (nCur16 < SURROGATE_FIRST_VALUE) + { + nCur = static_cast(0xE0 | (nCur16 >> 12)); + state = ePenultimate; + } + else + { + nCur = static_cast(0xF0 | (nCur16 >> 18)); + state = eSecondOf4Bytes; + } + break; + case eSecondOf4Bytes: + nCur = static_cast(0x80 | ((nCur16 >> 12) & 0x3F)); + state = ePenultimate; + break; + case ePenultimate: + nCur = static_cast(0x80 | ((nCur16 >> 6) & 0x3F)); + state = eFinal; + break; + case eFinal: + nCur = static_cast(0x80 | (nCur16 & 0x3F)); + state = eStart; + break; + // no default case is needed + } + *pCur++ = static_cast(nCur); + } + return pCur - pCurStart; +} + +//---------------------------------------------------------------------------- + +} // namespace astyle + +//---------------------------------------------------------------------------- + +using namespace astyle; + +//---------------------------------------------------------------------------- +// ASTYLE_JNI functions for Java library builds +//---------------------------------------------------------------------------- + +#ifdef ASTYLE_JNI + +// called by a java program to get the version number +// the function name is constructed from method names in the calling java program +extern "C" EXPORT +jstring STDCALL Java_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass) +{ + return env->NewStringUTF(g_version); +} + +extern "C" EXPORT +jstring STDCALL Java_cc_arduino_packages_formatter_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass) +{ + return env->NewStringUTF(g_version); +} + +// called by a java program to format the source code +// the function name is constructed from method names in the calling java program + +extern "C" EXPORT +jstring STDCALL Java_AStyleInterface_AStyleMain(JNIEnv* env, + jobject obj, + jstring textInJava, + jstring optionsJava) +{ + return Java_cc_arduino_packages_formatter_AStyleInterface_AStyleMain(env, obj, textInJava, optionsJava); +} + + +extern "C" EXPORT +jstring STDCALL Java_cc_arduino_packages_formatter_AStyleInterface_AStyleMain(JNIEnv* env, + jobject obj, + jstring textInJava, + jstring optionsJava) +{ + g_env = env; // make object available globally + g_obj = obj; // make object available globally + + jstring textErr = env->NewStringUTF(""); // zero length text returned if an error occurs + + // get the method ID + jclass cls = env->GetObjectClass(obj); + g_mid = env->GetMethodID(cls, "ErrorHandler", "(ILjava/lang/String;)V"); + if (g_mid == nullptr) + { + std::cout << "Cannot find java method ErrorHandler" << std::endl; + return textErr; + } + + // convert jstring to char* + const char* textIn = env->GetStringUTFChars(textInJava, nullptr); + const char* options = env->GetStringUTFChars(optionsJava, nullptr); + + // call the C++ formatting function + char* textOut = AStyleMain(textIn, options, javaErrorHandler, javaMemoryAlloc); + // if an error message occurred it was displayed by errorHandler + if (textOut == nullptr) + return textErr; + + // release memory + jstring textOutJava = env->NewStringUTF(textOut); + delete[] textOut; + env->ReleaseStringUTFChars(textInJava, textIn); + env->ReleaseStringUTFChars(optionsJava, options); + + return textOutJava; +} + +// Call the Java error handler +void STDCALL javaErrorHandler(int errorNumber, const char* errorMessage) +{ + jstring errorMessageJava = g_env->NewStringUTF(errorMessage); + g_env->CallVoidMethod(g_obj, g_mid, errorNumber, errorMessageJava); +} + +// Allocate memory for the formatted text +char* STDCALL javaMemoryAlloc(unsigned long memoryNeeded) +{ + // error condition is checked after return from AStyleMain + char* buffer = new (std::nothrow) char[memoryNeeded]; + return buffer; +} + +#endif // ASTYLE_JNI + +//---------------------------------------------------------------------------- +// ASTYLE_LIB functions for library builds +//---------------------------------------------------------------------------- + +#ifdef ASTYLE_LIB + +//---------------------------------------------------------------------------- +// ASTYLE_LIB entry point for AStyleMainUtf16 library builds +//---------------------------------------------------------------------------- +/* +* IMPORTANT Visual C DLL linker for WIN32 must have the additional options: +* /EXPORT:AStyleMain=_AStyleMain@16 +* /EXPORT:AStyleMainUtf16=_AStyleMainUtf16@16 +* /EXPORT:AStyleGetVersion=_AStyleGetVersion@0 +* No /EXPORT is required for x64 +*/ +extern "C" EXPORT char16_t* STDCALL AStyleMainUtf16(const char16_t* pSourceIn, // the source to be formatted + const char16_t* pOptions, // AStyle options + fpError fpErrorHandler, // error handler function + fpAlloc fpMemoryAlloc) // memory allocation function +{ + if (fpErrorHandler == nullptr) // cannot display a message if no error handler + return nullptr; + + if (pSourceIn == nullptr) + { + fpErrorHandler(101, "No pointer to source input."); + return nullptr; + } + if (pOptions == nullptr) + { + fpErrorHandler(102, "No pointer to AStyle options."); + return nullptr; + } + if (fpMemoryAlloc == nullptr) + { + fpErrorHandler(103, "No pointer to memory allocation function."); + return nullptr; + } +#ifndef _WIN32 + // check size of char16_t on Linux + int sizeCheck = 2; + if (sizeof(char16_t) != sizeCheck) + { + fpErrorHandler(104, "char16_t is not the correct size."); + return nullptr; + } +#endif + + ASLibrary library; + char16_t* utf16Out = library.formatUtf16(pSourceIn, pOptions, fpErrorHandler, fpMemoryAlloc); + return utf16Out; +} + +//---------------------------------------------------------------------------- +// ASTYLE_LIB entry point for library builds +//---------------------------------------------------------------------------- +/* + * IMPORTANT Visual C DLL linker for WIN32 must have the additional options: + * /EXPORT:AStyleMain=_AStyleMain@16 + * /EXPORT:AStyleMainUtf16=_AStyleMainUtf16@16 + * /EXPORT:AStyleGetVersion=_AStyleGetVersion@0 + * No /EXPORT is required for x64 + */ +extern "C" EXPORT char* STDCALL AStyleMain(const char* pSourceIn, // the source to be formatted + const char* pOptions, // AStyle options + fpError fpErrorHandler, // error handler function + fpAlloc fpMemoryAlloc) // memory allocation function +{ + if (fpErrorHandler == nullptr) // cannot display a message if no error handler + return nullptr; + + if (pSourceIn == nullptr) + { + fpErrorHandler(101, "No pointer to source input."); + return nullptr; + } + if (pOptions == nullptr) + { + fpErrorHandler(102, "No pointer to AStyle options."); + return nullptr; + } + if (fpMemoryAlloc == nullptr) + { + fpErrorHandler(103, "No pointer to memory allocation function."); + return nullptr; + } + + ASFormatter formatter; + ASOptions options(formatter); + + std::vector optionsVector; + std::stringstream opt(pOptions); + + options.importOptions(opt, optionsVector); + + bool ok = options.parseOptions(optionsVector); + if (!ok) + fpErrorHandler(130, options.getOptionErrors().c_str()); + + std::stringstream in(pSourceIn); + ASStreamIterator streamIterator(&in); + std::ostringstream out; + formatter.init(&streamIterator); + + while (formatter.hasMoreLines()) + { + out << formatter.nextLine(); + if (formatter.hasMoreLines()) + out << streamIterator.getLastOutputEOL(); + else + { + // this can happen if the file if missing a closing brace and break-blocks is requested + if (formatter.getIsLineReady()) + { + out << streamIterator.getLastOutputEOL(); + out << formatter.nextLine(); + } + } + } + + size_t textSizeOut = out.str().length(); + char* pTextOut = fpMemoryAlloc((long) textSizeOut + 1); // call memory allocation function + if (pTextOut == nullptr) + { + fpErrorHandler(120, "Allocation failure on output."); + return nullptr; + } + + strcpy(pTextOut, out.str().c_str()); +#ifndef NDEBUG + // The checksum is an assert in the console build and ASFormatter. + // This error returns the incorrectly formatted file to the editor. + // This is done to allow the file to be saved for debugging purposes. + if (formatter.getChecksumDiff() != 0) + fpErrorHandler(220, + "Checksum error.\n" + "The incorrectly formatted file will be returned for debugging."); +#endif + return pTextOut; +} + +extern "C" EXPORT const char* STDCALL AStyleGetVersion(void) +{ + return g_version; +} + +// ASTYLECON_LIB is defined to exclude "main" from the test programs +#elif !defined(ASTYLECON_LIB) + +//---------------------------------------------------------------------------- +// main function for ASConsole build +//---------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + // create objects + ASFormatter formatter; + std::unique_ptr console(new ASConsole(formatter)); + + // process command line and option files + // build the vectors fileNameVector, optionsVector, and fileOptionsVector + std::vector argvOptions; + argvOptions = console->getArgvOptions(argc, argv); + console->processOptions(argvOptions); + + // if no files have been given, use cin for input and cout for output + if (!console->fileNameVectorIsEmpty()) + console->processFiles(); + else + console->formatCinToCout(); + + return EXIT_SUCCESS; +} + +#endif // ASTYLE_LIB diff --git a/astyle/AStyle/src/astyle_main.h b/astyle/AStyle/src/astyle_main.h new file mode 100644 index 0000000..a1c2d08 --- /dev/null +++ b/astyle/AStyle/src/astyle_main.h @@ -0,0 +1,445 @@ +// astyle_main.h +// Copyright (c) 2025 The Artistic Style Authors. +// This code is licensed under the MIT License. +// License.md describes the conditions under which this software may be distributed. + +#ifndef ASTYLE_MAIN_H +#define ASTYLE_MAIN_H + +//---------------------------------------------------------------------------- +// headers +//---------------------------------------------------------------------------- + +#include "astyle.h" + +#include +#include + +#include + +#include "/home/pt/dev/kolibri/filesystem.h" + +#if defined(__BORLANDC__) && __BORLANDC__ < 0x0650 + // Embarcadero needs this for the following utime.h + // otherwise "struct utimbuf" gets an error on time_t + // 0x0650 for C++Builder XE3 + using std::time_t; +#endif + +#if defined(_MSC_VER) + #include + #include +#else + #include + #include +#endif // end compiler checks + +#ifdef ASTYLE_JNI + #include + #ifndef ASTYLE_LIB // ASTYLE_LIB must be defined for ASTYLE_JNI + #define ASTYLE_LIB + #endif +#endif // ASTYLE_JNI + +#ifndef ASTYLE_LIB + // for console build only + #include "ASLocalizer.h" + #define _(a) localizer.settext(a) +#endif // ASTYLE_LIB + +//----------------------------------------------------------------------------- +// declarations +//----------------------------------------------------------------------------- + +// for getenv and localtime +#if defined(_MSC_VER) + #pragma warning(disable: 4996) // secure version deprecation warnings +#endif + +#ifdef __clang__ + #pragma clang diagnostic ignored "-Wdeprecated-declarations" // getenv, localtime + #pragma clang diagnostic ignored "-Wmissing-braces" +#endif + +#ifdef ASTYLE_LIB + // define STDCALL and EXPORT for Windows + // MINGW defines STDCALL in Windows.h (actually windef.h) + // EXPORT has no value if ASTYLE_NO_EXPORT is defined + #ifdef _WIN32 + #ifndef STDCALL + #define STDCALL __stdcall + #endif + // define this to prevent compiler warning and error messages + #ifdef ASTYLE_NO_EXPORT + #define EXPORT + #else + #define EXPORT __declspec(dllexport) + #endif + // define STDCALL and EXPORT for non-Windows + // visibility attribute allows "-fvisibility=hidden" compiler option + #else + #define STDCALL + #if __GNUC__ >= 4 + #define EXPORT __attribute__ ((visibility ("default"))) + #else + #define EXPORT + #endif + #endif // #ifdef _WIN32 + // define pointers to callback error handler and memory allocation + typedef void (STDCALL* fpError)(int errorNumber, const char* errorMessage); + typedef char* (STDCALL* fpAlloc)(unsigned long memoryNeeded); +#endif // #ifdef ASTYLE_LIB + +//---------------------------------------------------------------------------- +// astyle namespace +//---------------------------------------------------------------------------- + +namespace astyle { +// +//---------------------------------------------------------------------------- +// ASStreamIterator class +// typename will be stringstream for AStyle +// it could be istream for plug-ins +// ASSourceIterator is an inherited abstract class defined in astyle.h +//---------------------------------------------------------------------------- + +template +class ASStreamIterator : public ASSourceIterator +{ +public: + bool checkForEmptyLine; + +public: // function declarations + explicit ASStreamIterator(T* in); + ~ASStreamIterator() override; + + int getStreamLength() const override; + std::string nextLine(bool emptyLineWasDeleted) override; + std::string peekNextLine() override; + void peekReset() override; + void saveLastInputLine(); + std::streamoff tellg() override; + +private: + T* inStream; // pointer to the input stream + std::string buffer; // current input line + std::string prevBuffer; // previous input line + std::string lastOutputEOL; // next output end of line char + + std::streamoff streamLength; // length of the input file stream + std::streamoff peekStart; // starting position for peekNextLine + bool prevLineDeleted; // the previous input line was deleted + +public: // inline functions + bool compareToInputBuffer(const std::string& nextLine_) const + { return (nextLine_ == prevBuffer); } + const std::string& getLastOutputEOL() const { return lastOutputEOL; } + std::streamoff getPeekStart() const override { return peekStart; } + bool hasMoreLines() const override { return !inStream->eof(); } +}; + +//---------------------------------------------------------------------------- +// ASEncoding class for utf8/16 conversions +// used by both console and library builds +//---------------------------------------------------------------------------- + +class ASEncoding +{ +private: + using utf16 = char16_t; // 16 bits unsigned + using utf8 = unsigned char; // 8 bits + using ubyte = unsigned char; // 8 bits + enum { SURROGATE_LEAD_FIRST = 0xD800 }; + enum { SURROGATE_LEAD_LAST = 0xDBFF }; + enum { SURROGATE_TRAIL_FIRST = 0xDC00 }; + enum { SURROGATE_TRAIL_LAST = 0xDFFF }; + enum { SURROGATE_FIRST_VALUE = 0x10000 }; + enum eState { eStart, eSecondOf4Bytes, ePenultimate, eFinal }; + +public: + bool getBigEndian() const; + int swap16bit(int value) const; + size_t utf16len(const utf16* utf16In) const; + size_t utf8LengthFromUtf16(const char* utf16In, size_t inLen, bool isBigEndian) const; + size_t utf8ToUtf16(char* utf8In, size_t inLen, bool isBigEndian, char* utf16Out) const; + size_t utf16LengthFromUtf8(const char* utf8In, size_t len) const; + size_t utf16ToUtf8(char* utf16In, size_t inLen, bool isBigEndian, + bool firstBlock, char* utf8Out) const; +}; + +//---------------------------------------------------------------------------- +// ASOptions class for options processing +// used by both console and library builds +//---------------------------------------------------------------------------- +class ASConsole; + +class ASOptions +{ +public: +#ifdef ASTYLE_LIB + ASOptions(ASFormatter& formatterArg); +#else + ASOptions(ASFormatter& formatterArg, ASConsole& consoleArg); +#endif + std::string getOptionErrors() const; + void importOptions(std::stringstream& in, std::vector& optionsVector); + bool parseOptions(std::vector& optionsVector); + +private: + // variables + ASFormatter& formatter; + std::stringstream optionErrors; // option error messages +#ifndef ASTYLE_LIB + ASConsole& console; // DO NOT USE for ASTYLE_LIB +#endif + + // functions + std::string getParam(const std::string& arg, const char* op); + std::string getParam(const std::string& arg, const char* op1, const char* op2); + bool isOption(const std::string& arg, const char* op); + bool isOption(const std::string& arg, const char* op1, const char* op2); + void isOptionError(const std::string& arg); + bool isParamOption(const std::string& arg, const char* option); + bool isParamOption(const std::string& arg, const char* option1, const char* option2); + void parseOption(const std::string& arg); + bool parseOptionContinued(const std::string& arg); +}; + +#ifndef ASTYLE_LIB + +//---------------------------------------------------------------------------- +// ASConsole class for console build +//---------------------------------------------------------------------------- + +class ASConsole +{ +private: // variables + ASFormatter& formatter; // reference to the ASFormatter object + ASEncoding encode; // file encoding conversion + ASLocalizer localizer; // language translation + std::ostream* errorStream; // direct error messages to cerr or std::cout + // command line options + bool isRecursive; // recursive option + bool isDryRun; // dry-run option + bool noBackup; // suffix=none option + bool preserveDate; // preserve-date option + bool isVerbose; // verbose option + bool isQuiet; // quiet option + bool isFormattedOnly; // formatted lines only option + bool ignoreExcludeErrors; // don't abort on unmatched excludes + bool ignoreExcludeErrorsDisplay; // don't display unmatched excludes + bool useAscii; // ascii option + // other variables + bool bypassBrowserOpen; // don't open the browser on html options + bool hasWildcard; // file name includes a wildcard + size_t mainDirectoryLength; // directory length to be excluded in displays + bool filesAreIdentical; // input and output files are identical + bool acceptEmptyFileList; // empty input file list will not be reported as error + bool rejectDryRunWithFormat; // formatted files in dry run mode will be reported as error + + int filesFormatted; // number of files formatted + int filesUnchanged; // number of files unchanged + int linesOut; // number of output lines + + std::string outputEOL; // current line end + + std::string astyleExePath; // absolute executable path and name from argv[0] + std::string optionFileName; // file path and name of the options file + std::string origSuffix; // suffix= option + std::string projectOptionFileName; // file path and name of the project options file + std::string stdPathIn; // path to input from stdin= + std::string stdPathOut; // path to output from stdout= + std::string targetDirectory; // path to the directory being processed + std::string targetFilename; // file name being processed + + std::vector excludeVector; // exclude from wildcard hits + std::vector excludeHitsVector; // exclude flags for error reporting + std::vector fileNameVector; // file paths and names from the command line + std::vector optionsVector; // options from the command line + std::vector projectOptionsVector;// project options from the project options file + std::vector fileOptionsVector; // options from the options file + std::vector fileName; // files to be processed including path + +public: // functions + explicit ASConsole(ASFormatter& formatterArg); + ASConsole(const ASConsole&) = delete; + ASConsole& operator=(ASConsole const&) = delete; + + FileEncoding detectEncoding(const char* data, size_t dataSize) const; + void error() const; + void error(const char* why, const char* what) const; + void formatCinToCout(); + std::vector getArgvOptions(int argc, char** argv); + bool fileExists(const char* file) const; + bool fileNameVectorIsEmpty() const; + std::ostream* getErrorStream() const; + bool getFilesAreIdentical() const; + int getFilesFormatted() const; + bool getIgnoreExcludeErrors() const; + bool getIgnoreExcludeErrorsDisplay() const; + bool getIsDryRun() const; + bool getIsFormattedOnly() const; + bool getIsQuiet() const; + bool getIsRecursive() const; + bool getIsVerbose() const; + bool getLineEndsMixed() const; + bool getNoBackup() const; + bool getPreserveDate() const; + + std::string getLanguageID() const; + std::string getNumberFormat(int num, size_t lcid = 0) const; + std::string getNumberFormat(int num, const char* groupingArg, const char* separator) const; + std::string getOptionFileName() const; + std::string getOrigSuffix() const; + std::string getProjectOptionFileName() const; + std::string getStdPathIn() const; + std::string getStdPathOut() const; + void getTargetFilenames(std::string& targetFilename_, std::vector& targetFilenameVector) const; + void processFiles(); + void processOptions(const std::vector& argvOptions); + void setBypassBrowserOpen(bool state); + void setErrorStream(std::ostream* errStreamPtr); + void setIgnoreExcludeErrors(bool state); + void setIgnoreExcludeErrorsAndDisplay(bool state); + void setIsDryRun(bool state); + void setRejectFormatWithDryRun(bool state); + void setIsFormattedOnly(bool state); + void setIsQuiet(bool state); + void setIsRecursive(bool state); + void setIsVerbose(bool state); + void setNoBackup(bool state); + void setOptionFileName(const std::string& name); + void setOrigSuffix(const std::string& suffix); + void setPreserveDate(bool state); + void setProjectOptionFileName(const std::string& optfilepath); + void setStdPathIn(const std::string& path); + void setStdPathOut(const std::string& path); + void setAcceptEmptyInputFileList(bool state); + void standardizePath(std::string& path, bool removeBeginningSeparator = false) const; + bool stringEndsWith(std::string_view str, std::string_view suffix) const; + void updateExcludeVector(const std::string& suffixParam); + + std::vector getExcludeVector() const; + std::vector getExcludeHitsVector() const; + std::vector getFileNameVector() const; + std::vector getOptionsVector() const; + std::vector getProjectOptionsVector() const; + std::vector getFileOptionsVector() const; + std::vector getFileName() const; + +private: // functions + + void formatFile(const std::string& fileName_); + std::string getParentDirectory(const std::string& absPath) const; + std::string findProjectOptionFilePath(const std::string& fileName_) const; + std::string getCurrentDirectory(const std::string& fileName_) const; + void getFileNames(const std::string& directory, const std::vector& wildcards); + void getFilePaths(const std::string& filePath); + std::string getFullPathName(const std::string& relativePath) const; + std::string getHtmlInstallPrefix() const; + std::string getParam(const std::string& arg, const char* op); + bool isHomeOrInvalidAbsPath(const std::string& absPath) const; + bool isWriteable(const std::filesystem::path& path) const; + void initializeOutputEOL(LineEndFormat lineEndFormat); + bool isOption(const std::string& arg, const char* op); + bool isOption(const std::string& arg, const char* a, const char* b); + bool isParamOption(const std::string& arg, const char* option); + bool isPathExcluded(const std::string& subPath); + void launchDefaultBrowser(const char* filePathIn = nullptr) const; + void printHelp() const; + void printMsg(const char* msg, const std::string& data) const; + void printSeparatingLine() const; + void printVerboseHeader() const; + void printVerboseStats(clock_t startTime) const; + FileEncoding readFile(const std::string& fileName_, std::stringstream& in) const; + void removeFile(const char* fileName_, const char* errMsg) const; + void renameFile(const char* oldFileName, const char* newFileName, const char* errMsg) const; + void sleep(int seconds) const; + int waitForRemove(const char* newFileName) const; + int wildcmp(const char* wild, const char* data) const; + void writeFile(const std::string& fileName_, FileEncoding encoding, std::ostringstream& out) const; +#ifdef _WIN32 + void displayLastError(); +#endif +}; +#else // ASTYLE_LIB + +//---------------------------------------------------------------------------- +// ASLibrary class for library build +//---------------------------------------------------------------------------- + +class ASLibrary +{ +public: + ASLibrary() = default; + virtual ~ASLibrary() = default; + // virtual functions are mocked in testing + char16_t* formatUtf16(const char16_t*, const char16_t*, fpError, fpAlloc) const; + virtual char16_t* convertUtf8ToUtf16(const char* utf8In, fpAlloc fpMemoryAlloc) const; + virtual char* convertUtf16ToUtf8(const char16_t* utf16In) const; + +private: + static char* STDCALL tempMemoryAllocation(unsigned long memoryNeeded); + +private: + ASEncoding encode; // file encoding conversion +}; + +#endif // ASTYLE_LIB + +//---------------------------------------------------------------------------- + +} // end of namespace astyle + +//---------------------------------------------------------------------------- +// declarations for java native interface (JNI) build +// they are called externally and are NOT part of the namespace +//---------------------------------------------------------------------------- +#ifdef ASTYLE_JNI +void STDCALL javaErrorHandler(int errorNumber, const char* errorMessage); +char* STDCALL javaMemoryAlloc(unsigned long memoryNeeded); +// the following function names are constructed from method names in the calling java program + +extern "C" EXPORT +jstring STDCALL Java_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass); +extern "C" EXPORT +jstring STDCALL Java_AStyleInterface_AStyleMain(JNIEnv* env, + jobject obj, + jstring textInJava, + jstring optionsJava); +extern "C" EXPORT +jstring STDCALL Java_cc_arduino_packages_formatter_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass); +extern "C" EXPORT +jstring STDCALL Java_cc_arduino_packages_formatter_AStyleInterface_AStyleMain(JNIEnv* env, + jobject obj, + jstring textInJava, + jstring optionsJava); +#endif // ASTYLE_JNI + +//---------------------------------------------------------------------------- +// declarations for UTF-16 interface +// they are called externally and are NOT part of the namespace +//---------------------------------------------------------------------------- +#ifdef ASTYLE_LIB +extern "C" EXPORT +char16_t* STDCALL AStyleMainUtf16(const char16_t* pSourceIn, + const char16_t* pOptions, + fpError fpErrorHandler, + fpAlloc fpMemoryAlloc); +#endif // ASTYLE_LIB + +//----------------------------------------------------------------------------- +// declarations for standard DLL interface +// they are called externally and are NOT part of the namespace +//----------------------------------------------------------------------------- +#ifdef ASTYLE_LIB +extern "C" EXPORT char* STDCALL AStyleMain(const char* pSourceIn, + const char* pOptions, + fpError fpErrorHandler, + fpAlloc fpMemoryAlloc); +extern "C" EXPORT const char* STDCALL AStyleGetVersion(void); +#endif // ASTYLE_LIB + +//----------------------------------------------------------------------------- + +#endif // closes ASTYLE_MAIN_H diff --git a/filesystem.h b/filesystem.h new file mode 100644 index 0000000..1ea0f0e --- /dev/null +++ b/filesystem.h @@ -0,0 +1,184 @@ +#ifndef __FILESYSTEM_H__ +#define __FILESYSTEM_H__ + +#include +#include +#include +#include +#include + +namespace std +{ + +namespace filesystem +{ + +class path +{ +public: + using value_type = char; + using string_type = std::basic_string< value_type >; + constexpr static size_t max_path = 256u; + + constexpr static value_type preferred_separator = '/'; + inline bool is_directory_separator(path::value_type c) noexcept + { + return c == preferred_separator; + } + + template + path(const std::basic_string& s) : m_pathname(s) {}; + + path() noexcept {} + path(path const& p) : m_pathname(p.m_pathname) {} + path(const value_type* s) : m_pathname(s) {} + + path(path&& p) noexcept : m_pathname(static_cast< string_type&& >(p.m_pathname)) + { + } + path& operator=(path&& p) noexcept + { + m_pathname = static_cast< string_type&& >(p.m_pathname); + return *this; + } + path& assign(path&& p) noexcept + { + m_pathname = static_cast< string_type&& >(p.m_pathname); + return *this; + } + path(string_type&& s) noexcept : m_pathname(static_cast< string_type&& >(s)) + { + } + path& operator=(string_type&& p) noexcept + { + m_pathname = static_cast< string_type&& >(p); + return *this; + } + path& assign(string_type&& p) noexcept + { + m_pathname = static_cast< string_type&& >(p); + return *this; + } + path(const value_type* begin, const value_type* end) : m_pathname(begin, end) {} + path& assign(path const& p) + { + m_pathname = p.m_pathname; + return *this; + } + path& assign(const value_type* begin, const value_type* end) + { + m_pathname.assign(begin, end); + return *this; + } + path& operator+=(value_type c) + { + m_pathname.push_back(c); + return *this; + } + path& operator+=(const string_type& s) + { + m_pathname.append(s); + return *this; + } + path& concat(path const& p) + { + m_pathname.append(p.m_pathname); + return *this; + } + path& concat(const value_type* begin, const value_type* end) + { + m_pathname.append(begin, end); + return *this; + } + + static string_type& quoted(const string_type& s, value_type q = '"') + { + return string_type{}.append(1,q).append(s).append(1,q); + } + + string_type string() const + { + return m_pathname; + } + + path& operator/= (const path& p) + { + m_pathname = m_pathname + preferred_separator + p.m_pathname; + return *this; + } + + bool empty() const noexcept { return m_pathname.empty(); } + + const value_type* c_str() const noexcept + { + return m_pathname.c_str(); + } + +private: + string_type m_pathname; +}; + + template< typename Char, typename Traits > +inline std::basic_ostream< Char, Traits >& +operator<<(std::basic_ostream< Char, Traits >& os, path const& p) +{ + return os << path::quoted(p.string()); +} + +template< typename Char, typename Traits > +inline std::basic_istream< Char, Traits >& +operator>>(std::basic_istream< Char, Traits >& is, path& p) +{ + std::basic_string< Char > str; + is >> path::quoted(p.string()); + p = str; + return is; +} + +path operator/(path lhs, path const& rhs) + { + return lhs.concat(path::string_type{path::preferred_separator}).concat(rhs); + } + +path current_path() +{ + char buf[path::max_path]; + memset(buf, 0, path::max_path); + const auto rv = _ksys_getcwd(buf, path::max_path - 1u); + return (rv == NULL) ? path{} : path{buf}; +} + +inline bool is_directory(path const& p) +{ + struct stat sb; + if (stat(p.string().c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) + { + return true; + } + return false; +} + +inline bool is_regular_file(path const& p) +{ + struct stat sb; + if (stat(p.string().c_str(), &sb) == 0 && S_ISREG(sb.st_mode)) + { + return true; + } + return false; +} + +bool exists(path const& p) +{ + return (is_regular_file(p) || is_directory(p)); +} + +path absolute(path const& p) +{ + return p; // TODO +} + +} // namespace filesystem +} // namespace std + +#endif // ~__FILESYSTEM_H__ \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fbda024 --- /dev/null +++ b/main.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filesystem.h" + +namespace fs = std::filesystem; + +// add this to astyle.h to compile under toolchain +namespace std +{ + using string_view=std::experimental::fundamentals_v1::string_view; + int isblank(int c){return ::isblank(c);} +} + +void check_directory(const fs::path& p) +{ + std::cout << "checking directory " << p << std::endl; + std::cout << std::boolalpha << "result : " << fs::is_directory(p) << std::endl; +} + +void check_file(const fs::path& p) +{ + std::cout << "checking file " << p << std::endl; + std::cout << std::boolalpha << "result : " << fs::is_regular_file(p) << std::endl; +} + + +int main(int argc, char** argv) { + + + const auto cwd = fs::current_path(); + std::cout << "cwd = " << cwd << std::endl; + + check_directory(cwd); + check_directory(cwd / "main"); + + check_directory("abcabc"); + + check_file(cwd); + check_file(cwd / "main"); + + check_file("abcabc"); + + std::isblank(15); + + return 0; +}