1 // ASFormatter.cpp
2 // Copyright (c) 2018 by Jim Pattee <jimp03@email.com>.
3 // This code is licensed under the MIT License.
4 // License.md describes the conditions under which this software may be distributed.
5 
6 //-----------------------------------------------------------------------------
7 // headers
8 //-----------------------------------------------------------------------------
9 
10 #include "astyle.h"
11 
12 #include <algorithm>
13 #include <fstream>
14 
15 //-----------------------------------------------------------------------------
16 // astyle namespace
17 //-----------------------------------------------------------------------------
18 
19 namespace astyle {
20 //
21 //-----------------------------------------------------------------------------
22 // ASFormatter class
23 //-----------------------------------------------------------------------------
24 
25 /**
26  * Constructor of ASFormatter
27  */
ASFormatter()28 ASFormatter::ASFormatter()
29 {
30 	sourceIterator = nullptr;
31 	enhancer = new ASEnhancer;
32 	preBraceHeaderStack = nullptr;
33 	braceTypeStack = nullptr;
34 	parenStack = nullptr;
35 	structStack = nullptr;
36 	questionMarkStack = nullptr;
37 	lineCommentNoIndent = false;
38 	formattingStyle = STYLE_NONE;
39 	braceFormatMode = NONE_MODE;
40 	pointerAlignment = PTR_ALIGN_NONE;
41 	referenceAlignment = REF_SAME_AS_PTR;
42 	objCColonPadMode = COLON_PAD_NO_CHANGE;
43 	lineEnd = LINEEND_DEFAULT;
44 	maxCodeLength = string::npos;
45 	shouldPadCommas = false;
46 	shouldPadOperators = false;
47 	shouldPadParensOutside = false;
48 	shouldPadFirstParen = false;
49 	shouldPadParensInside = false;
50 	shouldPadHeader = false;
51 	shouldStripCommentPrefix = false;
52 	shouldUnPadParens = false;
53 	attachClosingBraceMode = false;
54 	shouldBreakOneLineBlocks = true;
55 	shouldBreakOneLineHeaders = false;
56 	shouldBreakOneLineStatements = true;
57 	shouldConvertTabs = false;
58 	shouldIndentCol1Comments = false;
59 	shouldIndentPreprocBlock = false;
60 	shouldCloseTemplates = false;
61 	shouldAttachExternC = false;
62 	shouldAttachNamespace = false;
63 	shouldAttachClass = false;
64 	shouldAttachClosingWhile = false;
65 	shouldAttachInline = false;
66 	shouldBreakBlocks = false;
67 	shouldBreakClosingHeaderBlocks = false;
68 	shouldBreakClosingHeaderBraces = false;
69 	shouldDeleteEmptyLines = false;
70 	shouldBreakReturnType = false;
71 	shouldBreakReturnTypeDecl = false;
72 	shouldAttachReturnType = false;
73 	shouldAttachReturnTypeDecl = false;
74 	shouldBreakElseIfs = false;
75 	shouldBreakLineAfterLogical = false;
76 	shouldAddBraces = false;
77 	shouldAddOneLineBraces = false;
78 	shouldRemoveBraces = false;
79 	shouldPadMethodColon = false;
80 	shouldPadMethodPrefix = false;
81 	shouldUnPadMethodPrefix = false;
82 	shouldPadReturnType = false;
83 	shouldUnPadReturnType = false;
84 	shouldPadParamType = false;
85 	shouldUnPadParamType = false;
86 
87 	// initialize ASFormatter member vectors
88 	formatterFileType = 9;		// reset to an invalid type
89 	headers = new vector<const string*>;
90 	nonParenHeaders = new vector<const string*>;
91 	preDefinitionHeaders = new vector<const string*>;
92 	preCommandHeaders = new vector<const string*>;
93 	operators = new vector<const string*>;
94 	assignmentOperators = new vector<const string*>;
95 	castOperators = new vector<const string*>;
96 
97 	// initialize ASEnhancer member vectors
98 	indentableMacros = new vector<const pair<const string, const string>* >;
99 }
100 
101 /**
102  * Destructor of ASFormatter
103  */
~ASFormatter()104 ASFormatter::~ASFormatter()
105 {
106 	// delete ASFormatter stack vectors
107 	deleteContainer(preBraceHeaderStack);
108 	deleteContainer(braceTypeStack);
109 	deleteContainer(parenStack);
110 	deleteContainer(structStack);
111 	deleteContainer(questionMarkStack);
112 
113 	// delete ASFormatter member vectors
114 	formatterFileType = 9;		// reset to an invalid type
115 	delete headers;
116 	delete nonParenHeaders;
117 	delete preDefinitionHeaders;
118 	delete preCommandHeaders;
119 	delete operators;
120 	delete assignmentOperators;
121 	delete castOperators;
122 
123 	// delete ASEnhancer member vectors
124 	delete indentableMacros;
125 
126 	// must be done when the ASFormatter object is deleted (not ASBeautifier)
127 	// delete ASBeautifier member vectors
128 	ASBeautifier::deleteBeautifierVectors();
129 
130 	delete enhancer;
131 }
132 
133 /**
134  * initialize the ASFormatter.
135  *
136  * init() should be called every time a ASFormatter object is to start
137  * formatting a NEW source file.
138  * init() receives a pointer to a ASSourceIterator object that will be
139  * used to iterate through the source code.
140  *
141  * @param si        a pointer to the ASSourceIterator or ASStreamIterator object.
142  */
init(ASSourceIterator * si)143 void ASFormatter::init(ASSourceIterator* si)
144 {
145 	buildLanguageVectors();
146 	fixOptionVariableConflicts();
147 	ASBeautifier::init(si);
148 	sourceIterator = si;
149 
150 	enhancer->init(getFileType(),
151 	               getIndentLength(),
152 	               getTabLength(),
153 	               getIndentString() == "\t",
154 	               getForceTabIndentation(),
155 	               getNamespaceIndent(),
156 	               getCaseIndent(),
157 	               shouldIndentPreprocBlock,
158 	               getPreprocDefineIndent(),
159 	               getEmptyLineFill(),
160 	               indentableMacros);
161 
162 	initContainer(preBraceHeaderStack, new vector<const string*>);
163 	initContainer(parenStack, new vector<int>);
164 	initContainer(structStack, new vector<bool>);
165 	initContainer(questionMarkStack, new vector<bool>);
166 	parenStack->emplace_back(0);               // parenStack must contain this default entry
167 	initContainer(braceTypeStack, new vector<BraceType>);
168 	braceTypeStack->emplace_back(NULL_TYPE);   // braceTypeStack must contain this default entry
169 	clearFormattedLineSplitPoints();
170 
171 	currentHeader = nullptr;
172 	currentLine = "";
173 	readyFormattedLine = "";
174 	formattedLine = "";
175 	verbatimDelimiter = "";
176 	currentChar = ' ';
177 	previousChar = ' ';
178 	previousCommandChar = ' ';
179 	previousNonWSChar = ',';	// not a potential name or operator
180 	quoteChar = '"';
181 	preprocBlockEnd = 0;
182 	charNum = 0;
183 	checksumIn = 0;
184 	checksumOut = 0;
185 	currentLineFirstBraceNum = string::npos;
186 	formattedLineCommentNum = 0;
187 	leadingSpaces = 0;
188 	previousReadyFormattedLineLength = string::npos;
189 	preprocBraceTypeStackSize = 0;
190 	spacePadNum = 0;
191 	methodAttachCharNum = string::npos;
192 	methodAttachLineNum = 0;
193 	methodBreakCharNum = string::npos;
194 	methodBreakLineNum = 0;
195 	nextLineSpacePadNum = 0;
196 	objCColonAlign = 0;
197 	templateDepth = 0;
198 	squareBracketCount = 0;
199 	runInIndentChars = 0;
200 	tabIncrementIn = 0;
201 	previousBraceType = NULL_TYPE;
202 
203 	isVirgin = true;
204 	isInVirginLine = true;
205 	isInLineComment = false;
206 	isInComment = false;
207 	isInCommentStartLine = false;
208 	noTrimCommentContinuation = false;
209 	isInPreprocessor = false;
210 	isInPreprocessorBeautify = false;
211 	doesLineStartComment = false;
212 	lineEndsInCommentOnly = false;
213 	lineIsCommentOnly = false;
214 	lineIsLineCommentOnly = false;
215 	lineIsEmpty = false;
216 	isImmediatelyPostCommentOnly = false;
217 	isImmediatelyPostEmptyLine = false;
218 	isInClassInitializer = false;
219 	isInQuote = false;
220 	isInVerbatimQuote = false;
221 	haveLineContinuationChar = false;
222 	isInQuoteContinuation = false;
223 	isHeaderInMultiStatementLine = false;
224 	isSpecialChar = false;
225 	isNonParenHeader = false;
226 	foundNamespaceHeader = false;
227 	foundClassHeader = false;
228 	foundStructHeader = false;
229 	foundInterfaceHeader = false;
230 	foundPreDefinitionHeader = false;
231 	foundPreCommandHeader = false;
232 	foundPreCommandMacro = false;
233 	foundTrailingReturnType = false;
234 	foundCastOperator = false;
235 	foundQuestionMark = false;
236 	isInLineBreak = false;
237 	endOfAsmReached = false;
238 	endOfCodeReached = false;
239 	isFormattingModeOff = false;
240 	isInEnum = false;
241 	isInExecSQL = false;
242 	isInAsm = false;
243 	isInAsmOneLine = false;
244 	isInAsmBlock = false;
245 	isLineReady = false;
246 	elseHeaderFollowsComments = false;
247 	caseHeaderFollowsComments = false;
248 	isPreviousBraceBlockRelated = false;
249 	isInPotentialCalculation = false;
250 	needHeaderOpeningBrace = false;
251 	shouldBreakLineAtNextChar = false;
252 	shouldKeepLineUnbroken = false;
253 	shouldReparseCurrentChar = false;
254 	passedSemicolon = false;
255 	passedColon = false;
256 	isImmediatelyPostNonInStmt = false;
257 	isCharImmediatelyPostNonInStmt = false;
258 	isInTemplate = false;
259 	isImmediatelyPostComment = false;
260 	isImmediatelyPostLineComment = false;
261 	isImmediatelyPostEmptyBlock = false;
262 	isImmediatelyPostObjCMethodPrefix = false;
263 	isImmediatelyPostPreprocessor = false;
264 	isImmediatelyPostReturn = false;
265 	isImmediatelyPostThrow = false;
266 	isImmediatelyPostNewDelete = false;
267 	isImmediatelyPostOperator = false;
268 	isImmediatelyPostTemplate = false;
269 	isImmediatelyPostPointerOrReference = false;
270 	isCharImmediatelyPostReturn = false;
271 	isCharImmediatelyPostThrow = false;
272 	isCharImmediatelyPostNewDelete = false;
273 	isCharImmediatelyPostOperator = false;
274 	isCharImmediatelyPostComment = false;
275 	isPreviousCharPostComment = false;
276 	isCharImmediatelyPostLineComment = false;
277 	isCharImmediatelyPostOpenBlock = false;
278 	isCharImmediatelyPostCloseBlock = false;
279 	isCharImmediatelyPostTemplate = false;
280 	isCharImmediatelyPostPointerOrReference = false;
281 	isInObjCInterface = false;
282 	isInObjCMethodDefinition = false;
283 	isInObjCReturnType = false;
284 	isInObjCParam = false;
285 	isInObjCSelector = false;
286 	breakCurrentOneLineBlock = false;
287 	shouldRemoveNextClosingBrace = false;
288 	isInBraceRunIn = false;
289 	returnTypeChecked = false;
290 	currentLineBeginsWithBrace = false;
291 	isPrependPostBlockEmptyLineRequested = false;
292 	isAppendPostBlockEmptyLineRequested = false;
293 	isIndentableProprocessor = false;
294 	isIndentableProprocessorBlock = false;
295 	prependEmptyLine = false;
296 	appendOpeningBrace = false;
297 	foundClosingHeader = false;
298 	isImmediatelyPostHeader = false;
299 	isInHeader = false;
300 	isInCase = false;
301 	isFirstPreprocConditional = false;
302 	processedFirstConditional = false;
303 	isJavaStaticConstructor = false;
304 }
305 
306 /**
307  * build vectors for each programing language
308  * depending on the file extension.
309  */
buildLanguageVectors()310 void ASFormatter::buildLanguageVectors()
311 {
312 	if (getFileType() == formatterFileType)  // don't build unless necessary
313 		return;
314 
315 	formatterFileType = getFileType();
316 
317 	headers->clear();
318 	nonParenHeaders->clear();
319 	preDefinitionHeaders->clear();
320 	preCommandHeaders->clear();
321 	operators->clear();
322 	assignmentOperators->clear();
323 	castOperators->clear();
324 	indentableMacros->clear();	// ASEnhancer
325 
326 	ASResource::buildHeaders(headers, getFileType());
327 	ASResource::buildNonParenHeaders(nonParenHeaders, getFileType());
328 	ASResource::buildPreDefinitionHeaders(preDefinitionHeaders, getFileType());
329 	ASResource::buildPreCommandHeaders(preCommandHeaders, getFileType());
330 	ASResource::buildOperators(operators, getFileType());
331 	ASResource::buildAssignmentOperators(assignmentOperators);
332 	ASResource::buildCastOperators(castOperators);
333 	ASResource::buildIndentableMacros(indentableMacros);	//ASEnhancer
334 }
335 
336 /**
337  * set the variables for each predefined style.
338  * this will override any previous settings.
339  */
fixOptionVariableConflicts()340 void ASFormatter::fixOptionVariableConflicts()
341 {
342 	if (formattingStyle == STYLE_ALLMAN)
343 	{
344 		setBraceFormatMode(BREAK_MODE);
345 	}
346 	else if (formattingStyle == STYLE_JAVA)
347 	{
348 		setBraceFormatMode(ATTACH_MODE);
349 	}
350 	else if (formattingStyle == STYLE_KR)
351 	{
352 		setBraceFormatMode(LINUX_MODE);
353 	}
354 	else if (formattingStyle == STYLE_STROUSTRUP)
355 	{
356 		setBraceFormatMode(LINUX_MODE);
357 		setBreakClosingHeaderBracesMode(true);
358 	}
359 	else if (formattingStyle == STYLE_WHITESMITH)
360 	{
361 		setBraceFormatMode(BREAK_MODE);
362 		setBraceIndent(true);
363 		setClassIndent(true);			// avoid hanging indent with access modifiers
364 		setSwitchIndent(true);			// avoid hanging indent with case statements
365 	}
366 	else if (formattingStyle == STYLE_VTK)
367 	{
368 		// the unindented class brace does NOT cause a hanging indent like Whitesmith
369 		setBraceFormatMode(BREAK_MODE);
370 		setBraceIndentVtk(true);		// sets both braceIndent and braceIndentVtk
371 		setSwitchIndent(true);			// avoid hanging indent with case statements
372 	}
373 	else if (formattingStyle == STYLE_RATLIFF)
374 	{
375 		// attached braces can have hanging indents with the closing brace
376 		setBraceFormatMode(ATTACH_MODE);
377 		setBraceIndent(true);
378 		setClassIndent(true);			// avoid hanging indent with access modifiers
379 		setSwitchIndent(true);			// avoid hanging indent with case statements
380 	}
381 	else if (formattingStyle == STYLE_GNU)
382 	{
383 		setBraceFormatMode(BREAK_MODE);
384 		setBlockIndent(true);
385 	}
386 	else if (formattingStyle == STYLE_LINUX)
387 	{
388 		setBraceFormatMode(LINUX_MODE);
389 		// always for Linux style
390 		setMinConditionalIndentOption(MINCOND_ONEHALF);
391 	}
392 	else if (formattingStyle == STYLE_HORSTMANN)
393 	{
394 		setBraceFormatMode(RUN_IN_MODE);
395 		setSwitchIndent(true);
396 	}
397 	else if (formattingStyle == STYLE_1TBS)
398 	{
399 		setBraceFormatMode(LINUX_MODE);
400 		setAddBracesMode(true);
401 		setRemoveBracesMode(false);
402 	}
403 	else if (formattingStyle == STYLE_GOOGLE)
404 	{
405 		setBraceFormatMode(ATTACH_MODE);
406 		setModifierIndent(true);
407 		setClassIndent(false);
408 	}
409 	else if (formattingStyle == STYLE_MOZILLA)
410 	{
411 		setBraceFormatMode(LINUX_MODE);
412 	}
413 	else if (formattingStyle == STYLE_PICO)
414 	{
415 		setBraceFormatMode(RUN_IN_MODE);
416 		setAttachClosingBraceMode(true);
417 		setSwitchIndent(true);
418 		setBreakOneLineBlocksMode(false);
419 		setBreakOneLineStatementsMode(false);
420 		// add-braces won't work for pico, but it could be fixed if necessary
421 		// both options should be set to true
422 		if (shouldAddBraces)
423 			shouldAddOneLineBraces = true;
424 	}
425 	else if (formattingStyle == STYLE_LISP)
426 	{
427 		setBraceFormatMode(ATTACH_MODE);
428 		setAttachClosingBraceMode(true);
429 		setBreakOneLineStatementsMode(false);
430 		// add-one-line-braces won't work for lisp
431 		// only shouldAddBraces should be set to true
432 		if (shouldAddOneLineBraces)
433 		{
434 			shouldAddBraces = true;
435 			shouldAddOneLineBraces = false;
436 		}
437 	}
438 	setMinConditionalIndentLength();
439 	// if not set by indent=force-tab-x set equal to indentLength
440 	if (getTabLength() == 0)
441 		setDefaultTabLength();
442 	// add-one-line-braces implies keep-one-line-blocks
443 	if (shouldAddOneLineBraces)
444 		setBreakOneLineBlocksMode(false);
445 	// don't allow add-braces and remove-braces
446 	if (shouldAddBraces || shouldAddOneLineBraces)
447 		setRemoveBracesMode(false);
448 	// don't allow break-return-type and attach-return-type
449 	if (shouldBreakReturnType)
450 		shouldAttachReturnType = false;
451 	if (shouldBreakReturnTypeDecl)
452 		shouldAttachReturnTypeDecl = false;
453 	// don't allow indent-classes and indent-modifiers
454 	if (getClassIndent())
455 		setModifierIndent(false);
456 }
457 
458 /**
459  * get the next formatted line.
460  *
461  * @return    formatted line.
462  */
nextLine()463 string ASFormatter::nextLine()
464 {
465 	const string* newHeader = nullptr;
466 	isInVirginLine = isVirgin;
467 	isCharImmediatelyPostComment = false;
468 	isPreviousCharPostComment = false;
469 	isCharImmediatelyPostLineComment = false;
470 	isCharImmediatelyPostOpenBlock = false;
471 	isCharImmediatelyPostCloseBlock = false;
472 	isCharImmediatelyPostTemplate = false;
473 
474 	while (!isLineReady)
475 	{
476 		if (shouldReparseCurrentChar)
477 			shouldReparseCurrentChar = false;
478 		else if (!getNextChar())
479 		{
480 			breakLine();
481 			continue;
482 		}
483 		else // stuff to do when reading a new character...
484 		{
485 			// make sure that a virgin '{' at the beginning of the file will be treated as a block...
486 			if (isInVirginLine && currentChar == '{'
487 			        && currentLineBeginsWithBrace
488 			        && previousCommandChar == ' ')
489 				previousCommandChar = '{';
490 			if (isInClassInitializer
491 			        && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
492 				isInClassInitializer = false;
493 			if (isInBraceRunIn)
494 				isInLineBreak = false;
495 			if (!isWhiteSpace(currentChar))
496 				isInBraceRunIn = false;
497 			isPreviousCharPostComment = isCharImmediatelyPostComment;
498 			isCharImmediatelyPostComment = false;
499 			isCharImmediatelyPostTemplate = false;
500 			isCharImmediatelyPostReturn = false;
501 			isCharImmediatelyPostThrow = false;
502 			isCharImmediatelyPostNewDelete = false;
503 			isCharImmediatelyPostOperator = false;
504 			isCharImmediatelyPostPointerOrReference = false;
505 			isCharImmediatelyPostOpenBlock = false;
506 			isCharImmediatelyPostCloseBlock = false;
507 		}
508 
509 		if ((lineIsLineCommentOnly || lineIsCommentOnly)
510 		        && currentLine.find("*INDENT-ON*", charNum) != string::npos
511 		        && isFormattingModeOff)
512 		{
513 			isFormattingModeOff = false;
514 			breakLine();
515 			formattedLine = currentLine;
516 			charNum = (int) currentLine.length() - 1;
517 			continue;
518 		}
519 		if (isFormattingModeOff)
520 		{
521 			breakLine();
522 			formattedLine = currentLine;
523 			charNum = (int) currentLine.length() - 1;
524 			continue;
525 		}
526 		if ((lineIsLineCommentOnly || lineIsCommentOnly)
527 		        && currentLine.find("*INDENT-OFF*", charNum) != string::npos)
528 		{
529 			isFormattingModeOff = true;
530 			if (isInLineBreak)			// is true if not the first line
531 				breakLine();
532 			formattedLine = currentLine;
533 			charNum = (int) currentLine.length() - 1;
534 			continue;
535 		}
536 
537 		if (shouldBreakLineAtNextChar)
538 		{
539 			if (isWhiteSpace(currentChar) && !lineIsEmpty)
540 				continue;
541 			isInLineBreak = true;
542 			shouldBreakLineAtNextChar = false;
543 		}
544 
545 		if (isInExecSQL && !passedSemicolon)
546 		{
547 			if (currentChar == ';')
548 				passedSemicolon = true;
549 			appendCurrentChar();
550 			continue;
551 		}
552 
553 		if (isInLineComment)
554 		{
555 			formatLineCommentBody();
556 			continue;
557 		}
558 		else if (isInComment)
559 		{
560 			formatCommentBody();
561 			continue;
562 		}
563 
564 		else if (isInQuote)
565 		{
566 			formatQuoteBody();
567 			continue;
568 		}
569 
570 		// not in quote or comment or line comment
571 
572 		if (isSequenceReached("//"))
573 		{
574 			formatLineCommentOpener();
575 			testForTimeToSplitFormattedLine();
576 			continue;
577 		}
578 		else if (isSequenceReached("/*"))
579 		{
580 			formatCommentOpener();
581 			testForTimeToSplitFormattedLine();
582 			continue;
583 		}
584 		else if (currentChar == '"'
585 		         || (currentChar == '\'' && !isDigitSeparator(currentLine, charNum)))
586 		{
587 			formatQuoteOpener();
588 			testForTimeToSplitFormattedLine();
589 			continue;
590 		}
591 		// treat these preprocessor statements as a line comment
592 		else if (currentChar == '#'
593 		         && currentLine.find_first_not_of(" \t") == (size_t) charNum)
594 		{
595 			string preproc = trim(currentLine.c_str() + charNum + 1);
596 			if (preproc.length() > 0
597 			        && isCharPotentialHeader(preproc, 0)
598 			        && (findKeyword(preproc, 0, "region")
599 			            || findKeyword(preproc, 0, "endregion")
600 			            || findKeyword(preproc, 0, "error")
601 			            || findKeyword(preproc, 0, "warning")
602 			            || findKeyword(preproc, 0, "line")))
603 			{
604 				currentLine = rtrim(currentLine);	// trim the end only
605 				// check for run-in
606 				if (formattedLine.length() > 0 && formattedLine[0] == '{')
607 				{
608 					isInLineBreak = true;
609 					isInBraceRunIn = false;
610 				}
611 				if (previousCommandChar == '}')
612 					currentHeader = nullptr;
613 				isInLineComment = true;
614 				appendCurrentChar();
615 				continue;
616 			}
617 		}
618 
619 		if (isInPreprocessor)
620 		{
621 			appendCurrentChar();
622 			continue;
623 		}
624 
625 		if (isInTemplate && shouldCloseTemplates)
626 		{
627 			if (previousNonWSChar == '>' && isWhiteSpace(currentChar) && peekNextChar() == '>')
628 				continue;
629 		}
630 
631 		if (shouldRemoveNextClosingBrace && currentChar == '}')
632 		{
633 			currentLine[charNum] = currentChar = ' ';
634 			shouldRemoveNextClosingBrace = false;
635 			assert(adjustChecksumIn(-'}'));
636 			if (isEmptyLine(currentLine))
637 				continue;
638 		}
639 
640 		// handle white space - needed to simplify the rest.
641 		if (isWhiteSpace(currentChar))
642 		{
643 			appendCurrentChar();
644 			continue;
645 		}
646 
647 		/* not in MIDDLE of quote or comment or SQL or white-space of any type ... */
648 
649 		// check if in preprocessor
650 		// ** isInPreprocessor will be automatically reset at the beginning
651 		//    of a new line in getnextChar()
652 		if (currentChar == '#'
653 		        && currentLine.find_first_not_of(" \t") == (size_t) charNum
654 		        && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
655 		{
656 			isInPreprocessor = true;
657 			// check for run-in
658 			if (formattedLine.length() > 0 && formattedLine[0] == '{')
659 			{
660 				isInLineBreak = true;
661 				isInBraceRunIn = false;
662 			}
663 			processPreprocessor();
664 			// if top level it is potentially indentable
665 			if (shouldIndentPreprocBlock
666 			        && (isBraceType(braceTypeStack->back(), NULL_TYPE)
667 			            || isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
668 			        && !foundClassHeader
669 			        && !isInClassInitializer
670 			        && sourceIterator->tellg() > preprocBlockEnd)
671 			{
672 				// indent the #if preprocessor blocks
673 				string preproc = ASBeautifier::extractPreprocessorStatement(currentLine);
674 				if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef
675 				{
676 					if (isImmediatelyPostPreprocessor)
677 						breakLine();
678 					isIndentableProprocessorBlock = isIndentablePreprocessorBlock(currentLine, charNum);
679 					isIndentableProprocessor = isIndentableProprocessorBlock;
680 				}
681 			}
682 			if (isIndentableProprocessorBlock
683 			        && charNum < (int) currentLine.length() - 1
684 			        && isWhiteSpace(currentLine[charNum + 1]))
685 			{
686 				size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
687 				if (nextText != string::npos)
688 					currentLine.erase(charNum + 1, nextText - charNum - 1);
689 			}
690 			if (isIndentableProprocessorBlock
691 			        && sourceIterator->tellg() >= preprocBlockEnd)
692 				isIndentableProprocessorBlock = false;
693 			//  need to fall thru here to reset the variables
694 		}
695 
696 		/* not in preprocessor ... */
697 
698 		if (isImmediatelyPostComment)
699 		{
700 			caseHeaderFollowsComments = false;
701 			isImmediatelyPostComment = false;
702 			isCharImmediatelyPostComment = true;
703 		}
704 
705 		if (isImmediatelyPostLineComment)
706 		{
707 			caseHeaderFollowsComments = false;
708 			isImmediatelyPostLineComment = false;
709 			isCharImmediatelyPostLineComment = true;
710 		}
711 
712 		if (isImmediatelyPostReturn)
713 		{
714 			isImmediatelyPostReturn = false;
715 			isCharImmediatelyPostReturn = true;
716 		}
717 
718 		if (isImmediatelyPostThrow)
719 		{
720 			isImmediatelyPostThrow = false;
721 			isCharImmediatelyPostThrow = true;
722 		}
723 
724 		if (isImmediatelyPostNewDelete)
725 		{
726 			isImmediatelyPostNewDelete = false;
727 			isCharImmediatelyPostNewDelete = true;
728 		}
729 
730 		if (isImmediatelyPostOperator)
731 		{
732 			isImmediatelyPostOperator = false;
733 			isCharImmediatelyPostOperator = true;
734 		}
735 		if (isImmediatelyPostTemplate)
736 		{
737 			isImmediatelyPostTemplate = false;
738 			isCharImmediatelyPostTemplate = true;
739 		}
740 		if (isImmediatelyPostPointerOrReference)
741 		{
742 			isImmediatelyPostPointerOrReference = false;
743 			isCharImmediatelyPostPointerOrReference = true;
744 		}
745 
746 		// reset isImmediatelyPostHeader information
747 		if (isImmediatelyPostHeader)
748 		{
749 			// should braces be added
750 			if (currentChar != '{'
751 			        && shouldAddBraces
752 			        && currentChar != '#'	// don't add to preprocessor
753 			        && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine)
754 			        && isOkToBreakBlock(braceTypeStack->back()))
755 			{
756 				bool bracesAdded = addBracesToStatement();
757 				if (bracesAdded && !shouldAddOneLineBraces)
758 				{
759 					size_t firstText = currentLine.find_first_not_of(" \t");
760 					assert(firstText != string::npos);
761 					if ((int) firstText == charNum || shouldBreakOneLineHeaders)
762 						breakCurrentOneLineBlock = true;
763 				}
764 			}
765 			// should braces be removed
766 			else if (currentChar == '{' && shouldRemoveBraces)
767 			{
768 				bool bracesRemoved = removeBracesFromStatement();
769 				if (bracesRemoved)
770 				{
771 					shouldRemoveNextClosingBrace = true;
772 					if (isBeforeAnyLineEndComment(charNum))
773 						spacePadNum--;
774 					else if (shouldBreakOneLineBlocks
775 					         || (currentLineBeginsWithBrace
776 					             && currentLine.find_first_not_of(" \t") != string::npos))
777 						shouldBreakLineAtNextChar = true;
778 					continue;
779 				}
780 			}
781 
782 			// break 'else-if' if shouldBreakElseIfs is requested
783 			if (shouldBreakElseIfs
784 			        && currentHeader == &AS_ELSE
785 			        && isOkToBreakBlock(braceTypeStack->back())
786 			        && !isBeforeAnyComment()
787 			        && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine))
788 			{
789 				string nextText = peekNextText(currentLine.substr(charNum));
790 				if (nextText.length() > 0
791 				        && isCharPotentialHeader(nextText, 0)
792 				        && ASBase::findHeader(nextText, 0, headers) == &AS_IF)
793 				{
794 					isInLineBreak = true;
795 				}
796 			}
797 
798 			// break a header (e.g. if, while, else) from the following statement
799 			if (shouldBreakOneLineHeaders
800 			        && peekNextChar() != ' '
801 			        && (shouldBreakOneLineStatements
802 			            || (!isHeaderInMultiStatementLine
803 			                && !isMultiStatementLine()))
804 			        && isOkToBreakBlock(braceTypeStack->back())
805 			        && !isBeforeAnyComment())
806 			{
807 				if (currentChar == '{')
808 				{
809 					if (!currentLineBeginsWithBrace)
810 					{
811 						if (isOneLineBlockReached(currentLine, charNum) == 3)
812 							isInLineBreak = false;
813 						else
814 							breakCurrentOneLineBlock = true;
815 					}
816 				}
817 				else if (currentHeader == &AS_ELSE)
818 				{
819 					string nextText = peekNextText(currentLine.substr(charNum), true);
820 					if (nextText.length() > 0
821 					        && ((isCharPotentialHeader(nextText, 0)
822 					             && ASBase::findHeader(nextText, 0, headers) != &AS_IF)
823 					            || nextText[0] == '{'))
824 						isInLineBreak = true;
825 				}
826 				else
827 				{
828 					isInLineBreak = true;
829 				}
830 			}
831 
832 			isImmediatelyPostHeader = false;
833 		}
834 
835 		if (passedSemicolon)    // need to break the formattedLine
836 		{
837 			passedSemicolon = false;
838 			if (parenStack->back() == 0 && !isCharImmediatelyPostComment && currentChar != ';') // allow ;;
839 			{
840 				// does a one-line block have ending comments?
841 				if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
842 				{
843 					size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACE);
844 					assert(blockEnd != string::npos);
845 					// move ending comments to this formattedLine
846 					if (isBeforeAnyLineEndComment(blockEnd))
847 					{
848 						size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1);
849 						assert(commentStart != string::npos);
850 						assert((currentLine.compare(commentStart, 2, "//") == 0)
851 						       || (currentLine.compare(commentStart, 2, "/*") == 0));
852 						formattedLine.append(getIndentLength() - 1, ' ');
853 						// append comment
854 						int charNumSave = charNum;
855 						charNum = commentStart;
856 						while (charNum < (int) currentLine.length())
857 						{
858 							currentChar = currentLine[charNum];
859 							if (currentChar == '\t' && shouldConvertTabs)
860 								convertTabToSpaces();
861 							formattedLine.append(1, currentChar);
862 							++charNum;
863 						}
864 						size_t commentLength = currentLine.length() - commentStart;
865 						currentLine.erase(commentStart, commentLength);
866 						charNum = charNumSave;
867 						currentChar = currentLine[charNum];
868 						testForTimeToSplitFormattedLine();
869 					}
870 				}
871 				isInExecSQL = false;
872 				shouldReparseCurrentChar = true;
873 				if (formattedLine.find_first_not_of(" \t") != string::npos)
874 					isInLineBreak = true;
875 				if (needHeaderOpeningBrace)
876 				{
877 					isCharImmediatelyPostCloseBlock = true;
878 					needHeaderOpeningBrace = false;
879 				}
880 				continue;
881 			}
882 		}
883 
884 		if (passedColon)
885 		{
886 			passedColon = false;
887 			if (parenStack->back() == 0
888 			        && !isBeforeAnyComment()
889 			        && (formattedLine.find_first_not_of(" \t") != string::npos))
890 			{
891 				shouldReparseCurrentChar = true;
892 				isInLineBreak = true;
893 				continue;
894 			}
895 		}
896 
897 		// Check if in template declaration, e.g. foo<bar> or foo<bar,fig>
898 		if (!isInTemplate && currentChar == '<')
899 		{
900 			checkIfTemplateOpener();
901 		}
902 
903 		// Check for break return type
904 		if ((size_t) charNum >= methodBreakCharNum && methodBreakLineNum == 0)
905 		{
906 			if ((size_t) charNum == methodBreakCharNum)
907 				isInLineBreak = true;
908 			methodBreakCharNum = string::npos;
909 			methodBreakLineNum = 0;
910 		}
911 		// Check for attach return type
912 		if ((size_t) charNum >= methodAttachCharNum && methodAttachLineNum == 0)
913 		{
914 			if ((size_t) charNum == methodAttachCharNum)
915 			{
916 				int pa = pointerAlignment;
917 				int ra = referenceAlignment;
918 				int itemAlignment = (previousNonWSChar == '*' || previousNonWSChar == '^')
919 				                    ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
920 				isInLineBreak = false;
921 				if (previousNonWSChar == '*' || previousNonWSChar == '&' || previousNonWSChar == '^')
922 				{
923 					if (itemAlignment == REF_ALIGN_TYPE)
924 					{
925 						if (formattedLine.length() > 0
926 						        && !isWhiteSpace(formattedLine[formattedLine.length() - 1]))
927 							formattedLine.append(1, ' ');
928 					}
929 					else if (itemAlignment == REF_ALIGN_MIDDLE)
930 					{
931 						if (formattedLine.length() > 0
932 						        && !isWhiteSpace(formattedLine[formattedLine.length() - 1]))
933 							formattedLine.append(1, ' ');
934 					}
935 					else if (itemAlignment == REF_ALIGN_NAME)
936 					{
937 						if (formattedLine.length() > 0
938 						        && isWhiteSpace(formattedLine[formattedLine.length() - 1]))
939 							formattedLine.erase(formattedLine.length() - 1);
940 					}
941 					else
942 					{
943 						if (formattedLine.length() > 1
944 						        && !isWhiteSpace(formattedLine[formattedLine.length() - 2]))
945 							formattedLine.append(1, ' ');
946 					}
947 				}
948 				else
949 					formattedLine.append(1, ' ');
950 			}
951 			methodAttachCharNum = string::npos;
952 			methodAttachLineNum = 0;
953 		}
954 
955 		// handle parens
956 		if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<'))
957 		{
958 			questionMarkStack->push_back(foundQuestionMark);
959 			foundQuestionMark = false;
960 			parenStack->back()++;
961 			if (currentChar == '[')
962 			{
963 				++squareBracketCount;
964 				if (getAlignMethodColon() && squareBracketCount == 1 && isCStyle())
965 					objCColonAlign = findObjCColonAlignment();
966 			}
967 		}
968 		else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>'))
969 		{
970 			foundPreCommandHeader = false;
971 			parenStack->back()--;
972 			// this can happen in preprocessor directives
973 			if (parenStack->back() < 0)
974 				parenStack->back() = 0;
975 			if (!questionMarkStack->empty())
976 			{
977 				foundQuestionMark = questionMarkStack->back();
978 				questionMarkStack->pop_back();
979 			}
980 			if (isInTemplate && currentChar == '>')
981 			{
982 				templateDepth--;
983 				if (templateDepth == 0)
984 				{
985 					isInTemplate = false;
986 					isImmediatelyPostTemplate = true;
987 				}
988 			}
989 
990 			// check if this parenthesis closes a header, e.g. if (...), while (...)
991 			if (isInHeader && parenStack->back() == 0)
992 			{
993 				isInHeader = false;
994 				isImmediatelyPostHeader = true;
995 				foundQuestionMark = false;
996 			}
997 			if (currentChar == ']')
998 			{
999 				--squareBracketCount;
1000 				if (squareBracketCount <= 0)
1001 				{
1002 					squareBracketCount = 0;
1003 					objCColonAlign = 0;
1004 				}
1005 			}
1006 			if (currentChar == ')')
1007 			{
1008 				foundCastOperator = false;
1009 				if (parenStack->back() == 0)
1010 					endOfAsmReached = true;
1011 			}
1012 		}
1013 
1014 		// handle braces
1015 		if (currentChar == '{' || currentChar == '}')
1016 		{
1017 			// if appendOpeningBrace this was already done for the original brace
1018 			if (currentChar == '{' && !appendOpeningBrace)
1019 			{
1020 				BraceType newBraceType = getBraceType();
1021 				breakCurrentOneLineBlock = false;
1022 				foundNamespaceHeader = false;
1023 				foundClassHeader = false;
1024 				foundStructHeader = false;
1025 				foundInterfaceHeader = false;
1026 				foundPreDefinitionHeader = false;
1027 				foundPreCommandHeader = false;
1028 				foundPreCommandMacro = false;
1029 				foundTrailingReturnType = false;
1030 				isInPotentialCalculation = false;
1031 				isInObjCMethodDefinition = false;
1032 				isImmediatelyPostObjCMethodPrefix = false;
1033 				isInObjCInterface = false;
1034 				isInEnum = false;
1035 				isJavaStaticConstructor = false;
1036 				isCharImmediatelyPostNonInStmt = false;
1037 				needHeaderOpeningBrace = false;
1038 				shouldKeepLineUnbroken = false;
1039 				returnTypeChecked = false;
1040 				objCColonAlign = 0;
1041 				//assert(methodBreakCharNum == string::npos);	// comment out
1042 				//assert(methodBreakLineNum == 0);				// comment out
1043 				methodBreakCharNum = string::npos;
1044 				methodBreakLineNum = 0;
1045 				methodAttachCharNum = string::npos;
1046 				methodAttachLineNum = 0;
1047 
1048 				isPreviousBraceBlockRelated = !isBraceType(newBraceType, ARRAY_TYPE);
1049 				braceTypeStack->emplace_back(newBraceType);
1050 				preBraceHeaderStack->emplace_back(currentHeader);
1051 				currentHeader = nullptr;
1052 				structStack->push_back(isInIndentableStruct);
1053 				if (isBraceType(newBraceType, STRUCT_TYPE) && isCStyle())
1054 					isInIndentableStruct = isStructAccessModified(currentLine, charNum);
1055 				else
1056 					isInIndentableStruct = false;
1057 			}
1058 
1059 			// this must be done before the braceTypeStack is popped
1060 			BraceType braceType = braceTypeStack->back();
1061 			bool isOpeningArrayBrace = (isBraceType(braceType, ARRAY_TYPE)
1062 			                            && braceTypeStack->size() >= 2
1063 			                            && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2], ARRAY_TYPE)
1064 			                           );
1065 
1066 			if (currentChar == '}')
1067 			{
1068 				// if a request has been made to append a post block empty line,
1069 				// but the block exists immediately before a closing brace,
1070 				// then there is no need for the post block empty line.
1071 				isAppendPostBlockEmptyLineRequested = false;
1072 				if (isInAsm)
1073 					endOfAsmReached = true;
1074 				isInAsmOneLine = isInQuote = false;
1075 				shouldKeepLineUnbroken = false;
1076 				squareBracketCount = 0;
1077 
1078 				if (braceTypeStack->size() > 1)
1079 				{
1080 					previousBraceType = braceTypeStack->back();
1081 					braceTypeStack->pop_back();
1082 					isPreviousBraceBlockRelated = !isBraceType(braceType, ARRAY_TYPE);
1083 				}
1084 				else
1085 				{
1086 					previousBraceType = NULL_TYPE;
1087 					isPreviousBraceBlockRelated = false;
1088 				}
1089 
1090 				if (!preBraceHeaderStack->empty())
1091 				{
1092 					currentHeader = preBraceHeaderStack->back();
1093 					preBraceHeaderStack->pop_back();
1094 				}
1095 				else
1096 					currentHeader = nullptr;
1097 
1098 				if (!structStack->empty())
1099 				{
1100 					isInIndentableStruct = structStack->back();
1101 					structStack->pop_back();
1102 				}
1103 				else
1104 					isInIndentableStruct = false;
1105 
1106 				if (isNonInStatementArray
1107 				        && (!isBraceType(braceTypeStack->back(), ARRAY_TYPE)	// check previous brace
1108 				            || peekNextChar() == ';'))							// check for "};" added V2.01
1109 					isImmediatelyPostNonInStmt = true;
1110 
1111 				if (!shouldBreakOneLineStatements
1112 				        && ASBeautifier::getNextWord(currentLine, charNum) == AS_ELSE)
1113 				{
1114 					// handle special case of "else" at the end of line
1115 					size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
1116 					if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ')
1117 						shouldBreakLineAtNextChar = true;
1118 				}
1119 			}
1120 
1121 			// format braces
1122 			appendOpeningBrace = false;
1123 			if (isBraceType(braceType, ARRAY_TYPE))
1124 			{
1125 				formatArrayBraces(braceType, isOpeningArrayBrace);
1126 			}
1127 			else
1128 			{
1129 				if (currentChar == '{')
1130 					formatOpeningBrace(braceType);
1131 				else
1132 					formatClosingBrace(braceType);
1133 			}
1134 			continue;
1135 		}
1136 
1137 		if ((((previousCommandChar == '{' && isPreviousBraceBlockRelated)
1138 		        || ((previousCommandChar == '}'
1139 		             && !isImmediatelyPostEmptyBlock
1140 		             && isPreviousBraceBlockRelated
1141 		             && !isPreviousCharPostComment       // Fixes wrongly appended newlines after '}' immediately after comments
1142 		             && peekNextChar() != ' '
1143 		             && !isBraceType(previousBraceType, DEFINITION_TYPE))
1144 		            && !isBraceType(braceTypeStack->back(), DEFINITION_TYPE)))
1145 		        && isOkToBreakBlock(braceTypeStack->back()))
1146 		        // check for array
1147 		        || (previousCommandChar == '{'			// added 9/30/2010
1148 		            && isBraceType(braceTypeStack->back(), ARRAY_TYPE)
1149 		            && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
1150 		            && isNonInStatementArray)
1151 		        // check for pico one line braces
1152 		        || (formattingStyle == STYLE_PICO
1153 		            && (previousCommandChar == '{' && isPreviousBraceBlockRelated)
1154 		            && isBraceType(braceTypeStack->back(), COMMAND_TYPE)
1155 		            && isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
1156 		            && braceFormatMode == RUN_IN_MODE)
1157 		   )
1158 		{
1159 			isCharImmediatelyPostOpenBlock = (previousCommandChar == '{');
1160 			isCharImmediatelyPostCloseBlock = (previousCommandChar == '}');
1161 
1162 			if (isCharImmediatelyPostOpenBlock
1163 			        && !isCharImmediatelyPostComment
1164 			        && !isCharImmediatelyPostLineComment)
1165 			{
1166 				previousCommandChar = ' ';
1167 
1168 				if (braceFormatMode == NONE_MODE)
1169 				{
1170 					if (isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
1171 					        && (isBraceType(braceTypeStack->back(), BREAK_BLOCK_TYPE)
1172 					            || shouldBreakOneLineBlocks))
1173 						isInLineBreak = true;
1174 					else if (currentLineBeginsWithBrace)
1175 						formatRunIn();
1176 					else
1177 						breakLine();
1178 				}
1179 				else if (braceFormatMode == RUN_IN_MODE
1180 				         && currentChar != '#')
1181 					formatRunIn();
1182 				else
1183 					isInLineBreak = true;
1184 			}
1185 			else if (isCharImmediatelyPostCloseBlock
1186 			         && shouldBreakOneLineStatements
1187 			         && !isCharImmediatelyPostComment
1188 			         && ((isLegalNameChar(currentChar) && currentChar != '.')
1189 			             || currentChar == '+'
1190 			             || currentChar == '-'
1191 			             || currentChar == '*'
1192 			             || currentChar == '&'
1193 			             || currentChar == '('))
1194 			{
1195 				previousCommandChar = ' ';
1196 				isInLineBreak = true;
1197 			}
1198 		}
1199 
1200 		// reset block handling flags
1201 		isImmediatelyPostEmptyBlock = false;
1202 
1203 		// Objective-C method prefix with no return type
1204 		if (isImmediatelyPostObjCMethodPrefix && currentChar != '(')
1205 		{
1206 			if (shouldPadMethodPrefix || shouldUnPadMethodPrefix)
1207 				padObjCMethodPrefix();
1208 			isImmediatelyPostObjCMethodPrefix = false;
1209 		}
1210 
1211 		// look for headers
1212 		bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum);
1213 
1214 		if (isPotentialHeader && !isInTemplate && squareBracketCount == 0)
1215 		{
1216 			isNonParenHeader = false;
1217 			foundClosingHeader = false;
1218 
1219 			newHeader = findHeader(headers);
1220 
1221 			// java can have a 'default' not in a switch
1222 			if (newHeader == &AS_DEFAULT
1223 			        && ASBeautifier::peekNextChar(
1224 			            currentLine, charNum + (*newHeader).length() - 1) != ':')
1225 				newHeader = nullptr;
1226 			// Qt headers may be variables in C++
1227 			if (isCStyle()
1228 			        && (newHeader == &AS_FOREVER || newHeader == &AS_FOREACH))
1229 			{
1230 				if (currentLine.find_first_of("=;", charNum) != string::npos)
1231 					newHeader = nullptr;
1232 			}
1233 			if (isJavaStyle()
1234 			        && (newHeader == &AS_SYNCHRONIZED))
1235 			{
1236 				// want synchronized statements not synchronized methods
1237 				if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE))
1238 					newHeader = nullptr;
1239 			}
1240 			else if (newHeader == &AS_USING
1241 			         && ASBeautifier::peekNextChar(
1242 			             currentLine, charNum + (*newHeader).length() - 1) != '(')
1243 				newHeader = nullptr;
1244 
1245 			if (newHeader != nullptr)
1246 			{
1247 				foundClosingHeader = isClosingHeader(newHeader);
1248 
1249 				if (!foundClosingHeader)
1250 				{
1251 					// these are closing headers
1252 					if ((newHeader == &AS_WHILE && currentHeader == &AS_DO)
1253 					        || (newHeader == &_AS_FINALLY && currentHeader == &_AS_TRY)
1254 					        || (newHeader == &_AS_EXCEPT && currentHeader == &_AS_TRY))
1255 						foundClosingHeader = true;
1256 					// don't append empty block for these related headers
1257 					else if (isSharpStyle()
1258 					         && previousNonWSChar == '}'
1259 					         && ((newHeader == &AS_SET && currentHeader == &AS_GET)
1260 					             || (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
1261 					         && isOkToBreakBlock(braceTypeStack->back()))
1262 						isAppendPostBlockEmptyLineRequested = false;
1263 				}
1264 
1265 				// TODO: this can be removed in a future release
1266 				// version 3.0 - break erroneous attached header from previous versions
1267 				if (isSharpStyle()
1268 				        && ((newHeader == &AS_SET && currentHeader == &AS_GET)
1269 				            || (newHeader == &AS_REMOVE && currentHeader == &AS_ADD))
1270 				        && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
1271 				        && currentLine[currentLine.find_first_not_of(" \t")] == '}')
1272 					isInLineBreak = true;
1273 				// END TODO
1274 
1275 				const string* previousHeader = currentHeader;
1276 				currentHeader = newHeader;
1277 				needHeaderOpeningBrace = true;
1278 
1279 				// is the previous statement on the same line?
1280 				if ((previousNonWSChar == ';' || previousNonWSChar == ':')
1281 				        && !isInLineBreak
1282 				        && isOkToBreakBlock(braceTypeStack->back()))
1283 				{
1284 					// if breaking lines, break the line at the header
1285 					// except for multiple 'case' statements on a line
1286 					if (maxCodeLength != string::npos
1287 					        && previousHeader != &AS_CASE)
1288 						isInLineBreak = true;
1289 					else
1290 						isHeaderInMultiStatementLine = true;
1291 				}
1292 
1293 				if (foundClosingHeader && previousNonWSChar == '}')
1294 				{
1295 					if (isOkToBreakBlock(braceTypeStack->back()))
1296 						isLineBreakBeforeClosingHeader();
1297 
1298 					// get the adjustment for a comment following the closing header
1299 					if (isInLineBreak)
1300 						nextLineSpacePadNum = getNextLineCommentAdjustment();
1301 					else
1302 						spacePadNum = getCurrentLineCommentAdjustment();
1303 				}
1304 
1305 				// check if the found header is non-paren header
1306 				isNonParenHeader = findHeader(nonParenHeaders) != nullptr;
1307 
1308 				if (isNonParenHeader
1309 				        && (currentHeader == &AS_CATCH
1310 				            || currentHeader == &AS_CASE))
1311 				{
1312 					int startChar = charNum + currentHeader->length() - 1;
1313 					if (ASBeautifier::peekNextChar(currentLine, startChar) == '(')
1314 						isNonParenHeader = false;
1315 				}
1316 
1317 				// join 'else if' statements
1318 				if (currentHeader == &AS_IF
1319 				        && previousHeader == &AS_ELSE
1320 				        && isInLineBreak
1321 				        && !shouldBreakElseIfs
1322 				        && !isCharImmediatelyPostLineComment
1323 				        && !isImmediatelyPostPreprocessor)
1324 				{
1325 					// 'else' must be last thing on the line
1326 					size_t start = formattedLine.length() >= 6 ? formattedLine.length() - 6 : 0;
1327 					if (formattedLine.find(AS_ELSE, start) != string::npos)
1328 					{
1329 						appendSpacePad();
1330 						isInLineBreak = false;
1331 					}
1332 				}
1333 
1334 				appendSequence(*currentHeader);
1335 				goForward(currentHeader->length() - 1);
1336 				// if a paren-header is found add a space after it, if needed
1337 				// this checks currentLine, appendSpacePad() checks formattedLine
1338 				// in 'case' and C# 'catch' can be either a paren or non-paren header
1339 				if (shouldPadHeader
1340 				        && !isNonParenHeader
1341 				        && charNum < (int) currentLine.length() - 1 && !isWhiteSpace(currentLine[charNum + 1]))
1342 					appendSpacePad();
1343 
1344 				// Signal that a header has been reached
1345 				// *** But treat a closing while() (as in do...while)
1346 				//     as if it were NOT a header since a closing while()
1347 				//     should never have a block after it!
1348 				if (currentHeader != &AS_CASE && currentHeader != &AS_DEFAULT
1349 				        && !(foundClosingHeader && currentHeader == &AS_WHILE))
1350 				{
1351 					isInHeader = true;
1352 
1353 					// in C# 'catch' and 'delegate' can be a paren or non-paren header
1354 					if (isNonParenHeader && !isSharpStyleWithParen(currentHeader))
1355 					{
1356 						isImmediatelyPostHeader = true;
1357 						isInHeader = false;
1358 					}
1359 				}
1360 
1361 				if (shouldBreakBlocks
1362 				        && isOkToBreakBlock(braceTypeStack->back())
1363 				        && !isHeaderInMultiStatementLine)
1364 				{
1365 					if (previousHeader == nullptr
1366 					        && !foundClosingHeader
1367 					        && !isCharImmediatelyPostOpenBlock
1368 					        && !isImmediatelyPostCommentOnly)
1369 					{
1370 						isPrependPostBlockEmptyLineRequested = true;
1371 					}
1372 
1373 					if (isClosingHeader(currentHeader)
1374 					        || foundClosingHeader)
1375 					{
1376 						isPrependPostBlockEmptyLineRequested = false;
1377 					}
1378 
1379 					if (shouldBreakClosingHeaderBlocks
1380 					        && isCharImmediatelyPostCloseBlock
1381 					        && !isImmediatelyPostCommentOnly
1382 					        && !(currentHeader == &AS_WHILE			// do-while
1383 					             && foundClosingHeader))
1384 					{
1385 						isPrependPostBlockEmptyLineRequested = true;
1386 					}
1387 				}
1388 
1389 				if (currentHeader == &AS_CASE
1390 				        || currentHeader == &AS_DEFAULT)
1391 					isInCase = true;
1392 
1393 				continue;
1394 			}
1395 			else if ((newHeader = findHeader(preDefinitionHeaders)) != nullptr
1396 			         && parenStack->back() == 0
1397 			         && !isInEnum)		// not C++11 enum class
1398 			{
1399 				if (newHeader == &AS_NAMESPACE || newHeader == &AS_MODULE)
1400 					foundNamespaceHeader = true;
1401 				if (newHeader == &AS_CLASS)
1402 					foundClassHeader = true;
1403 				if (newHeader == &AS_STRUCT)
1404 					foundStructHeader = true;
1405 				if (newHeader == &AS_INTERFACE && !foundNamespaceHeader && !foundClassHeader)
1406 					foundInterfaceHeader = true;
1407 				foundPreDefinitionHeader = true;
1408 				appendSequence(*newHeader);
1409 				goForward(newHeader->length() - 1);
1410 
1411 				continue;
1412 			}
1413 			else if ((newHeader = findHeader(preCommandHeaders)) != nullptr)
1414 			{
1415 				// must be after function arguments
1416 				if (previousNonWSChar == ')')
1417 					foundPreCommandHeader = true;
1418 			}
1419 			else if ((newHeader = findHeader(castOperators)) != nullptr)
1420 			{
1421 				foundCastOperator = true;
1422 				appendSequence(*newHeader);
1423 				goForward(newHeader->length() - 1);
1424 				continue;
1425 			}
1426 		}   // (isPotentialHeader && !isInTemplate)
1427 
1428 		if (isInLineBreak)          // OK to break line here
1429 		{
1430 			breakLine();
1431 			if (isInVirginLine)		// adjust for the first line
1432 			{
1433 				lineCommentNoBeautify = lineCommentNoIndent;
1434 				lineCommentNoIndent = false;
1435 				if (isImmediatelyPostPreprocessor)
1436 				{
1437 					isInIndentablePreproc = isIndentableProprocessor;
1438 					isIndentableProprocessor = false;
1439 				}
1440 			}
1441 		}
1442 
1443 		if (previousNonWSChar == '}' || currentChar == ';')
1444 		{
1445 			if (currentChar == ';')
1446 			{
1447 				squareBracketCount = 0;
1448 				//assert(methodBreakCharNum == string::npos);	// comment out
1449 				//assert(methodBreakLineNum == 0);				// comment out
1450 				methodBreakCharNum = string::npos;
1451 				methodBreakLineNum = 0;
1452 				methodAttachCharNum = string::npos;
1453 				methodAttachLineNum = 0;
1454 
1455 				if (((shouldBreakOneLineStatements
1456 				        || isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
1457 				        && isOkToBreakBlock(braceTypeStack->back()))
1458 				        && !(attachClosingBraceMode && peekNextChar() == '}'))
1459 				{
1460 					passedSemicolon = true;
1461 				}
1462 				else if (!shouldBreakOneLineStatements
1463 				         && ASBeautifier::getNextWord(currentLine, charNum) == AS_ELSE)
1464 				{
1465 					// handle special case of "else" at the end of line
1466 					size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
1467 					if (ASBeautifier::peekNextChar(currentLine, nextText + 3) == ' ')
1468 						passedSemicolon = true;
1469 				}
1470 
1471 				if (shouldBreakBlocks
1472 				        && currentHeader != nullptr
1473 				        && currentHeader != &AS_CASE
1474 				        && currentHeader != &AS_DEFAULT
1475 				        && !isHeaderInMultiStatementLine
1476 				        && parenStack->back() == 0)
1477 				{
1478 					isAppendPostBlockEmptyLineRequested = true;
1479 				}
1480 			}
1481 			if (currentChar != ';'
1482 			        || (needHeaderOpeningBrace && parenStack->back() == 0))
1483 				currentHeader = nullptr;
1484 
1485 			resetEndOfStatement();
1486 		}
1487 
1488 		if (currentChar == ':'
1489 		        && previousChar != ':'         // not part of '::'
1490 		        && peekNextChar() != ':')      // not part of '::'
1491 		{
1492 			if (isInCase)
1493 			{
1494 				isInCase = false;
1495 				if (shouldBreakOneLineStatements)
1496 					passedColon = true;
1497 			}
1498 			else if (isCStyle()                     // for C/C++ only
1499 			         && isOkToBreakBlock(braceTypeStack->back())
1500 			         && shouldBreakOneLineStatements
1501 			         && !foundQuestionMark          // not in a ?: sequence
1502 			         && !foundPreDefinitionHeader   // not in a definition block
1503 			         && previousCommandChar != ')'  // not after closing paren of a method header
1504 			         && !foundPreCommandHeader      // not after a 'noexcept'
1505 			         && squareBracketCount == 0     // not in objC method call
1506 			         && !isInObjCMethodDefinition   // not objC '-' or '+' method
1507 			         && !isInObjCInterface          // not objC @interface
1508 			         && !isInObjCSelector           // not objC @selector
1509 			         && !isDigit(peekNextChar())    // not a bit field
1510 			         && !isInEnum                   // not an enum with a base type
1511 			         && !isInAsm                    // not in extended assembler
1512 			         && !isInAsmOneLine             // not in extended assembler
1513 			         && !isInAsmBlock)              // not in extended assembler
1514 			{
1515 				passedColon = true;
1516 			}
1517 
1518 			if (isCStyle()
1519 			        && (squareBracketCount > 0 || isInObjCMethodDefinition || isInObjCSelector)
1520 			        && !foundQuestionMark)			// not in a ?: sequence
1521 			{
1522 				isImmediatelyPostObjCMethodPrefix = false;
1523 				isInObjCReturnType = false;
1524 				isInObjCParam = true;
1525 				if (shouldPadMethodColon)
1526 					padObjCMethodColon();
1527 			}
1528 
1529 			if (isInObjCInterface)
1530 			{
1531 				appendSpacePad();
1532 				if ((int) currentLine.length() > charNum + 1
1533 				        && !isWhiteSpace(currentLine[charNum + 1]))
1534 					currentLine.insert(charNum + 1, " ");
1535 			}
1536 
1537 			if (isClassInitializer())
1538 				isInClassInitializer = true;
1539 		}
1540 
1541 		if (currentChar == '?')
1542 			foundQuestionMark = true;
1543 
1544 		if (isPotentialHeader && !isInTemplate)
1545 		{
1546 			if (findKeyword(currentLine, charNum, AS_NEW)
1547 			        || findKeyword(currentLine, charNum, AS_DELETE))
1548 			{
1549 				isInPotentialCalculation = false;
1550 				isImmediatelyPostNewDelete = true;
1551 			}
1552 
1553 			if (findKeyword(currentLine, charNum, AS_RETURN))
1554 			{
1555 				isInPotentialCalculation = true;
1556 				isImmediatelyPostReturn = true;		// return is the same as an = sign
1557 			}
1558 
1559 			if (findKeyword(currentLine, charNum, AS_OPERATOR))
1560 				isImmediatelyPostOperator = true;
1561 
1562 			if (findKeyword(currentLine, charNum, AS_ENUM))
1563 			{
1564 				size_t firstNum = currentLine.find_first_of("(){},/");
1565 				if (firstNum == string::npos
1566 				        || currentLine[firstNum] == '{'
1567 				        || currentLine[firstNum] == '/')
1568 					isInEnum = true;
1569 			}
1570 
1571 			if (isCStyle()
1572 			        && findKeyword(currentLine, charNum, AS_THROW)
1573 			        && previousCommandChar != ')'
1574 			        && !foundPreCommandHeader)      // 'const' throw()
1575 				isImmediatelyPostThrow = true;
1576 
1577 			if (isCStyle() && findKeyword(currentLine, charNum, AS_EXTERN) && isExternC())
1578 				isInExternC = true;
1579 
1580 			if (isCStyle() && findKeyword(currentLine, charNum, AS_AUTO)
1581 			        && (isBraceType(braceTypeStack->back(), NULL_TYPE)
1582 			            || isBraceType(braceTypeStack->back(), DEFINITION_TYPE)))
1583 				foundTrailingReturnType = true;
1584 
1585 			// check for break/attach return type
1586 			if (shouldBreakReturnType || shouldBreakReturnTypeDecl
1587 			        || shouldAttachReturnType || shouldAttachReturnTypeDecl)
1588 			{
1589 				if ((isBraceType(braceTypeStack->back(), NULL_TYPE)
1590 				        || isBraceType(braceTypeStack->back(), DEFINITION_TYPE))
1591 				        && !returnTypeChecked
1592 				        && !foundNamespaceHeader
1593 				        && !foundClassHeader
1594 				        && !isInObjCMethodDefinition
1595 				        // bypass objective-C and java @ character
1596 				        && charNum == (int) currentLine.find_first_not_of(" \t")
1597 				        && !(isCStyle() && isCharPotentialHeader(currentLine, charNum)
1598 				             && (findKeyword(currentLine, charNum, AS_PUBLIC)
1599 				                 || findKeyword(currentLine, charNum, AS_PRIVATE)
1600 				                 || findKeyword(currentLine, charNum, AS_PROTECTED))))
1601 				{
1602 					findReturnTypeSplitPoint(currentLine);
1603 					returnTypeChecked = true;
1604 				}
1605 			}
1606 
1607 			// Objective-C NSException macros are preCommandHeaders
1608 			if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_DURING))
1609 				foundPreCommandMacro = true;
1610 			if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_HANDLER))
1611 				foundPreCommandMacro = true;
1612 
1613 			if (isCStyle() && isExecSQL(currentLine, charNum))
1614 				isInExecSQL = true;
1615 
1616 			if (isCStyle())
1617 			{
1618 				if (findKeyword(currentLine, charNum, AS_ASM)
1619 				        || findKeyword(currentLine, charNum, AS__ASM__))
1620 				{
1621 					isInAsm = true;
1622 				}
1623 				else if (findKeyword(currentLine, charNum, AS_MS_ASM)		// microsoft specific
1624 				         || findKeyword(currentLine, charNum, AS_MS__ASM))
1625 				{
1626 					int index = 4;
1627 					if (peekNextChar() == '_')	// check for __asm
1628 						index = 5;
1629 
1630 					char peekedChar = ASBase::peekNextChar(currentLine, charNum + index);
1631 					if (peekedChar == '{' || peekedChar == ' ')
1632 						isInAsmBlock = true;
1633 					else
1634 						isInAsmOneLine = true;
1635 				}
1636 			}
1637 
1638 			if (isJavaStyle()
1639 			        && (findKeyword(currentLine, charNum, AS_STATIC)
1640 			            && isNextCharOpeningBrace(charNum + 6)))
1641 				isJavaStaticConstructor = true;
1642 
1643 			if (isSharpStyle()
1644 			        && (findKeyword(currentLine, charNum, AS_DELEGATE)
1645 			            || findKeyword(currentLine, charNum, AS_UNCHECKED)))
1646 				isSharpDelegate = true;
1647 
1648 			// append the entire name
1649 			string name = getCurrentWord(currentLine, charNum);
1650 			// must pad the 'and' and 'or' operators if required
1651 			if (name == "and" || name == "or")
1652 			{
1653 				if (shouldPadOperators && previousNonWSChar != ':')
1654 				{
1655 					appendSpacePad();
1656 					appendOperator(name);
1657 					goForward(name.length() - 1);
1658 					if (!isBeforeAnyComment()
1659 					        && !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0)
1660 					        && !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0))
1661 						appendSpaceAfter();
1662 				}
1663 				else
1664 				{
1665 					appendOperator(name);
1666 					goForward(name.length() - 1);
1667 				}
1668 			}
1669 			else
1670 			{
1671 				appendSequence(name);
1672 				goForward(name.length() - 1);
1673 			}
1674 
1675 			continue;
1676 
1677 		}   // (isPotentialHeader &&  !isInTemplate)
1678 
1679 		// determine if this is an Objective-C statement
1680 
1681 		if (currentChar == '@'
1682 		        && isCStyle()
1683 		        && (int) currentLine.length() > charNum + 1
1684 		        && !isWhiteSpace(currentLine[charNum + 1])
1685 		        && isCharPotentialHeader(currentLine, charNum + 1)
1686 		        && findKeyword(currentLine, charNum + 1, AS_INTERFACE)
1687 		        && isBraceType(braceTypeStack->back(), NULL_TYPE))
1688 		{
1689 			isInObjCInterface = true;
1690 			string name = '@' + AS_INTERFACE;
1691 			appendSequence(name);
1692 			goForward(name.length() - 1);
1693 			continue;
1694 		}
1695 		else if (currentChar == '@'
1696 		         && isCStyle()
1697 		         && (int) currentLine.length() > charNum + 1
1698 		         && !isWhiteSpace(currentLine[charNum + 1])
1699 		         && isCharPotentialHeader(currentLine, charNum + 1)
1700 		         && findKeyword(currentLine, charNum + 1, AS_SELECTOR))
1701 		{
1702 			isInObjCSelector = true;
1703 			string name = '@' + AS_SELECTOR;
1704 			appendSequence(name);
1705 			goForward(name.length() - 1);
1706 			continue;
1707 		}
1708 		else if ((currentChar == '-' || currentChar == '+')
1709 		         && isCStyle()
1710 		         && (int) currentLine.find_first_not_of(" \t") == charNum
1711 		         && !isInPotentialCalculation
1712 		         && !isInObjCMethodDefinition
1713 		         && (isBraceType(braceTypeStack->back(), NULL_TYPE)
1714 		             || (isBraceType(braceTypeStack->back(), EXTERN_TYPE))))
1715 		{
1716 			isInObjCMethodDefinition = true;
1717 			isImmediatelyPostObjCMethodPrefix = true;
1718 			isInObjCParam = false;
1719 			isInObjCInterface = false;
1720 			if (getAlignMethodColon())
1721 				objCColonAlign = findObjCColonAlignment();
1722 			appendCurrentChar();
1723 			continue;
1724 		}
1725 
1726 		// determine if this is a potential calculation
1727 
1728 		bool isPotentialOperator = isCharPotentialOperator(currentChar);
1729 		newHeader = nullptr;
1730 
1731 		if (isPotentialOperator)
1732 		{
1733 			newHeader = findOperator(operators);
1734 
1735 			// check for Java ? wildcard
1736 			if (newHeader != nullptr
1737 			        && newHeader == &AS_GCC_MIN_ASSIGN
1738 			        && isJavaStyle()
1739 			        && isInTemplate)
1740 				newHeader = nullptr;
1741 
1742 			if (newHeader != nullptr)
1743 			{
1744 				if (newHeader == &AS_LAMBDA)
1745 					foundPreCommandHeader = true;
1746 
1747 				// correct mistake of two >> closing a template
1748 				if (isInTemplate && (newHeader == &AS_GR_GR || newHeader == &AS_GR_GR_GR))
1749 					newHeader = &AS_GR;
1750 
1751 				if (!isInPotentialCalculation)
1752 				{
1753 					// must determine if newHeader is an assignment operator
1754 					// do NOT use findOperator - the length must be exact!!!
1755 					if (find(begin(*assignmentOperators), end(*assignmentOperators), newHeader)
1756 					        != end(*assignmentOperators))
1757 					{
1758 						foundPreCommandHeader = false;
1759 						char peekedChar = peekNextChar();
1760 						isInPotentialCalculation = !(newHeader == &AS_EQUAL && peekedChar == '*')
1761 						                           && !(newHeader == &AS_EQUAL && peekedChar == '&')
1762 						                           && !isCharImmediatelyPostOperator;
1763 					}
1764 				}
1765 			}
1766 		}
1767 
1768 		// process pointers and references
1769 		// check newHeader to eliminate things like '&&' sequence
1770 		if (newHeader != nullptr && !isJavaStyle()
1771 		        && (newHeader == &AS_MULT
1772 		            || newHeader == &AS_BIT_AND
1773 		            || newHeader == &AS_BIT_XOR
1774 		            || newHeader == &AS_AND)
1775 		        && isPointerOrReference())
1776 		{
1777 			if (!isDereferenceOrAddressOf() && !isOperatorPaddingDisabled())
1778 				formatPointerOrReference();
1779 			else
1780 			{
1781 				appendOperator(*newHeader);
1782 				goForward(newHeader->length() - 1);
1783 			}
1784 			isImmediatelyPostPointerOrReference = true;
1785 			continue;
1786 		}
1787 
1788 		if (shouldPadOperators && newHeader != nullptr && !isOperatorPaddingDisabled())
1789 		{
1790 			padOperators(newHeader);
1791 			continue;
1792 		}
1793 
1794 		// remove spaces before commas
1795 		if (currentChar == ',')
1796 		{
1797 			const size_t len = formattedLine.length();
1798 			size_t lastText = formattedLine.find_last_not_of(' ');
1799 			if (lastText != string::npos && lastText < len - 1)
1800 			{
1801 				formattedLine.resize(lastText + 1);
1802 				int size_diff = len - (lastText + 1);
1803 				spacePadNum -= size_diff;
1804 			}
1805 		}
1806 
1807 		// pad commas and semi-colons
1808 		if (currentChar == ';'
1809 		        || (currentChar == ',' && (shouldPadOperators || shouldPadCommas)))
1810 		{
1811 			char nextChar = ' ';
1812 			if (charNum + 1 < (int) currentLine.length())
1813 				nextChar = currentLine[charNum + 1];
1814 			if (!isWhiteSpace(nextChar)
1815 			        && nextChar != '}'
1816 			        && nextChar != ')'
1817 			        && nextChar != ']'
1818 			        && nextChar != '>'
1819 			        && nextChar != ';'
1820 			        && !isBeforeAnyComment()
1821 			        /* && !(isBraceType(braceTypeStack->back(), ARRAY_TYPE)) */
1822 			   )
1823 			{
1824 				appendCurrentChar();
1825 				appendSpaceAfter();
1826 				continue;
1827 			}
1828 		}
1829 
1830 		// pad parens
1831 		if (currentChar == '(' || currentChar == ')')
1832 		{
1833 			if (currentChar == '(')
1834 			{
1835 				if (shouldPadHeader
1836 				        && (isCharImmediatelyPostReturn
1837 				            || isCharImmediatelyPostThrow
1838 				            || isCharImmediatelyPostNewDelete))
1839 					appendSpacePad();
1840 			}
1841 
1842 			if (shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen)
1843 				padParens();
1844 			else
1845 				appendCurrentChar();
1846 
1847 			if (isInObjCMethodDefinition)
1848 			{
1849 				if (currentChar == '(' && isImmediatelyPostObjCMethodPrefix)
1850 				{
1851 					if (shouldPadMethodPrefix || shouldUnPadMethodPrefix)
1852 						padObjCMethodPrefix();
1853 					isImmediatelyPostObjCMethodPrefix = false;
1854 					isInObjCReturnType = true;
1855 				}
1856 				else if (currentChar == ')' && isInObjCReturnType)
1857 				{
1858 					if (shouldPadReturnType || shouldUnPadReturnType)
1859 						padObjCReturnType();
1860 					isInObjCReturnType = false;
1861 				}
1862 				else if (isInObjCParam
1863 				         && (shouldPadParamType || shouldUnPadParamType))
1864 					padObjCParamType();
1865 			}
1866 			continue;
1867 		}
1868 
1869 		// bypass the entire operator
1870 		if (newHeader != nullptr)
1871 		{
1872 			appendOperator(*newHeader);
1873 			goForward(newHeader->length() - 1);
1874 			continue;
1875 		}
1876 
1877 		appendCurrentChar();
1878 
1879 	}   // end of while loop  *  end of while loop  *  end of while loop  *  end of while loop
1880 
1881 	// return a beautified (i.e. correctly indented) line.
1882 
1883 	string beautifiedLine;
1884 	size_t readyFormattedLineLength = trim(readyFormattedLine).length();
1885 	bool isInNamespace = isBraceType(braceTypeStack->back(), NAMESPACE_TYPE);
1886 
1887 	if (prependEmptyLine		// prepend a blank line before this formatted line
1888 	        && readyFormattedLineLength > 0
1889 	        && previousReadyFormattedLineLength > 0)
1890 	{
1891 		isLineReady = true;		// signal a waiting readyFormattedLine
1892 		beautifiedLine = beautify("");
1893 		previousReadyFormattedLineLength = 0;
1894 		// call the enhancer for new empty lines
1895 		enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL);
1896 	}
1897 	else		// format the current formatted line
1898 	{
1899 		isLineReady = false;
1900 		runInIndentContinuation = runInIndentChars;
1901 		beautifiedLine = beautify(readyFormattedLine);
1902 		previousReadyFormattedLineLength = readyFormattedLineLength;
1903 		// the enhancer is not called for no-indent line comments
1904 		if (!lineCommentNoBeautify && !isFormattingModeOff)
1905 			enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL);
1906 		runInIndentChars = 0;
1907 		lineCommentNoBeautify = lineCommentNoIndent;
1908 		lineCommentNoIndent = false;
1909 		isInIndentablePreproc = isIndentableProprocessor;
1910 		isIndentableProprocessor = false;
1911 		isElseHeaderIndent = elseHeaderFollowsComments;
1912 		isCaseHeaderCommentIndent = caseHeaderFollowsComments;
1913 		objCColonAlignSubsequent = objCColonAlign;
1914 		if (isCharImmediatelyPostNonInStmt)
1915 		{
1916 			isNonInStatementArray = false;
1917 			isCharImmediatelyPostNonInStmt = false;
1918 		}
1919 		isInPreprocessorBeautify = isInPreprocessor;	// used by ASEnhancer
1920 		isInBeautifySQL = isInExecSQL;					// used by ASEnhancer
1921 	}
1922 
1923 	prependEmptyLine = false;
1924 	assert(computeChecksumOut(beautifiedLine));
1925 	return beautifiedLine;
1926 }
1927 
1928 /**
1929  * check if there are any indented lines ready to be read by nextLine()
1930  *
1931  * @return    are there any indented lines ready?
1932  */
hasMoreLines() const1933 bool ASFormatter::hasMoreLines() const
1934 {
1935 	return !endOfCodeReached;
1936 }
1937 
1938 /**
1939  * comparison function for BraceType enum
1940  */
isBraceType(BraceType a,BraceType b) const1941 bool ASFormatter::isBraceType(BraceType a, BraceType b) const
1942 {
1943 	if (a == NULL_TYPE || b == NULL_TYPE)
1944 		return (a == b);
1945 	return ((a & b) == b);
1946 }
1947 
1948 /**
1949  * set the formatting style.
1950  *
1951  * @param style         the formatting style.
1952  */
setFormattingStyle(FormatStyle style)1953 void ASFormatter::setFormattingStyle(FormatStyle style)
1954 {
1955 	formattingStyle = style;
1956 }
1957 
1958 /**
1959  * set the add braces mode.
1960  * options:
1961  *    true     braces added to headers for single line statements.
1962  *    false    braces NOT added to headers for single line statements.
1963  *
1964  * @param state         the add braces state.
1965  */
setAddBracesMode(bool state)1966 void ASFormatter::setAddBracesMode(bool state)
1967 {
1968 	shouldAddBraces = state;
1969 }
1970 
1971 /**
1972  * set the add one line braces mode.
1973  * options:
1974  *    true     one line braces added to headers for single line statements.
1975  *    false    one line braces NOT added to headers for single line statements.
1976  *
1977  * @param state         the add one line braces state.
1978  */
setAddOneLineBracesMode(bool state)1979 void ASFormatter::setAddOneLineBracesMode(bool state)
1980 {
1981 	shouldAddBraces = state;
1982 	shouldAddOneLineBraces = state;
1983 }
1984 
1985 /**
1986  * set the remove braces mode.
1987  * options:
1988  *    true     braces removed from headers for single line statements.
1989  *    false    braces NOT removed from headers for single line statements.
1990  *
1991  * @param state         the remove braces state.
1992  */
setRemoveBracesMode(bool state)1993 void ASFormatter::setRemoveBracesMode(bool state)
1994 {
1995 	shouldRemoveBraces = state;
1996 }
1997 
1998 // retained for compatibility with release 2.06
1999 // "Brackets" have been changed to "Braces" in 3.0
2000 // it is referenced only by the old "bracket" options
setAddBracketsMode(bool state)2001 void ASFormatter::setAddBracketsMode(bool state)
2002 {
2003 	setAddBracesMode(state);
2004 }
2005 
2006 // retained for compatibility with release 2.06
2007 // "Brackets" have been changed to "Braces" in 3.0
2008 // it is referenced only by the old "bracket" options
setAddOneLineBracketsMode(bool state)2009 void ASFormatter::setAddOneLineBracketsMode(bool state)
2010 {
2011 	setAddOneLineBracesMode(state);
2012 }
2013 
2014 // retained for compatibility with release 2.06
2015 // "Brackets" have been changed to "Braces" in 3.0
2016 // it is referenced only by the old "bracket" options
setRemoveBracketsMode(bool state)2017 void ASFormatter::setRemoveBracketsMode(bool state)
2018 {
2019 	setRemoveBracesMode(state);
2020 }
2021 
2022 // retained for compatibility with release 2.06
2023 // "Brackets" have been changed to "Braces" in 3.0
2024 // it is referenced only by the old "bracket" options
setBreakClosingHeaderBracketsMode(bool state)2025 void ASFormatter::setBreakClosingHeaderBracketsMode(bool state)
2026 {
2027 	setBreakClosingHeaderBracesMode(state);
2028 }
2029 
2030 /**
2031  * set the brace formatting mode.
2032  * options:
2033  *
2034  * @param mode         the brace formatting mode.
2035  */
setBraceFormatMode(BraceMode mode)2036 void ASFormatter::setBraceFormatMode(BraceMode mode)
2037 {
2038 	braceFormatMode = mode;
2039 }
2040 
2041 /**
2042  * set 'break after' mode for maximum code length
2043  *
2044  * @param state         the 'break after' mode.
2045  */
setBreakAfterMode(bool state)2046 void ASFormatter::setBreakAfterMode(bool state)
2047 {
2048 	shouldBreakLineAfterLogical = state;
2049 }
2050 
2051 /**
2052  * set closing header brace breaking mode
2053  * options:
2054  *    true     braces just before closing headers (e.g. 'else', 'catch')
2055  *             will be broken, even if standard braces are attached.
2056  *    false    closing header braces will be treated as standard braces.
2057  *
2058  * @param state         the closing header brace breaking mode.
2059  */
setBreakClosingHeaderBracesMode(bool state)2060 void ASFormatter::setBreakClosingHeaderBracesMode(bool state)
2061 {
2062 	shouldBreakClosingHeaderBraces = state;
2063 }
2064 
2065 /**
2066  * set 'else if()' breaking mode
2067  * options:
2068  *    true     'else' headers will be broken from their succeeding 'if' headers.
2069  *    false    'else' headers will be attached to their succeeding 'if' headers.
2070  *
2071  * @param state         the 'else if()' breaking mode.
2072  */
setBreakElseIfsMode(bool state)2073 void ASFormatter::setBreakElseIfsMode(bool state)
2074 {
2075 	shouldBreakElseIfs = state;
2076 }
2077 
2078 /**
2079 * set comma padding mode.
2080 * options:
2081 *    true     statement commas and semicolons will be padded with spaces around them.
2082 *    false    statement commas and semicolons will not be padded.
2083 *
2084 * @param state         the padding mode.
2085 */
setCommaPaddingMode(bool state)2086 void ASFormatter::setCommaPaddingMode(bool state)
2087 {
2088 	shouldPadCommas = state;
2089 }
2090 
2091 /**
2092  * set maximum code length
2093  *
2094  * @param max         the maximum code length.
2095  */
setMaxCodeLength(int max)2096 void ASFormatter::setMaxCodeLength(int max)
2097 {
2098 	maxCodeLength = max;
2099 }
2100 
2101 /**
2102  * set operator padding mode.
2103  * options:
2104  *    true     statement operators will be padded with spaces around them.
2105  *    false    statement operators will not be padded.
2106  *
2107  * @param state         the padding mode.
2108  */
setOperatorPaddingMode(bool state)2109 void ASFormatter::setOperatorPaddingMode(bool state)
2110 {
2111 	shouldPadOperators = state;
2112 }
2113 
2114 /**
2115  * set parenthesis outside padding mode.
2116  * options:
2117  *    true     statement parentheses will be padded with spaces around them.
2118  *    false    statement parentheses will not be padded.
2119  *
2120  * @param state         the padding mode.
2121  */
setParensOutsidePaddingMode(bool state)2122 void ASFormatter::setParensOutsidePaddingMode(bool state)
2123 {
2124 	shouldPadParensOutside = state;
2125 }
2126 
2127 /**
2128  * set parenthesis inside padding mode.
2129  * options:
2130  *    true     statement parenthesis will be padded with spaces around them.
2131  *    false    statement parenthesis will not be padded.
2132  *
2133  * @param state         the padding mode.
2134  */
setParensInsidePaddingMode(bool state)2135 void ASFormatter::setParensInsidePaddingMode(bool state)
2136 {
2137 	shouldPadParensInside = state;
2138 }
2139 
2140 /**
2141  * set padding mode before one or more open parentheses.
2142  * options:
2143  *    true     first open parenthesis will be padded with a space before.
2144  *    false    first open parenthesis will not be padded.
2145  *
2146  * @param state         the padding mode.
2147  */
setParensFirstPaddingMode(bool state)2148 void ASFormatter::setParensFirstPaddingMode(bool state)
2149 {
2150 	shouldPadFirstParen = state;
2151 }
2152 
2153 /**
2154  * set header padding mode.
2155  * options:
2156  *    true     headers will be padded with spaces around them.
2157  *    false    headers will not be padded.
2158  *
2159  * @param state         the padding mode.
2160  */
setParensHeaderPaddingMode(bool state)2161 void ASFormatter::setParensHeaderPaddingMode(bool state)
2162 {
2163 	shouldPadHeader = state;
2164 }
2165 
2166 /**
2167  * set parenthesis unpadding mode.
2168  * options:
2169  *    true     statement parenthesis will be unpadded with spaces removed around them.
2170  *    false    statement parenthesis will not be unpadded.
2171  *
2172  * @param state         the padding mode.
2173  */
setParensUnPaddingMode(bool state)2174 void ASFormatter::setParensUnPaddingMode(bool state)
2175 {
2176 	shouldUnPadParens = state;
2177 }
2178 
2179 /**
2180 * set the state of the preprocessor indentation option.
2181 * If true, #ifdef blocks at level 0 will be indented.
2182 *
2183 * @param   state             state of option.
2184 */
setPreprocBlockIndent(bool state)2185 void ASFormatter::setPreprocBlockIndent(bool state)
2186 {
2187 	shouldIndentPreprocBlock = state;
2188 }
2189 
2190 /**
2191  * Set strip comment prefix mode.
2192  * options:
2193  *    true     strip leading '*' in a comment.
2194  *    false    leading '*' in a comment will be left unchanged.
2195  *
2196  * @param state         the strip comment prefix mode.
2197  */
setStripCommentPrefix(bool state)2198 void ASFormatter::setStripCommentPrefix(bool state)
2199 {
2200 	shouldStripCommentPrefix = state;
2201 }
2202 
2203 /**
2204  * set objective-c '-' or '+' class prefix padding mode.
2205  * options:
2206  *    true     class prefix will be padded a spaces after them.
2207  *    false    class prefix will be left unchanged.
2208  *
2209  * @param state         the padding mode.
2210  */
setMethodPrefixPaddingMode(bool state)2211 void ASFormatter::setMethodPrefixPaddingMode(bool state)
2212 {
2213 	shouldPadMethodPrefix = state;
2214 }
2215 
2216 /**
2217  * set objective-c '-' or '+' class prefix unpadding mode.
2218  * options:
2219  *    true     class prefix will be unpadded with spaces after them removed.
2220  *    false    class prefix will left unchanged.
2221  *
2222  * @param state         the unpadding mode.
2223  */
setMethodPrefixUnPaddingMode(bool state)2224 void ASFormatter::setMethodPrefixUnPaddingMode(bool state)
2225 {
2226 	shouldUnPadMethodPrefix = state;
2227 }
2228 
2229 // set objective-c '-' or '+' return type padding mode.
setReturnTypePaddingMode(bool state)2230 void ASFormatter::setReturnTypePaddingMode(bool state)
2231 {
2232 	shouldPadReturnType = state;
2233 }
2234 
2235 // set objective-c '-' or '+' return type unpadding mode.
setReturnTypeUnPaddingMode(bool state)2236 void ASFormatter::setReturnTypeUnPaddingMode(bool state)
2237 {
2238 	shouldUnPadReturnType = state;
2239 }
2240 
2241 // set objective-c method parameter type padding mode.
setParamTypePaddingMode(bool state)2242 void ASFormatter::setParamTypePaddingMode(bool state)
2243 {
2244 	shouldPadParamType = state;
2245 }
2246 
2247 // set objective-c method parameter type unpadding mode.
setParamTypeUnPaddingMode(bool state)2248 void ASFormatter::setParamTypeUnPaddingMode(bool state)
2249 {
2250 	shouldUnPadParamType = state;
2251 }
2252 
2253 /**
2254  * set objective-c method colon padding mode.
2255  *
2256  * @param mode         objective-c colon padding mode.
2257  */
setObjCColonPaddingMode(ObjCColonPad mode)2258 void ASFormatter::setObjCColonPaddingMode(ObjCColonPad mode)
2259 {
2260 	shouldPadMethodColon = true;
2261 	objCColonPadMode = mode;
2262 }
2263 
2264 /**
2265  * set option to attach closing braces
2266  *
2267  * @param state        true = attach, false = don't attach.
2268  */
setAttachClosingBraceMode(bool state)2269 void ASFormatter::setAttachClosingBraceMode(bool state)
2270 {
2271 	attachClosingBraceMode = state;
2272 }
2273 
2274 /**
2275  * set option to attach class braces
2276  *
2277  * @param state        true = attach, false = use style default.
2278  */
setAttachClass(bool state)2279 void ASFormatter::setAttachClass(bool state)
2280 {
2281 	shouldAttachClass = state;
2282 }
2283 
2284 /**
2285  * set option to attach extern "C" braces
2286  *
2287  * @param state        true = attach, false = use style default.
2288  */
setAttachExternC(bool state)2289 void ASFormatter::setAttachExternC(bool state)
2290 {
2291 	shouldAttachExternC = state;
2292 }
2293 
2294 /**
2295  * set option to attach namespace braces
2296  *
2297  * @param state        true = attach, false = use style default.
2298  */
setAttachNamespace(bool state)2299 void ASFormatter::setAttachNamespace(bool state)
2300 {
2301 	shouldAttachNamespace = state;
2302 }
2303 
2304 /**
2305  * set option to attach inline braces
2306  *
2307  * @param state        true = attach, false = use style default.
2308  */
setAttachInline(bool state)2309 void ASFormatter::setAttachInline(bool state)
2310 {
2311 	shouldAttachInline = state;
2312 }
2313 
setAttachClosingWhile(bool state)2314 void ASFormatter::setAttachClosingWhile(bool state)
2315 {
2316 	shouldAttachClosingWhile = state;
2317 }
2318 
2319 /**
2320  * set option to break/not break one-line blocks
2321  *
2322  * @param state        true = break, false = don't break.
2323  */
setBreakOneLineBlocksMode(bool state)2324 void ASFormatter::setBreakOneLineBlocksMode(bool state)
2325 {
2326 	shouldBreakOneLineBlocks = state;
2327 }
2328 
2329 /**
2330 * set one line headers breaking mode
2331 */
setBreakOneLineHeadersMode(bool state)2332 void ASFormatter::setBreakOneLineHeadersMode(bool state)
2333 {
2334 	shouldBreakOneLineHeaders = state;
2335 }
2336 
2337 /**
2338 * set option to break/not break lines consisting of multiple statements.
2339 *
2340 * @param state        true = break, false = don't break.
2341 */
setBreakOneLineStatementsMode(bool state)2342 void ASFormatter::setBreakOneLineStatementsMode(bool state)
2343 {
2344 	shouldBreakOneLineStatements = state;
2345 }
2346 
setCloseTemplatesMode(bool state)2347 void ASFormatter::setCloseTemplatesMode(bool state)
2348 {
2349 	shouldCloseTemplates = state;
2350 }
2351 
2352 /**
2353  * set option to convert tabs to spaces.
2354  *
2355  * @param state        true = convert, false = don't convert.
2356  */
setTabSpaceConversionMode(bool state)2357 void ASFormatter::setTabSpaceConversionMode(bool state)
2358 {
2359 	shouldConvertTabs = state;
2360 }
2361 
2362 /**
2363  * set option to indent comments in column 1.
2364  *
2365  * @param state        true = indent, false = don't indent.
2366  */
setIndentCol1CommentsMode(bool state)2367 void ASFormatter::setIndentCol1CommentsMode(bool state)
2368 {
2369 	shouldIndentCol1Comments = state;
2370 }
2371 
2372 /**
2373  * set option to force all line ends to a particular style.
2374  *
2375  * @param fmt           format enum value
2376  */
setLineEndFormat(LineEndFormat fmt)2377 void ASFormatter::setLineEndFormat(LineEndFormat fmt)
2378 {
2379 	lineEnd = fmt;
2380 }
2381 
2382 /**
2383  * set option to break unrelated blocks of code with empty lines.
2384  *
2385  * @param state        true = convert, false = don't convert.
2386  */
setBreakBlocksMode(bool state)2387 void ASFormatter::setBreakBlocksMode(bool state)
2388 {
2389 	shouldBreakBlocks = state;
2390 }
2391 
2392 /**
2393  * set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines.
2394  *
2395  * @param state        true = convert, false = don't convert.
2396  */
setBreakClosingHeaderBlocksMode(bool state)2397 void ASFormatter::setBreakClosingHeaderBlocksMode(bool state)
2398 {
2399 	shouldBreakClosingHeaderBlocks = state;
2400 }
2401 
2402 /**
2403  * set option to delete empty lines.
2404  *
2405  * @param state        true = delete, false = don't delete.
2406  */
setDeleteEmptyLinesMode(bool state)2407 void ASFormatter::setDeleteEmptyLinesMode(bool state)
2408 {
2409 	shouldDeleteEmptyLines = state;
2410 }
2411 
setBreakReturnType(bool state)2412 void ASFormatter::setBreakReturnType(bool state)
2413 {
2414 	shouldBreakReturnType = state;
2415 }
2416 
setBreakReturnTypeDecl(bool state)2417 void ASFormatter::setBreakReturnTypeDecl(bool state)
2418 {
2419 	shouldBreakReturnTypeDecl = state;
2420 }
2421 
setAttachReturnType(bool state)2422 void ASFormatter::setAttachReturnType(bool state)
2423 {
2424 	shouldAttachReturnType = state;
2425 }
2426 
setAttachReturnTypeDecl(bool state)2427 void ASFormatter::setAttachReturnTypeDecl(bool state)
2428 {
2429 	shouldAttachReturnTypeDecl = state;
2430 }
2431 
2432 /**
2433  * set the pointer alignment.
2434  *
2435  * @param alignment    the pointer alignment.
2436  */
setPointerAlignment(PointerAlign alignment)2437 void ASFormatter::setPointerAlignment(PointerAlign alignment)
2438 {
2439 	pointerAlignment = alignment;
2440 }
2441 
setReferenceAlignment(ReferenceAlign alignment)2442 void ASFormatter::setReferenceAlignment(ReferenceAlign alignment)
2443 {
2444 	referenceAlignment = alignment;
2445 }
2446 
2447 /**
2448  * jump over several characters.
2449  *
2450  * @param i       the number of characters to jump over.
2451  */
goForward(int i)2452 void ASFormatter::goForward(int i)
2453 {
2454 	while (--i >= 0)
2455 		getNextChar();
2456 }
2457 
2458 /**
2459  * peek at the next unread character.
2460  *
2461  * @return     the next unread character.
2462  */
peekNextChar() const2463 char ASFormatter::peekNextChar() const
2464 {
2465 	char ch = ' ';
2466 	size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
2467 
2468 	if (peekNum == string::npos)
2469 		return ch;
2470 
2471 	ch = currentLine[peekNum];
2472 
2473 	return ch;
2474 }
2475 
2476 /**
2477  * check if current placement is before a comment
2478  *
2479  * @return     is before a comment.
2480  */
isBeforeComment() const2481 bool ASFormatter::isBeforeComment() const
2482 {
2483 	bool foundComment = false;
2484 	size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
2485 
2486 	if (peekNum == string::npos)
2487 		return foundComment;
2488 
2489 	foundComment = (currentLine.compare(peekNum, 2, "/*") == 0);
2490 
2491 	return foundComment;
2492 }
2493 
2494 /**
2495  * check if current placement is before a comment or line-comment
2496  *
2497  * @return     is before a comment or line-comment.
2498  */
isBeforeAnyComment() const2499 bool ASFormatter::isBeforeAnyComment() const
2500 {
2501 	bool foundComment = false;
2502 	size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
2503 
2504 	if (peekNum == string::npos)
2505 		return foundComment;
2506 
2507 	foundComment = (currentLine.compare(peekNum, 2, "/*") == 0
2508 	                || currentLine.compare(peekNum, 2, "//") == 0);
2509 
2510 	return foundComment;
2511 }
2512 
2513 /**
2514  * check if current placement is before a comment or line-comment
2515  * if a block comment it must be at the end of the line
2516  *
2517  * @return     is before a comment or line-comment.
2518  */
isBeforeAnyLineEndComment(int startPos) const2519 bool ASFormatter::isBeforeAnyLineEndComment(int startPos) const
2520 {
2521 	bool foundLineEndComment = false;
2522 	size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
2523 
2524 	if (peekNum != string::npos)
2525 	{
2526 		if (currentLine.compare(peekNum, 2, "//") == 0)
2527 			foundLineEndComment = true;
2528 		else if (currentLine.compare(peekNum, 2, "/*") == 0)
2529 		{
2530 			// comment must be closed on this line with nothing after it
2531 			size_t endNum = currentLine.find("*/", peekNum + 2);
2532 			if (endNum != string::npos)
2533 			{
2534 				size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
2535 				if (nextChar == string::npos)
2536 					foundLineEndComment = true;
2537 			}
2538 		}
2539 	}
2540 	return foundLineEndComment;
2541 }
2542 
2543 /**
2544  * check if current placement is before a comment followed by a line-comment
2545  *
2546  * @return     is before a multiple line-end comment.
2547  */
isBeforeMultipleLineEndComments(int startPos) const2548 bool ASFormatter::isBeforeMultipleLineEndComments(int startPos) const
2549 {
2550 	bool foundMultipleLineEndComment = false;
2551 	size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1);
2552 
2553 	if (peekNum != string::npos)
2554 	{
2555 		if (currentLine.compare(peekNum, 2, "/*") == 0)
2556 		{
2557 			// comment must be closed on this line with nothing after it
2558 			size_t endNum = currentLine.find("*/", peekNum + 2);
2559 			if (endNum != string::npos)
2560 			{
2561 				size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2);
2562 				if (nextChar != string::npos
2563 				        && currentLine.compare(nextChar, 2, "//") == 0)
2564 					foundMultipleLineEndComment = true;
2565 			}
2566 		}
2567 	}
2568 	return foundMultipleLineEndComment;
2569 }
2570 
2571 /**
2572  * get the next character, increasing the current placement in the process.
2573  * the new character is inserted into the variable currentChar.
2574  *
2575  * @return   whether succeeded to receive the new character.
2576  */
getNextChar()2577 bool ASFormatter::getNextChar()
2578 {
2579 	isInLineBreak = false;
2580 	previousChar = currentChar;
2581 
2582 	if (!isWhiteSpace(currentChar))
2583 	{
2584 		previousNonWSChar = currentChar;
2585 		if (!isInComment && !isInLineComment && !isInQuote
2586 		        && !isImmediatelyPostComment
2587 		        && !isImmediatelyPostLineComment
2588 		        && !isInPreprocessor
2589 		        && !isSequenceReached("/*")
2590 		        && !isSequenceReached("//"))
2591 			previousCommandChar = currentChar;
2592 	}
2593 
2594 	if (charNum + 1 < (int) currentLine.length()
2595 	        && (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment))
2596 	{
2597 		currentChar = currentLine[++charNum];
2598 
2599 		if (currentChar == '\t' && shouldConvertTabs)
2600 			convertTabToSpaces();
2601 
2602 		return true;
2603 	}
2604 
2605 	// end of line has been reached
2606 	return getNextLine();
2607 }
2608 
2609 /**
2610  * get the next line of input, increasing the current placement in the process.
2611  *
2612  * @param emptyLineWasDeleted         an empty line was deleted.
2613  * @return   whether succeeded in reading the next line.
2614  */
getNextLine(bool emptyLineWasDeleted)2615 bool ASFormatter::getNextLine(bool emptyLineWasDeleted /*false*/)
2616 {
2617 	if (!sourceIterator->hasMoreLines())
2618 	{
2619 		endOfCodeReached = true;
2620 		return false;
2621 	}
2622 	if (appendOpeningBrace)
2623 		currentLine = "{";		// append brace that was removed from the previous line
2624 	else
2625 	{
2626 		currentLine = sourceIterator->nextLine(emptyLineWasDeleted);
2627 		assert(computeChecksumIn(currentLine));
2628 	}
2629 	// reset variables for new line
2630 	inLineNumber++;
2631 	if (endOfAsmReached)
2632 		endOfAsmReached = isInAsmBlock = isInAsm = false;
2633 	shouldKeepLineUnbroken = false;
2634 	isInCommentStartLine = false;
2635 	isInCase = false;
2636 	isInAsmOneLine = false;
2637 	isHeaderInMultiStatementLine = false;
2638 	isInQuoteContinuation = isInVerbatimQuote || haveLineContinuationChar;
2639 	haveLineContinuationChar = false;
2640 	isImmediatelyPostEmptyLine = lineIsEmpty;
2641 	previousChar = ' ';
2642 
2643 	if (currentLine.length() == 0)
2644 		currentLine = string(" ");        // a null is inserted if this is not done
2645 
2646 	if (methodBreakLineNum > 0)
2647 		--methodBreakLineNum;
2648 	if (methodAttachLineNum > 0)
2649 		--methodAttachLineNum;
2650 
2651 	// unless reading in the first line of the file, break a new line.
2652 	if (!isVirgin)
2653 		isInLineBreak = true;
2654 	else
2655 		isVirgin = false;
2656 
2657 	if (isImmediatelyPostNonInStmt)
2658 	{
2659 		isCharImmediatelyPostNonInStmt = true;
2660 		isImmediatelyPostNonInStmt = false;
2661 	}
2662 
2663 	// check if is in preprocessor before line trimming
2664 	// a blank line after a \ will remove the flag
2665 	isImmediatelyPostPreprocessor = isInPreprocessor;
2666 	if (!isInComment
2667 	        && (previousNonWSChar != '\\'
2668 	            || isEmptyLine(currentLine)))
2669 		isInPreprocessor = false;
2670 
2671 	if (passedSemicolon)
2672 		isInExecSQL = false;
2673 	initNewLine();
2674 
2675 	currentChar = currentLine[charNum];
2676 	if (isInBraceRunIn && previousNonWSChar == '{' && !isInComment)
2677 		isInLineBreak = false;
2678 	isInBraceRunIn = false;
2679 
2680 	if (currentChar == '\t' && shouldConvertTabs)
2681 		convertTabToSpaces();
2682 
2683 	// check for an empty line inside a command brace.
2684 	// if yes then read the next line (calls getNextLine recursively).
2685 	// must be after initNewLine.
2686 	if (shouldDeleteEmptyLines
2687 	        && lineIsEmpty
2688 	        && isBraceType((*braceTypeStack)[braceTypeStack->size() - 1], COMMAND_TYPE))
2689 	{
2690 		if (!shouldBreakBlocks || previousNonWSChar == '{' || !commentAndHeaderFollows())
2691 		{
2692 			isInPreprocessor = isImmediatelyPostPreprocessor;		// restore
2693 			lineIsEmpty = false;
2694 			return getNextLine(true);
2695 		}
2696 	}
2697 	return true;
2698 }
2699 
2700 /**
2701  * jump over the leading white space in the current line,
2702  * IF the line does not begin a comment or is in a preprocessor definition.
2703  */
initNewLine()2704 void ASFormatter::initNewLine()
2705 {
2706 	size_t len = currentLine.length();
2707 	size_t tabSize = getTabLength();
2708 	charNum = 0;
2709 
2710 	// don't trim these
2711 	if (isInQuoteContinuation
2712 	        || (isInPreprocessor && !getPreprocDefineIndent()))
2713 		return;
2714 
2715 	// SQL continuation lines must be adjusted so the leading spaces
2716 	// is equivalent to the opening EXEC SQL
2717 	if (isInExecSQL)
2718 	{
2719 		// replace leading tabs with spaces
2720 		// so that continuation indent will be spaces
2721 		size_t tabCount_ = 0;
2722 		size_t i;
2723 		for (i = 0; i < currentLine.length(); i++)
2724 		{
2725 			if (!isWhiteSpace(currentLine[i]))		// stop at first text
2726 				break;
2727 			if (currentLine[i] == '\t')
2728 			{
2729 				size_t numSpaces = tabSize - ((tabCount_ + i) % tabSize);
2730 				currentLine.replace(i, 1, numSpaces, ' ');
2731 				tabCount_++;
2732 				i += tabSize - 1;
2733 			}
2734 		}
2735 		// this will correct the format if EXEC SQL is not a hanging indent
2736 		trimContinuationLine();
2737 		return;
2738 	}
2739 
2740 	// comment continuation lines must be adjusted so the leading spaces
2741 	// is equivalent to the opening comment
2742 	if (isInComment)
2743 	{
2744 		if (noTrimCommentContinuation)
2745 			leadingSpaces = tabIncrementIn = 0;
2746 		trimContinuationLine();
2747 		return;
2748 	}
2749 
2750 	// compute leading spaces
2751 	isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly;
2752 	lineIsCommentOnly = false;
2753 	lineIsLineCommentOnly = false;
2754 	lineEndsInCommentOnly = false;
2755 	doesLineStartComment = false;
2756 	currentLineBeginsWithBrace = false;
2757 	lineIsEmpty = false;
2758 	currentLineFirstBraceNum = string::npos;
2759 	tabIncrementIn = 0;
2760 
2761 	// bypass whitespace at the start of a line
2762 	// preprocessor tabs are replaced later in the program
2763 	for (charNum = 0; isWhiteSpace(currentLine[charNum]) && charNum + 1 < (int) len; charNum++)
2764 	{
2765 		if (currentLine[charNum] == '\t' && !isInPreprocessor)
2766 			tabIncrementIn += tabSize - 1 - ((tabIncrementIn + charNum) % tabSize);
2767 	}
2768 	leadingSpaces = charNum + tabIncrementIn;
2769 
2770 	if (isSequenceReached("/*"))
2771 	{
2772 		doesLineStartComment = true;
2773 		if ((int) currentLine.length() > charNum + 2
2774 		        && currentLine.find("*/", charNum + 2) != string::npos)
2775 			lineIsCommentOnly = true;
2776 	}
2777 	else if (isSequenceReached("//"))
2778 	{
2779 		lineIsLineCommentOnly = true;
2780 	}
2781 	else if (isSequenceReached("{"))
2782 	{
2783 		currentLineBeginsWithBrace = true;
2784 		currentLineFirstBraceNum = charNum;
2785 		size_t firstText = currentLine.find_first_not_of(" \t", charNum + 1);
2786 		if (firstText != string::npos)
2787 		{
2788 			if (currentLine.compare(firstText, 2, "//") == 0)
2789 				lineIsLineCommentOnly = true;
2790 			else if (currentLine.compare(firstText, 2, "/*") == 0
2791 			         || isExecSQL(currentLine, firstText))
2792 			{
2793 				// get the extra adjustment
2794 				size_t j;
2795 				for (j = charNum + 1; j < firstText && isWhiteSpace(currentLine[j]); j++)
2796 				{
2797 					if (currentLine[j] == '\t')
2798 						tabIncrementIn += tabSize - 1 - ((tabIncrementIn + j) % tabSize);
2799 				}
2800 				leadingSpaces = j + tabIncrementIn;
2801 				if (currentLine.compare(firstText, 2, "/*") == 0)
2802 					doesLineStartComment = true;
2803 			}
2804 		}
2805 	}
2806 	else if (isWhiteSpace(currentLine[charNum]) && !(charNum + 1 < (int) currentLine.length()))
2807 	{
2808 		lineIsEmpty = true;
2809 	}
2810 
2811 	// do not trim indented preprocessor define (except for comment continuation lines)
2812 	if (isInPreprocessor)
2813 	{
2814 		if (!doesLineStartComment)
2815 			leadingSpaces = 0;
2816 		charNum = 0;
2817 	}
2818 }
2819 
2820 /**
2821  * Append a character to the current formatted line.
2822  * The formattedLine split points are updated.
2823  *
2824  * @param ch               the character to append.
2825  * @param canBreakLine     if true, a registered line-break
2826  */
appendChar(char ch,bool canBreakLine)2827 void ASFormatter::appendChar(char ch, bool canBreakLine)
2828 {
2829 	if (canBreakLine && isInLineBreak)
2830 		breakLine();
2831 
2832 	formattedLine.append(1, ch);
2833 	isImmediatelyPostCommentOnly = false;
2834 	if (maxCodeLength != string::npos)
2835 	{
2836 		// These compares reduce the frequency of function calls.
2837 		if (isOkToSplitFormattedLine())
2838 			updateFormattedLineSplitPoints(ch);
2839 		if (formattedLine.length() > maxCodeLength)
2840 			testForTimeToSplitFormattedLine();
2841 	}
2842 }
2843 
2844 /**
2845  * Append a string sequence to the current formatted line.
2846  * The formattedLine split points are NOT updated.
2847  * But the formattedLine is checked for time to split.
2848  *
2849  * @param sequence         the sequence to append.
2850  * @param canBreakLine     if true, a registered line-break
2851  */
appendSequence(const string & sequence,bool canBreakLine)2852 void ASFormatter::appendSequence(const string& sequence, bool canBreakLine)
2853 {
2854 	if (canBreakLine && isInLineBreak)
2855 		breakLine();
2856 	formattedLine.append(sequence);
2857 	if (formattedLine.length() > maxCodeLength)
2858 		testForTimeToSplitFormattedLine();
2859 }
2860 
2861 /**
2862  * Append an operator sequence to the current formatted line.
2863  * The formattedLine split points are updated.
2864  *
2865  * @param sequence         the sequence to append.
2866  * @param canBreakLine     if true, a registered line-break
2867  */
appendOperator(const string & sequence,bool canBreakLine)2868 void ASFormatter::appendOperator(const string& sequence, bool canBreakLine)
2869 {
2870 	if (canBreakLine && isInLineBreak)
2871 		breakLine();
2872 	formattedLine.append(sequence);
2873 	if (maxCodeLength != string::npos)
2874 	{
2875 		// These compares reduce the frequency of function calls.
2876 		if (isOkToSplitFormattedLine())
2877 			updateFormattedLineSplitPointsOperator(sequence);
2878 		if (formattedLine.length() > maxCodeLength)
2879 			testForTimeToSplitFormattedLine();
2880 	}
2881 }
2882 
2883 /**
2884  * append a space to the current formattedline, UNLESS the
2885  * last character is already a white-space character.
2886  */
appendSpacePad()2887 void ASFormatter::appendSpacePad()
2888 {
2889 	int len = formattedLine.length();
2890 	if (len > 0 && !isWhiteSpace(formattedLine[len - 1]))
2891 	{
2892 		formattedLine.append(1, ' ');
2893 		spacePadNum++;
2894 		if (maxCodeLength != string::npos)
2895 		{
2896 			// These compares reduce the frequency of function calls.
2897 			if (isOkToSplitFormattedLine())
2898 				updateFormattedLineSplitPoints(' ');
2899 			if (formattedLine.length() > maxCodeLength)
2900 				testForTimeToSplitFormattedLine();
2901 		}
2902 	}
2903 }
2904 
2905 /**
2906  * append a space to the current formattedline, UNLESS the
2907  * next character is already a white-space character.
2908  */
appendSpaceAfter()2909 void ASFormatter::appendSpaceAfter()
2910 {
2911 	int len = currentLine.length();
2912 	if (charNum + 1 < len && !isWhiteSpace(currentLine[charNum + 1]))
2913 	{
2914 		formattedLine.append(1, ' ');
2915 		spacePadNum++;
2916 		if (maxCodeLength != string::npos)
2917 		{
2918 			// These compares reduce the frequency of function calls.
2919 			if (isOkToSplitFormattedLine())
2920 				updateFormattedLineSplitPoints(' ');
2921 			if (formattedLine.length() > maxCodeLength)
2922 				testForTimeToSplitFormattedLine();
2923 		}
2924 	}
2925 }
2926 
2927 /**
2928  * register a line break for the formatted line.
2929  */
breakLine(bool isSplitLine)2930 void ASFormatter::breakLine(bool isSplitLine /*false*/)
2931 {
2932 	isLineReady = true;
2933 	isInLineBreak = false;
2934 	spacePadNum = nextLineSpacePadNum;
2935 	nextLineSpacePadNum = 0;
2936 	readyFormattedLine = formattedLine;
2937 	formattedLine.erase();
2938 	// queue an empty line prepend request if one exists
2939 	prependEmptyLine = isPrependPostBlockEmptyLineRequested;
2940 
2941 	if (!isSplitLine)
2942 	{
2943 		formattedLineCommentNum = string::npos;
2944 		clearFormattedLineSplitPoints();
2945 
2946 		if (isAppendPostBlockEmptyLineRequested)
2947 		{
2948 			isAppendPostBlockEmptyLineRequested = false;
2949 			isPrependPostBlockEmptyLineRequested = true;
2950 		}
2951 		else
2952 			isPrependPostBlockEmptyLineRequested = false;
2953 	}
2954 }
2955 
2956 /**
2957  * check if the currently reached open-brace (i.e. '{')
2958  * opens a:
2959  * - a definition type block (such as a class or namespace),
2960  * - a command block (such as a method block)
2961  * - a static array
2962  * this method takes for granted that the current character
2963  * is an opening brace.
2964  *
2965  * @return    the type of the opened block.
2966  */
getBraceType()2967 BraceType ASFormatter::getBraceType()
2968 {
2969 	assert(currentChar == '{');
2970 
2971 	BraceType returnVal = NULL_TYPE;
2972 
2973 	if ((previousNonWSChar == '='
2974 	        || isBraceType(braceTypeStack->back(), ARRAY_TYPE))
2975 	        && previousCommandChar != ')'
2976 	        && !isNonParenHeader)
2977 		returnVal = ARRAY_TYPE;
2978 	else if (foundPreDefinitionHeader && previousCommandChar != ')')
2979 	{
2980 		returnVal = DEFINITION_TYPE;
2981 		if (foundNamespaceHeader)
2982 			returnVal = (BraceType)(returnVal | NAMESPACE_TYPE);
2983 		else if (foundClassHeader)
2984 			returnVal = (BraceType)(returnVal | CLASS_TYPE);
2985 		else if (foundStructHeader)
2986 			returnVal = (BraceType)(returnVal | STRUCT_TYPE);
2987 		else if (foundInterfaceHeader)
2988 			returnVal = (BraceType)(returnVal | INTERFACE_TYPE);
2989 	}
2990 	else if (isInEnum)
2991 	{
2992 		returnVal = (BraceType)(ARRAY_TYPE | ENUM_TYPE);
2993 	}
2994 	else
2995 	{
2996 		bool isCommandType = (foundPreCommandHeader
2997 		                      || foundPreCommandMacro
2998 		                      || (currentHeader != nullptr && isNonParenHeader)
2999 		                      || (previousCommandChar == ')')
3000 		                      || (previousCommandChar == ':' && !foundQuestionMark)
3001 		                      || (previousCommandChar == ';')
3002 		                      || ((previousCommandChar == '{' || previousCommandChar == '}')
3003 		                          && isPreviousBraceBlockRelated)
3004 		                      || (isInClassInitializer
3005 		                          && ((!isLegalNameChar(previousNonWSChar) && previousNonWSChar != '(')
3006 		                              || foundPreCommandHeader))
3007 		                      || foundTrailingReturnType
3008 		                      || isInObjCMethodDefinition
3009 		                      || isInObjCInterface
3010 		                      || isJavaStaticConstructor
3011 		                      || isSharpDelegate);
3012 
3013 		// C# methods containing 'get', 'set', 'add', and 'remove' do NOT end with parens
3014 		if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1))
3015 		{
3016 			isCommandType = true;
3017 			isSharpAccessor = true;
3018 		}
3019 
3020 		if (isInExternC)
3021 			returnVal = (isCommandType ? COMMAND_TYPE : EXTERN_TYPE);
3022 		else
3023 			returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE);
3024 	}
3025 
3026 	int foundOneLineBlock = isOneLineBlockReached(currentLine, charNum);
3027 
3028 	if (foundOneLineBlock == 2 && returnVal == COMMAND_TYPE)
3029 		returnVal = ARRAY_TYPE;
3030 
3031 	if (foundOneLineBlock > 0)
3032 	{
3033 		returnVal = (BraceType) (returnVal | SINGLE_LINE_TYPE);
3034 		if (breakCurrentOneLineBlock)
3035 			returnVal = (BraceType) (returnVal | BREAK_BLOCK_TYPE);
3036 		if (foundOneLineBlock == 3)
3037 			returnVal = (BraceType)(returnVal | EMPTY_BLOCK_TYPE);
3038 	}
3039 
3040 	if (isBraceType(returnVal, ARRAY_TYPE))
3041 	{
3042 		if (isNonInStatementArrayBrace())
3043 		{
3044 			returnVal = (BraceType)(returnVal | ARRAY_NIS_TYPE);
3045 			isNonInStatementArray = true;
3046 			isImmediatelyPostNonInStmt = false;		// in case of "},{"
3047 			nonInStatementBrace = formattedLine.length() - 1;
3048 		}
3049 		if (isUniformInitializerBrace())
3050 			returnVal = (BraceType)(returnVal | INIT_TYPE);
3051 	}
3052 
3053 	return returnVal;
3054 }
3055 
isNumericVariable(string word) const3056 bool ASFormatter::isNumericVariable(string word) const
3057 {
3058 	if (word == "bool"
3059 	        || word == "int"
3060 	        || word == "void"
3061 	        || word == "char"
3062 	        || word == "long"
3063 	        || word == "short"
3064 	        || word == "double"
3065 	        || word == "float"
3066 	        || (word.length() >= 4     // check end of word for _t
3067 	            && word.compare(word.length() - 2, 2, "_t") == 0)
3068 // removed release 3.1
3069 //	        || word == "Int32"
3070 //	        || word == "UInt32"
3071 //	        || word == "Int64"
3072 //	        || word == "UInt64"
3073 	        || word == "BOOL"
3074 	        || word == "DWORD"
3075 	        || word == "HWND"
3076 	        || word == "INT"
3077 	        || word == "LPSTR"
3078 	        || word == "VOID"
3079 	        || word == "LPVOID"
3080 	        || word == "wxFontEncoding"
3081 	   )
3082 		return true;
3083 	return false;
3084 }
3085 
3086 /**
3087 * check if a colon is a class initializer separator
3088 *
3089 * @return        whether it is a class initializer separator
3090 */
isClassInitializer() const3091 bool ASFormatter::isClassInitializer() const
3092 {
3093 	assert(currentChar == ':');
3094 	assert(previousChar != ':' && peekNextChar() != ':');	// not part of '::'
3095 
3096 	// this should be similar to ASBeautifier::parseCurrentLine()
3097 	bool foundClassInitializer = false;
3098 
3099 	if (foundQuestionMark)
3100 	{
3101 		// do nothing special
3102 	}
3103 	else if (parenStack->back() > 0)
3104 	{
3105 		// found a 'for' loop or an objective-C statement
3106 		// so do nothing special
3107 	}
3108 	else if (isInEnum)
3109 	{
3110 		// found an enum with a base-type
3111 	}
3112 	else if (isCStyle()
3113 	         && !isInCase
3114 	         && (previousCommandChar == ')' || foundPreCommandHeader))
3115 	{
3116 		// found a 'class' c'tor initializer
3117 		foundClassInitializer = true;
3118 	}
3119 	return foundClassInitializer;
3120 }
3121 
3122 /**
3123  * check if a line is empty
3124  *
3125  * @return        whether line is empty
3126  */
isEmptyLine(const string & line) const3127 bool ASFormatter::isEmptyLine(const string& line) const
3128 {
3129 	return line.find_first_not_of(" \t") == string::npos;
3130 }
3131 
3132 /**
3133  * Check if the following text is "C" as in extern "C".
3134  *
3135  * @return        whether the statement is extern "C"
3136  */
isExternC() const3137 bool ASFormatter::isExternC() const
3138 {
3139 	// charNum should be at 'extern'
3140 	assert(!isWhiteSpace(currentLine[charNum]));
3141 	size_t startQuote = currentLine.find_first_of(" \t\"", charNum);
3142 	if (startQuote == string::npos)
3143 		return false;
3144 	startQuote = currentLine.find_first_not_of(" \t", startQuote);
3145 	if (startQuote == string::npos)
3146 		return false;
3147 	if (currentLine.compare(startQuote, 3, "\"C\"") != 0)
3148 		return false;
3149 	return true;
3150 }
3151 
3152 /**
3153  * Check if the currently reached '*', '&' or '^' character is
3154  * a pointer-or-reference symbol, or another operator.
3155  * A pointer dereference (*) or an "address of" character (&)
3156  * counts as a pointer or reference because it is not an
3157  * arithmetic operator.
3158  *
3159  * @return        whether current character is a reference-or-pointer
3160  */
isPointerOrReference() const3161 bool ASFormatter::isPointerOrReference() const
3162 {
3163 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
3164 
3165 	if (isJavaStyle())
3166 		return false;
3167 
3168 	if (isCharImmediatelyPostOperator)
3169 		return false;
3170 
3171 	// get the last legal word (may be a number)
3172 	string lastWord = getPreviousWord(currentLine, charNum);
3173 	if (lastWord.empty())
3174 		lastWord = " ";
3175 
3176 	// check for preceding or following numeric values
3177 	string nextText = peekNextText(currentLine.substr(charNum + 1));
3178 	if (nextText.length() == 0)
3179 		nextText = " ";
3180 	if (isDigit(lastWord[0])
3181 	        || isDigit(nextText[0])
3182 	        || nextText[0] == '!'
3183 	        || nextText[0] == '~')
3184 		return false;
3185 
3186 	// check for multiply then a dereference (a * *b)
3187 	char nextChar = peekNextChar();
3188 	if (currentChar == '*'
3189 	        && nextChar == '*'
3190 	        && !isPointerToPointer(currentLine, charNum))
3191 		return false;
3192 
3193 	if ((foundCastOperator && nextChar == '>')
3194 	        || isPointerOrReferenceVariable(lastWord))
3195 		return true;
3196 
3197 	if (isInClassInitializer
3198 	        && previousNonWSChar != '('
3199 	        && previousNonWSChar != '{'
3200 	        && previousCommandChar != ','
3201 	        && nextChar != ')'
3202 	        && nextChar != '}')
3203 		return false;
3204 
3205 	//check for rvalue reference
3206 	if (currentChar == '&' && nextChar == '&')
3207 	{
3208 		if (lastWord == AS_AUTO)
3209 			return true;
3210 		if (previousNonWSChar == '>')
3211 			return true;
3212 		string followingText;
3213 		if ((int) currentLine.length() > charNum + 2)
3214 			followingText = peekNextText(currentLine.substr(charNum + 2));
3215 		if (followingText.length() > 0 && followingText[0] == ')')
3216 			return true;
3217 		if (currentHeader != nullptr || isInPotentialCalculation)
3218 			return false;
3219 		if (parenStack->back() > 0 && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
3220 			return false;
3221 		return true;
3222 	}
3223 	if (nextChar == '*'
3224 	        || previousNonWSChar == '='
3225 	        || previousNonWSChar == '('
3226 	        || previousNonWSChar == '['
3227 	        || isCharImmediatelyPostReturn
3228 	        || isInTemplate
3229 	        || isCharImmediatelyPostTemplate
3230 	        || currentHeader == &AS_CATCH
3231 	        || currentHeader == &AS_FOREACH
3232 	        || currentHeader == &AS_QFOREACH)
3233 		return true;
3234 
3235 	if (isBraceType(braceTypeStack->back(), ARRAY_TYPE)
3236 	        && isLegalNameChar(lastWord[0])
3237 	        && isLegalNameChar(nextChar)
3238 	        && previousNonWSChar != ')')
3239 	{
3240 		if (isArrayOperator())
3241 			return false;
3242 	}
3243 
3244 	// checks on operators in parens
3245 	if (parenStack->back() > 0
3246 	        && isLegalNameChar(lastWord[0])
3247 	        && isLegalNameChar(nextChar))
3248 	{
3249 		// if followed by an assignment it is a pointer or reference
3250 		// if followed by semicolon it is a pointer or reference in range-based for
3251 		const string* followingOperator = getFollowingOperator();
3252 		if (followingOperator != nullptr
3253 		        && followingOperator != &AS_MULT
3254 		        && followingOperator != &AS_BIT_AND)
3255 		{
3256 			if (followingOperator == &AS_ASSIGN || followingOperator == &AS_COLON)
3257 				return true;
3258 			return false;
3259 		}
3260 
3261 		if (isBraceType(braceTypeStack->back(), COMMAND_TYPE)
3262 		        || squareBracketCount > 0)
3263 			return false;
3264 		return true;
3265 	}
3266 
3267 	// checks on operators in parens with following '('
3268 	if (parenStack->back() > 0
3269 	        && nextChar == '('
3270 	        && previousNonWSChar != ','
3271 	        && previousNonWSChar != '('
3272 	        && previousNonWSChar != '!'
3273 	        && previousNonWSChar != '&'
3274 	        && previousNonWSChar != '*'
3275 	        && previousNonWSChar != '|')
3276 		return false;
3277 
3278 	if (nextChar == '-'
3279 	        || nextChar == '+')
3280 	{
3281 		size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
3282 		if (nextNum != string::npos)
3283 		{
3284 			if (currentLine.compare(nextNum, 2, "++") != 0
3285 			        && currentLine.compare(nextNum, 2, "--") != 0)
3286 				return false;
3287 		}
3288 	}
3289 
3290 	bool isPR = (!isInPotentialCalculation
3291 	             || (!isLegalNameChar(previousNonWSChar)
3292 	                 && !(previousNonWSChar == ')' && nextChar == '(')
3293 	                 && !(previousNonWSChar == ')' && currentChar == '*' && !isImmediatelyPostCast())
3294 	                 && previousNonWSChar != ']')
3295 	             || (!isWhiteSpace(nextChar)
3296 	                 && nextChar != '-'
3297 	                 && nextChar != '('
3298 	                 && nextChar != '['
3299 	                 && !isLegalNameChar(nextChar))
3300 	            );
3301 
3302 	return isPR;
3303 }
3304 
3305 /**
3306  * Check if the currently reached  '*' or '&' character is
3307  * a dereferenced pointer or "address of" symbol.
3308  * NOTE: this MUST be a pointer or reference as determined by
3309  * the function isPointerOrReference().
3310  *
3311  * @return        whether current character is a dereference or address of
3312  */
isDereferenceOrAddressOf() const3313 bool ASFormatter::isDereferenceOrAddressOf() const
3314 {
3315 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
3316 
3317 	if (isCharImmediatelyPostTemplate)
3318 		return false;
3319 
3320 	if (previousNonWSChar == '='
3321 	        || previousNonWSChar == ','
3322 	        || previousNonWSChar == '.'
3323 	        || previousNonWSChar == '{'
3324 	        || previousNonWSChar == '>'
3325 	        || previousNonWSChar == '<'
3326 	        || previousNonWSChar == '?'
3327 	        || isCharImmediatelyPostLineComment
3328 	        || isCharImmediatelyPostComment
3329 	        || isCharImmediatelyPostReturn)
3330 		return true;
3331 
3332 	char nextChar = peekNextChar();
3333 	if (currentChar == '*' && nextChar == '*')
3334 	{
3335 		if (previousNonWSChar == '(')
3336 			return true;
3337 		if ((int) currentLine.length() < charNum + 2)
3338 			return true;
3339 		return false;
3340 	}
3341 	if (currentChar == '&' && nextChar == '&')
3342 	{
3343 		if (previousNonWSChar == '(' || isInTemplate)
3344 			return true;
3345 		if ((int) currentLine.length() < charNum + 2)
3346 			return true;
3347 		return false;
3348 	}
3349 
3350 	// check first char on the line
3351 	if (charNum == (int) currentLine.find_first_not_of(" \t")
3352 	        && (isBraceType(braceTypeStack->back(), COMMAND_TYPE)
3353 	            || parenStack->back() != 0))
3354 		return true;
3355 
3356 	string nextText = peekNextText(currentLine.substr(charNum + 1));
3357 	if (nextText.length() > 0)
3358 	{
3359 		if (nextText[0] == ')' || nextText[0] == '>'
3360 		        || nextText[0] == ',' || nextText[0] == '=')
3361 			return false;
3362 		if (nextText[0] == ';')
3363 			return true;
3364 	}
3365 
3366 	// check for reference to a pointer *&
3367 	if ((currentChar == '*' && nextChar == '&')
3368 	        || (previousNonWSChar == '*' && currentChar == '&'))
3369 		return false;
3370 
3371 	if (!isBraceType(braceTypeStack->back(), COMMAND_TYPE)
3372 	        && parenStack->back() == 0)
3373 		return false;
3374 
3375 	string lastWord = getPreviousWord(currentLine, charNum);
3376 	if (lastWord == "else" || lastWord == "delete")
3377 		return true;
3378 
3379 	if (isPointerOrReferenceVariable(lastWord))
3380 		return false;
3381 
3382 	bool isDA = (!(isLegalNameChar(previousNonWSChar) || previousNonWSChar == '>')
3383 	             || (nextText.length() > 0 && !isLegalNameChar(nextText[0]) && nextText[0] != '/')
3384 	             || (ispunct((unsigned char)previousNonWSChar) && previousNonWSChar != '.')
3385 	             || isCharImmediatelyPostReturn);
3386 
3387 	return isDA;
3388 }
3389 
3390 /**
3391  * Check if the currently reached  '*' or '&' character is
3392  * centered with one space on each side.
3393  * Only spaces are checked, not tabs.
3394  * If true then a space will be deleted on the output.
3395  *
3396  * @return        whether current character is centered.
3397  */
isPointerOrReferenceCentered() const3398 bool ASFormatter::isPointerOrReferenceCentered() const
3399 {
3400 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
3401 
3402 	int prNum = charNum;
3403 	int lineLength = (int) currentLine.length();
3404 
3405 	// check for end of line
3406 	if (peekNextChar() == ' ')
3407 		return false;
3408 
3409 	// check space before
3410 	if (prNum < 1
3411 	        || currentLine[prNum - 1] != ' ')
3412 		return false;
3413 
3414 	// check no space before that
3415 	if (prNum < 2
3416 	        || currentLine[prNum - 2] == ' ')
3417 		return false;
3418 
3419 	// check for ** or &&
3420 	if (prNum + 1 < lineLength
3421 	        && (currentLine[prNum + 1] == '*' || currentLine[prNum + 1] == '&'))
3422 		prNum++;
3423 
3424 	// check space after
3425 	if (prNum + 1 <= lineLength
3426 	        && currentLine[prNum + 1] != ' ')
3427 		return false;
3428 
3429 	// check no space after that
3430 	if (prNum + 2 < lineLength
3431 	        && currentLine[prNum + 2] == ' ')
3432 		return false;
3433 
3434 	return true;
3435 }
3436 
3437 /**
3438  * Check if a word is a pointer or reference variable type.
3439  *
3440  * @return        whether word is a pointer or reference variable.
3441  */
isPointerOrReferenceVariable(const string & word) const3442 bool ASFormatter::isPointerOrReferenceVariable(const string& word) const
3443 {
3444 	return (word == "char"
3445 	        || word == "int"
3446 	        || word == "void"
3447 	        || (word.length() >= 6     // check end of word for _t
3448 	            && word.compare(word.length() - 2, 2, "_t") == 0)
3449 	        || word == "INT"
3450 	        || word == "VOID");
3451 }
3452 
3453 /**
3454  * Check if * * is a pointer to a pointer or a multiply then a dereference.
3455  *
3456  * @return        true if a pointer *.
3457  */
isPointerToPointer(const string & line,int currPos) const3458 bool ASFormatter::isPointerToPointer(const string& line, int currPos) const
3459 {
3460 	assert(line[currPos] == '*' && peekNextChar() == '*');
3461 	if ((int) line.length() > currPos + 1 && line[currPos + 1] == '*')
3462 		return true;
3463 	size_t nextText = line.find_first_not_of(" \t", currPos + 1);
3464 	if (nextText == string::npos || line[nextText] != '*')
3465 		return false;
3466 	size_t nextText2 = line.find_first_not_of(" \t", nextText + 1);
3467 	if (nextText == string::npos)
3468 		return false;
3469 	if (line[nextText2] == ')' || line[nextText2] == '*')
3470 		return true;
3471 	return false;
3472 }
3473 
3474 /**
3475  * check if the currently reached '+' or '-' character is a unary operator
3476  * this method takes for granted that the current character
3477  * is a '+' or '-'.
3478  *
3479  * @return        whether the current '+' or '-' is a unary operator.
3480  */
isUnaryOperator() const3481 bool ASFormatter::isUnaryOperator() const
3482 {
3483 	assert(currentChar == '+' || currentChar == '-');
3484 
3485 	// does a digit follow a c-style cast
3486 	if (previousCommandChar == ')')
3487 	{
3488 		if (!isdigit(peekNextChar()))
3489 			return false;
3490 		size_t end = currentLine.rfind(')', charNum);
3491 		if (end == string::npos)
3492 			return false;
3493 		size_t lastChar = currentLine.find_last_not_of(" \t", end - 1);
3494 		if (lastChar == string::npos)
3495 			return false;
3496 		if (currentLine[lastChar] == '*')
3497 			end = lastChar;
3498 		string prevWord = getPreviousWord(currentLine, end);
3499 		if (prevWord.empty())
3500 			return false;
3501 		if (!isNumericVariable(prevWord))
3502 			return false;
3503 		return true;
3504 	}
3505 
3506 	return ((isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar))
3507 	        && previousCommandChar != '.'
3508 	        && previousCommandChar != '\"'
3509 	        && previousCommandChar != '\''
3510 	        && previousCommandChar != ']');
3511 }
3512 
3513 /**
3514  * check if the currently reached comment is in a 'switch' statement
3515  *
3516  * @return        whether the current '+' or '-' is in an exponent.
3517  */
isInSwitchStatement() const3518 bool ASFormatter::isInSwitchStatement() const
3519 {
3520 	assert(isInLineComment || isInComment);
3521 	if (!preBraceHeaderStack->empty())
3522 		for (size_t i = 1; i < preBraceHeaderStack->size(); i++)
3523 			if (preBraceHeaderStack->at(i) == &AS_SWITCH)
3524 				return true;
3525 	return false;
3526 }
3527 
3528 /**
3529  * check if the currently reached '+' or '-' character is
3530  * part of an exponent, i.e. 0.2E-5.
3531  *
3532  * @return        whether the current '+' or '-' is in an exponent.
3533  */
isInExponent() const3534 bool ASFormatter::isInExponent() const
3535 {
3536 	assert(currentChar == '+' || currentChar == '-');
3537 
3538 	if (charNum >= 2)
3539 	{
3540 		char prevPrevFormattedChar = currentLine[charNum - 2];
3541 		char prevFormattedChar = currentLine[charNum - 1];
3542 		return ((prevFormattedChar == 'e' || prevFormattedChar == 'E')
3543 		        && (prevPrevFormattedChar == '.' || isDigit(prevPrevFormattedChar)));
3544 	}
3545 	return false;
3546 }
3547 
3548 /**
3549  * check if an array brace should NOT have an in-statement indent
3550  *
3551  * @return        the array is non in-statement
3552  */
isNonInStatementArrayBrace() const3553 bool ASFormatter::isNonInStatementArrayBrace() const
3554 {
3555 	bool returnVal = false;
3556 	char nextChar = peekNextChar();
3557 	// if this opening brace begins the line there will be no inStatement indent
3558 	if (currentLineBeginsWithBrace
3559 	        && (size_t) charNum == currentLineFirstBraceNum
3560 	        && nextChar != '}')
3561 		returnVal = true;
3562 	// if an opening brace ends the line there will be no inStatement indent
3563 	if (isWhiteSpace(nextChar)
3564 	        || isBeforeAnyLineEndComment(charNum)
3565 	        || nextChar == '{')
3566 		returnVal = true;
3567 
3568 	// Java "new Type [] {...}" IS an inStatement indent
3569 	if (isJavaStyle() && previousNonWSChar == ']')
3570 		returnVal = false;
3571 
3572 	return returnVal;
3573 }
3574 
3575 /**
3576  * check if a one-line block has been reached,
3577  * i.e. if the currently reached '{' character is closed
3578  * with a complimentary '}' elsewhere on the current line,
3579  *.
3580  * @return     0 = one-line block has not been reached.
3581  *             1 = one-line block has been reached.
3582  *             2 = one-line block has been reached and is followed by a comma.
3583  *             3 = one-line block has been reached and is an empty block.
3584  */
isOneLineBlockReached(const string & line,int startChar) const3585 int ASFormatter::isOneLineBlockReached(const string& line, int startChar) const
3586 {
3587 	assert(line[startChar] == '{');
3588 
3589 	bool isInComment_ = false;
3590 	bool isInQuote_ = false;
3591 	bool hasText = false;
3592 	int braceCount = 0;
3593 	int lineLength = line.length();
3594 	char quoteChar_ = ' ';
3595 	char ch = ' ';
3596 	char prevCh = ' ';
3597 
3598 	for (int i = startChar; i < lineLength; ++i)
3599 	{
3600 		ch = line[i];
3601 
3602 		if (isInComment_)
3603 		{
3604 			if (line.compare(i, 2, "*/") == 0)
3605 			{
3606 				isInComment_ = false;
3607 				++i;
3608 			}
3609 			continue;
3610 		}
3611 
3612 		if (isInQuote_)
3613 		{
3614 			if (ch == '\\')
3615 				++i;
3616 			else if (ch == quoteChar_)
3617 				isInQuote_ = false;
3618 			continue;
3619 		}
3620 
3621 		if (ch == '"'
3622 		        || (ch == '\'' && !isDigitSeparator(line, i)))
3623 		{
3624 			isInQuote_ = true;
3625 			quoteChar_ = ch;
3626 			continue;
3627 		}
3628 
3629 		if (line.compare(i, 2, "//") == 0)
3630 			break;
3631 
3632 		if (line.compare(i, 2, "/*") == 0)
3633 		{
3634 			isInComment_ = true;
3635 			++i;
3636 			continue;
3637 		}
3638 
3639 		if (ch == '{')
3640 		{
3641 			++braceCount;
3642 			continue;
3643 		}
3644 		if (ch == '}')
3645 		{
3646 			--braceCount;
3647 			if (braceCount == 0)
3648 			{
3649 				// is this an array?
3650 				if (parenStack->back() == 0 && prevCh != '}')
3651 				{
3652 					size_t peekNum = line.find_first_not_of(" \t", i + 1);
3653 					if (peekNum != string::npos && line[peekNum] == ',')
3654 						return 2;
3655 				}
3656 				if (!hasText)
3657 					return 3;	// is an empty block
3658 				return 1;
3659 			}
3660 		}
3661 		if (ch == ';')
3662 			continue;
3663 		if (!isWhiteSpace(ch))
3664 		{
3665 			hasText = true;
3666 			prevCh = ch;
3667 		}
3668 	}
3669 
3670 	return 0;
3671 }
3672 
3673 /**
3674  * peek at the next word to determine if it is a C# non-paren header.
3675  * will look ahead in the input file if necessary.
3676  *
3677  * @param  startChar      position on currentLine to start the search
3678  * @return                true if the next word is get or set.
3679  */
isNextWordSharpNonParenHeader(int startChar) const3680 bool ASFormatter::isNextWordSharpNonParenHeader(int startChar) const
3681 {
3682 	// look ahead to find the next non-comment text
3683 	string nextText = peekNextText(currentLine.substr(startChar));
3684 	if (nextText.length() == 0)
3685 		return false;
3686 	if (nextText[0] == '[')
3687 		return true;
3688 	if (!isCharPotentialHeader(nextText, 0))
3689 		return false;
3690 	if (findKeyword(nextText, 0, AS_GET) || findKeyword(nextText, 0, AS_SET)
3691 	        || findKeyword(nextText, 0, AS_ADD) || findKeyword(nextText, 0, AS_REMOVE))
3692 		return true;
3693 	return false;
3694 }
3695 
3696 /**
3697  * peek at the next char to determine if it is an opening brace.
3698  * will look ahead in the input file if necessary.
3699  * this determines a java static constructor.
3700  *
3701  * @param startChar     position on currentLine to start the search
3702  * @return              true if the next word is an opening brace.
3703  */
isNextCharOpeningBrace(int startChar) const3704 bool ASFormatter::isNextCharOpeningBrace(int startChar) const
3705 {
3706 	bool retVal = false;
3707 	string nextText = peekNextText(currentLine.substr(startChar));
3708 	if (nextText.length() > 0
3709 	        && nextText.compare(0, 1, "{") == 0)
3710 		retVal = true;
3711 	return retVal;
3712 }
3713 
3714 /**
3715 * Check if operator and, pointer, and reference padding is disabled.
3716 * Disabling is done thru a NOPAD tag in an ending comment.
3717 *
3718 * @return              true if the formatting on this line is disabled.
3719 */
isOperatorPaddingDisabled() const3720 bool ASFormatter::isOperatorPaddingDisabled() const
3721 {
3722 	size_t commentStart = currentLine.find("//", charNum);
3723 	if (commentStart == string::npos)
3724 	{
3725 		commentStart = currentLine.find("/*", charNum);
3726 		// comment must end on this line
3727 		if (commentStart != string::npos)
3728 		{
3729 			size_t commentEnd = currentLine.find("*/", commentStart + 2);
3730 			if (commentEnd == string::npos)
3731 				commentStart = string::npos;
3732 		}
3733 	}
3734 	if (commentStart == string::npos)
3735 		return false;
3736 	size_t noPadStart = currentLine.find("*NOPAD*", commentStart);
3737 	if (noPadStart == string::npos)
3738 		return false;
3739 	return true;
3740 }
3741 
3742 /**
3743 * Determine if an opening array-type brace should have a leading space pad.
3744 * This is to identify C++11 uniform initializers.
3745 */
isUniformInitializerBrace() const3746 bool ASFormatter::isUniformInitializerBrace() const
3747 {
3748 	if (isCStyle() && !isInEnum && !isImmediatelyPostPreprocessor)
3749 	{
3750 		if (isInClassInitializer
3751 		        || isLegalNameChar(previousNonWSChar)
3752 		        || previousNonWSChar == '(')
3753 			return true;
3754 	}
3755 	return false;
3756 }
3757 
3758 /**
3759 * Determine if there is a following statement on the current line.
3760 */
isMultiStatementLine() const3761 bool ASFormatter::isMultiStatementLine() const
3762 {
3763 	assert((isImmediatelyPostHeader || foundClosingHeader));
3764 	bool isInComment_ = false;
3765 	bool isInQuote_ = false;
3766 	int  semiCount_ = 0;
3767 	int  parenCount_ = 0;
3768 	int  braceCount_ = 0;
3769 
3770 	for (size_t i = 0; i < currentLine.length(); i++)
3771 	{
3772 		if (isInComment_)
3773 		{
3774 			if (currentLine.compare(i, 2, "*/") == 0)
3775 			{
3776 				isInComment_ = false;
3777 				continue;
3778 			}
3779 		}
3780 		if (currentLine.compare(i, 2, "/*") == 0)
3781 		{
3782 			isInComment_ = true;
3783 			continue;
3784 		}
3785 		if (currentLine.compare(i, 2, "//") == 0)
3786 			return false;
3787 		if (isInQuote_)
3788 		{
3789 			if (currentLine[i] == '"' || currentLine[i] == '\'')
3790 				isInQuote_ = false;
3791 			continue;
3792 		}
3793 		if (currentLine[i] == '"' || currentLine[i] == '\'')
3794 		{
3795 			isInQuote_ = true;
3796 			continue;
3797 		}
3798 		if (currentLine[i] == '(')
3799 		{
3800 			++parenCount_;
3801 			continue;
3802 		}
3803 		if (currentLine[i] == ')')
3804 		{
3805 			--parenCount_;
3806 			continue;
3807 		}
3808 		if (parenCount_ > 0)
3809 			continue;
3810 		if (currentLine[i] == '{')
3811 		{
3812 			++braceCount_;
3813 		}
3814 		if (currentLine[i] == '}')
3815 		{
3816 			--braceCount_;
3817 		}
3818 		if (braceCount_ > 0)
3819 			continue;
3820 		if (currentLine[i] == ';')
3821 		{
3822 			++semiCount_;
3823 			if (semiCount_ > 1)
3824 				return true;
3825 			continue;
3826 		}
3827 	}
3828 	return false;
3829 }
3830 
3831 /**
3832  * get the next non-whitespace substring on following lines, bypassing all comments.
3833  *
3834  * @param   firstLine   the first line to check
3835  * @return  the next non-whitespace substring.
3836  */
peekNextText(const string & firstLine,bool endOnEmptyLine,shared_ptr<ASPeekStream> streamArg) const3837 string ASFormatter::peekNextText(const string& firstLine,
3838                                  bool endOnEmptyLine /*false*/,
3839                                  shared_ptr<ASPeekStream> streamArg /*nullptr*/) const
3840 {
3841 	assert(sourceIterator->getPeekStart() == 0 || streamArg != nullptr);	// Borland may need != 0
3842 	bool isFirstLine = true;
3843 	string nextLine_ = firstLine;
3844 	size_t firstChar = string::npos;
3845 	shared_ptr<ASPeekStream> stream = streamArg;
3846 	if (stream == nullptr)					// Borland may need == 0
3847 		stream = make_shared<ASPeekStream>(sourceIterator);
3848 
3849 	// find the first non-blank text, bypassing all comments.
3850 	bool isInComment_ = false;
3851 	while (stream->hasMoreLines() || isFirstLine)
3852 	{
3853 		if (isFirstLine)
3854 			isFirstLine = false;
3855 		else
3856 			nextLine_ = stream->peekNextLine();
3857 
3858 		firstChar = nextLine_.find_first_not_of(" \t");
3859 		if (firstChar == string::npos)
3860 		{
3861 			if (endOnEmptyLine && !isInComment_)
3862 				break;
3863 			continue;
3864 		}
3865 
3866 		if (nextLine_.compare(firstChar, 2, "/*") == 0)
3867 		{
3868 			firstChar += 2;
3869 			isInComment_ = true;
3870 		}
3871 
3872 		if (isInComment_)
3873 		{
3874 			firstChar = nextLine_.find("*/", firstChar);
3875 			if (firstChar == string::npos)
3876 				continue;
3877 			firstChar += 2;
3878 			isInComment_ = false;
3879 			firstChar = nextLine_.find_first_not_of(" \t", firstChar);
3880 			if (firstChar == string::npos)
3881 				continue;
3882 		}
3883 
3884 		if (nextLine_.compare(firstChar, 2, "//") == 0)
3885 			continue;
3886 
3887 		// found the next text
3888 		break;
3889 	}
3890 
3891 	if (firstChar == string::npos)
3892 		nextLine_ = "";
3893 	else
3894 		nextLine_ = nextLine_.substr(firstChar);
3895 	return nextLine_;
3896 }
3897 
3898 /**
3899  * adjust comment position because of adding or deleting spaces
3900  * the spaces are added or deleted to formattedLine
3901  * spacePadNum contains the adjustment
3902  */
adjustComments()3903 void ASFormatter::adjustComments()
3904 {
3905 	assert(spacePadNum != 0);
3906 	assert(isSequenceReached("//") || isSequenceReached("/*"));
3907 
3908 	// block comment must be closed on this line with nothing after it
3909 	if (isSequenceReached("/*"))
3910 	{
3911 		size_t endNum = currentLine.find("*/", charNum + 2);
3912 		if (endNum == string::npos)
3913 			return;
3914 		// following line comments may be a tag from AStyleWx //[[)>
3915 		size_t nextNum = currentLine.find_first_not_of(" \t", endNum + 2);
3916 		if (nextNum != string::npos
3917 		        && currentLine.compare(nextNum, 2, "//") != 0)
3918 			return;
3919 	}
3920 
3921 	size_t len = formattedLine.length();
3922 	// don't adjust a tab
3923 	if (formattedLine[len - 1] == '\t')
3924 		return;
3925 	// if spaces were removed, need to add spaces before the comment
3926 	if (spacePadNum < 0)
3927 	{
3928 		int adjust = -spacePadNum;          // make the number positive
3929 		formattedLine.append(adjust, ' ');
3930 	}
3931 	// if spaces were added, need to delete extra spaces before the comment
3932 	// if cannot be done put the comment one space after the last text
3933 	else if (spacePadNum > 0)
3934 	{
3935 		int adjust = spacePadNum;
3936 		size_t lastText = formattedLine.find_last_not_of(' ');
3937 		if (lastText != string::npos
3938 		        && lastText < len - adjust - 1)
3939 			formattedLine.resize(len - adjust);
3940 		else if (len > lastText + 2)
3941 			formattedLine.resize(lastText + 2);
3942 		else if (len < lastText + 2)
3943 			formattedLine.append(len - lastText, ' ');
3944 	}
3945 }
3946 
3947 /**
3948  * append the current brace inside the end of line comments
3949  * currentChar contains the brace, it will be appended to formattedLine
3950  * formattedLineCommentNum is the comment location on formattedLine
3951  */
appendCharInsideComments()3952 void ASFormatter::appendCharInsideComments()
3953 {
3954 	if (formattedLineCommentNum == string::npos     // does the comment start on the previous line?
3955 	        || formattedLineCommentNum == 0)
3956 	{
3957 		appendCurrentChar();                        // don't attach
3958 		return;
3959 	}
3960 	assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0
3961 	       || formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0);
3962 
3963 	// find the previous non space char
3964 	size_t end = formattedLineCommentNum;
3965 	size_t beg = formattedLine.find_last_not_of(" \t", end - 1);
3966 	if (beg == string::npos)
3967 	{
3968 		appendCurrentChar();                // don't attach
3969 		return;
3970 	}
3971 	beg++;
3972 
3973 	// insert the brace
3974 	if (end - beg < 3)                      // is there room to insert?
3975 		formattedLine.insert(beg, 3 - end + beg, ' ');
3976 	if (formattedLine[beg] == '\t')         // don't pad with a tab
3977 		formattedLine.insert(beg, 1, ' ');
3978 	formattedLine[beg + 1] = currentChar;
3979 	testForTimeToSplitFormattedLine();
3980 
3981 	if (isBeforeComment())
3982 		breakLine();
3983 	else if (isCharImmediatelyPostLineComment)
3984 		shouldBreakLineAtNextChar = true;
3985 }
3986 
3987 /**
3988  * add or remove space padding to operators
3989  * the operators and necessary padding will be appended to formattedLine
3990  * the calling function should have a continue statement after calling this method
3991  *
3992  * @param newOperator     the operator to be padded
3993  */
padOperators(const string * newOperator)3994 void ASFormatter::padOperators(const string* newOperator)
3995 {
3996 	assert(shouldPadOperators);
3997 	assert(newOperator != nullptr);
3998 
3999 	char nextNonWSChar = ASBase::peekNextChar(currentLine, charNum);
4000 	bool shouldPad = (newOperator != &AS_SCOPE_RESOLUTION
4001 	                  && newOperator != &AS_PLUS_PLUS
4002 	                  && newOperator != &AS_MINUS_MINUS
4003 	                  && newOperator != &AS_NOT
4004 	                  && newOperator != &AS_BIT_NOT
4005 	                  && newOperator != &AS_ARROW
4006 	                  && !(newOperator == &AS_COLON && !foundQuestionMark			// objC methods
4007 	                       && (isInObjCMethodDefinition || isInObjCInterface
4008 	                           || isInObjCSelector || squareBracketCount != 0))
4009 	                  && !(newOperator == &AS_MINUS && isInExponent())
4010 	                  && !(newOperator == &AS_PLUS && isInExponent())
4011 	                  && !((newOperator == &AS_PLUS || newOperator == &AS_MINUS)	// check for unary plus or minus
4012 	                       && (previousNonWSChar == '('
4013 	                           || previousNonWSChar == '['
4014 	                           || previousNonWSChar == '='
4015 	                           || previousNonWSChar == ','
4016 	                           || previousNonWSChar == ':'
4017 	                           || previousNonWSChar == '{'))
4018 //?                   // commented out in release 2.05.1 - doesn't seem to do anything???
4019 //x                   && !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND || newOperator == &AS_AND)
4020 //x                        && isPointerOrReference())
4021 	                  && !(newOperator == &AS_MULT
4022 	                       && (previousNonWSChar == '.'
4023 	                           || previousNonWSChar == '>'))    // check for ->
4024 	                  && !(newOperator == &AS_MULT && peekNextChar() == '>')
4025 	                  && !((isInTemplate || isImmediatelyPostTemplate)
4026 	                       && (newOperator == &AS_LS || newOperator == &AS_GR))
4027 	                  && !(newOperator == &AS_GCC_MIN_ASSIGN
4028 	                       && ASBase::peekNextChar(currentLine, charNum + 1) == '>')
4029 	                  && !(newOperator == &AS_GR && previousNonWSChar == '?')
4030 	                  && !(newOperator == &AS_QUESTION			// check for Java wildcard
4031 	                       && isJavaStyle()
4032 	                       && (previousNonWSChar == '<'
4033 	                           || nextNonWSChar == '>'
4034 	                           || nextNonWSChar == '.'))
4035 	                  && !(newOperator == &AS_QUESTION			// check for C# null conditional operator
4036 	                       && isSharpStyle()
4037 	                       && (nextNonWSChar == '.'
4038 	                           || nextNonWSChar == '['))
4039 	                  && !isCharImmediatelyPostOperator
4040 	                  && !isInCase
4041 	                  && !isInAsm
4042 	                  && !isInAsmOneLine
4043 	                  && !isInAsmBlock
4044 	                 );
4045 
4046 	// pad before operator
4047 	if (shouldPad
4048 	        && !(newOperator == &AS_COLON
4049 	             && (!foundQuestionMark && !isInEnum) && currentHeader != &AS_FOR)
4050 	        && !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
4051 	             && currentLine.find(':', charNum + 1) == string::npos)
4052 	   )
4053 		appendSpacePad();
4054 	appendOperator(*newOperator);
4055 	goForward(newOperator->length() - 1);
4056 
4057 	currentChar = (*newOperator)[newOperator->length() - 1];
4058 	// pad after operator
4059 	// but do not pad after a '-' that is a unary-minus.
4060 	if (shouldPad
4061 	        && !isBeforeAnyComment()
4062 	        && !(newOperator == &AS_PLUS && isUnaryOperator())
4063 	        && !(newOperator == &AS_MINUS && isUnaryOperator())
4064 	        && !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0)
4065 	        && !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0)
4066 	        && !(peekNextChar() == ',')
4067 	        && !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?)
4068 	             && peekNextChar() == '[')
4069 	   )
4070 		appendSpaceAfter();
4071 }
4072 
4073 /**
4074  * format pointer or reference
4075  * currentChar contains the pointer or reference
4076  * the symbol and necessary padding will be appended to formattedLine
4077  * the calling function should have a continue statement after calling this method
4078  *
4079  * NOTE: Do NOT use appendCurrentChar() in this method. The line should not be
4080  *       broken once the calculation starts.
4081  */
formatPointerOrReference()4082 void ASFormatter::formatPointerOrReference()
4083 {
4084 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
4085 	assert(!isJavaStyle());
4086 
4087 	int pa = pointerAlignment;
4088 	int ra = referenceAlignment;
4089 	int itemAlignment = (currentChar == '*' || currentChar == '^')
4090 	                    ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
4091 
4092 	// check for ** and &&
4093 	int ptrLength = 1;
4094 	char peekedChar = peekNextChar();
4095 	if ((currentChar == '*' && peekedChar == '*')
4096 	        || (currentChar == '&' && peekedChar == '&'))
4097 	{
4098 		ptrLength = 2;
4099 		size_t nextChar = currentLine.find_first_not_of(" \t", charNum + 2);
4100 		if (nextChar == string::npos)
4101 			peekedChar = ' ';
4102 		else
4103 			peekedChar = currentLine[nextChar];
4104 	}
4105 	// check for cast
4106 	if (peekedChar == ')' || peekedChar == '>' || peekedChar == ',')
4107 	{
4108 		formatPointerOrReferenceCast();
4109 		return;
4110 	}
4111 
4112 	// check for a padded space and remove it
4113 	if (charNum > 0
4114 	        && !isWhiteSpace(currentLine[charNum - 1])
4115 	        && formattedLine.length() > 0
4116 	        && isWhiteSpace(formattedLine[formattedLine.length() - 1]))
4117 	{
4118 		formattedLine.erase(formattedLine.length() - 1);
4119 		spacePadNum--;
4120 	}
4121 
4122 	if (itemAlignment == PTR_ALIGN_TYPE)
4123 	{
4124 		formatPointerOrReferenceToType();
4125 	}
4126 	else if (itemAlignment == PTR_ALIGN_MIDDLE)
4127 	{
4128 		formatPointerOrReferenceToMiddle();
4129 	}
4130 	else if (itemAlignment == PTR_ALIGN_NAME)
4131 	{
4132 		formatPointerOrReferenceToName();
4133 	}
4134 	else	// pointerAlignment == PTR_ALIGN_NONE
4135 	{
4136 		formattedLine.append(currentLine.substr(charNum, ptrLength));
4137 		if (ptrLength > 1)
4138 			goForward(ptrLength - 1);
4139 	}
4140 }
4141 
4142 /**
4143  * format pointer or reference with align to type
4144  */
formatPointerOrReferenceToType()4145 void ASFormatter::formatPointerOrReferenceToType()
4146 {
4147 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
4148 	assert(!isJavaStyle());
4149 
4150 	// do this before bumping charNum
4151 	bool isOldPRCentered = isPointerOrReferenceCentered();
4152 	string sequenceToInsert(1, currentChar);
4153 	// get the sequence
4154 	if (currentChar == peekNextChar())
4155 	{
4156 		for (size_t i = charNum + 1; currentLine.length() > i; i++)
4157 		{
4158 			if (currentLine[i] == sequenceToInsert[0])
4159 			{
4160 				sequenceToInsert.append(1, currentLine[i]);
4161 				goForward(1);
4162 				continue;
4163 			}
4164 			break;
4165 		}
4166 	}
4167 	// append the seqence
4168 	string charSave;
4169 	size_t prevCh = formattedLine.find_last_not_of(" \t");
4170 	if (prevCh < formattedLine.length())
4171 	{
4172 		charSave = formattedLine.substr(prevCh + 1);
4173 		formattedLine.resize(prevCh + 1);
4174 	}
4175 	formattedLine.append(sequenceToInsert);
4176 	if (peekNextChar() != ')')
4177 		formattedLine.append(charSave);
4178 	else
4179 		spacePadNum -= charSave.length();
4180 	// if no space after then add one
4181 	if (charNum < (int) currentLine.length() - 1
4182 	        && !isWhiteSpace(currentLine[charNum + 1])
4183 	        && currentLine[charNum + 1] != ')')
4184 		appendSpacePad();
4185 	// if old pointer or reference is centered, remove a space
4186 	if (isOldPRCentered
4187 	        && isWhiteSpace(formattedLine[formattedLine.length() - 1]))
4188 	{
4189 		formattedLine.erase(formattedLine.length() - 1, 1);
4190 		spacePadNum--;
4191 	}
4192 	// update the formattedLine split point
4193 	if (maxCodeLength != string::npos && formattedLine.length() > 0)
4194 	{
4195 		size_t index = formattedLine.length() - 1;
4196 		if (isWhiteSpace(formattedLine[index]))
4197 		{
4198 			updateFormattedLineSplitPointsPointerOrReference(index);
4199 			testForTimeToSplitFormattedLine();
4200 		}
4201 	}
4202 }
4203 
4204 /**
4205  * format pointer or reference with align in the middle
4206  */
formatPointerOrReferenceToMiddle()4207 void ASFormatter::formatPointerOrReferenceToMiddle()
4208 {
4209 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
4210 	assert(!isJavaStyle());
4211 
4212 	// compute current whitespace before
4213 	size_t wsBefore = currentLine.find_last_not_of(" \t", charNum - 1);
4214 	if (wsBefore == string::npos)
4215 		wsBefore = 0;
4216 	else
4217 		wsBefore = charNum - wsBefore - 1;
4218 	string sequenceToInsert(1, currentChar);
4219 	if (currentChar == peekNextChar())
4220 	{
4221 		for (size_t i = charNum + 1; currentLine.length() > i; i++)
4222 		{
4223 			if (currentLine[i] == sequenceToInsert[0])
4224 			{
4225 				sequenceToInsert.append(1, currentLine[i]);
4226 				goForward(1);
4227 				continue;
4228 			}
4229 			break;
4230 		}
4231 	}
4232 	// if reference to a pointer check for conflicting alignment
4233 	else if (currentChar == '*' && peekNextChar() == '&'
4234 	         && (referenceAlignment == REF_ALIGN_TYPE
4235 	             || referenceAlignment == REF_ALIGN_MIDDLE
4236 	             || referenceAlignment == REF_SAME_AS_PTR))
4237 	{
4238 		sequenceToInsert = "*&";
4239 		goForward(1);
4240 		for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
4241 			goForward(1);
4242 	}
4243 	// if a comment follows don't align, just space pad
4244 	if (isBeforeAnyComment())
4245 	{
4246 		appendSpacePad();
4247 		formattedLine.append(sequenceToInsert);
4248 		appendSpaceAfter();
4249 		return;
4250 	}
4251 	// do this before goForward()
4252 	bool isAfterScopeResolution = previousNonWSChar == ':';
4253 	size_t charNumSave = charNum;
4254 	// if this is the last thing on the line
4255 	if (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos)
4256 	{
4257 		if (wsBefore == 0 && !isAfterScopeResolution)
4258 			formattedLine.append(1, ' ');
4259 		formattedLine.append(sequenceToInsert);
4260 		return;
4261 	}
4262 	// goForward() to convert tabs to spaces, if necessary,
4263 	// and move following characters to preceding characters
4264 	// this may not work every time with tab characters
4265 	for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
4266 	{
4267 		goForward(1);
4268 		if (formattedLine.length() > 0)
4269 			formattedLine.append(1, currentLine[i]);
4270 		else
4271 			spacePadNum--;
4272 	}
4273 	// find space padding after
4274 	size_t wsAfter = currentLine.find_first_not_of(" \t", charNumSave + 1);
4275 	if (wsAfter == string::npos || isBeforeAnyComment())
4276 		wsAfter = 0;
4277 	else
4278 		wsAfter = wsAfter - charNumSave - 1;
4279 	// don't pad before scope resolution operator, but pad after
4280 	if (isAfterScopeResolution)
4281 	{
4282 		size_t lastText = formattedLine.find_last_not_of(" \t");
4283 		formattedLine.insert(lastText + 1, sequenceToInsert);
4284 		appendSpacePad();
4285 	}
4286 	else if (formattedLine.length() > 0)
4287 	{
4288 		// whitespace should be at least 2 chars to center
4289 		if (wsBefore + wsAfter < 2)
4290 		{
4291 			size_t charsToAppend = (2 - (wsBefore + wsAfter));
4292 			formattedLine.append(charsToAppend, ' ');
4293 			spacePadNum += charsToAppend;
4294 			if (wsBefore == 0)
4295 				wsBefore++;
4296 			if (wsAfter == 0)
4297 				wsAfter++;
4298 		}
4299 		// insert the pointer or reference char
4300 		size_t padAfter = (wsBefore + wsAfter) / 2;
4301 		size_t index = formattedLine.length() - padAfter;
4302 		if (index < formattedLine.length())
4303 			formattedLine.insert(index, sequenceToInsert);
4304 		else
4305 			formattedLine.append(sequenceToInsert);
4306 	}
4307 	else	// formattedLine.length() == 0
4308 	{
4309 		formattedLine.append(sequenceToInsert);
4310 		if (wsAfter == 0)
4311 			wsAfter++;
4312 		formattedLine.append(wsAfter, ' ');
4313 		spacePadNum += wsAfter;
4314 	}
4315 	// update the formattedLine split point after the pointer
4316 	if (maxCodeLength != string::npos && formattedLine.length() > 0)
4317 	{
4318 		size_t index = formattedLine.find_last_not_of(" \t");
4319 		if (index != string::npos && (index < formattedLine.length() - 1))
4320 		{
4321 			index++;
4322 			updateFormattedLineSplitPointsPointerOrReference(index);
4323 			testForTimeToSplitFormattedLine();
4324 		}
4325 	}
4326 }
4327 
4328 /**
4329  * format pointer or reference with align to name
4330  */
formatPointerOrReferenceToName()4331 void ASFormatter::formatPointerOrReferenceToName()
4332 {
4333 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
4334 	assert(!isJavaStyle());
4335 
4336 	// do this before bumping charNum
4337 	bool isOldPRCentered = isPointerOrReferenceCentered();
4338 
4339 	size_t startNum = formattedLine.find_last_not_of(" \t");
4340 	if (startNum == string::npos)
4341 		startNum = 0;
4342 	string sequenceToInsert(1, currentChar);
4343 	if (currentChar == peekNextChar())
4344 	{
4345 		for (size_t i = charNum + 1; currentLine.length() > i; i++)
4346 		{
4347 			if (currentLine[i] == sequenceToInsert[0])
4348 			{
4349 				sequenceToInsert.append(1, currentLine[i]);
4350 				goForward(1);
4351 				continue;
4352 			}
4353 			break;
4354 		}
4355 	}
4356 	// if reference to a pointer align both to name
4357 	else if (currentChar == '*' && peekNextChar() == '&')
4358 	{
4359 		sequenceToInsert = "*&";
4360 		goForward(1);
4361 		for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++)
4362 			goForward(1);
4363 	}
4364 	char peekedChar = peekNextChar();
4365 	bool isAfterScopeResolution = previousNonWSChar == ':';		// check for ::
4366 	// if this is not the last thing on the line
4367 	if ((isLegalNameChar(peekedChar) || peekedChar == '(' || peekedChar == '[' || peekedChar == '=')
4368 	        && (int) currentLine.find_first_not_of(" \t", charNum + 1) > charNum)
4369 	{
4370 		// goForward() to convert tabs to spaces, if necessary,
4371 		// and move following characters to preceding characters
4372 		// this may not work every time with tab characters
4373 		for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
4374 		{
4375 			// if a padded paren follows don't move
4376 			if (shouldPadParensOutside && peekedChar == '(' && !isOldPRCentered)
4377 			{
4378 				// empty parens don't count
4379 				size_t start = currentLine.find_first_not_of("( \t", i);
4380 				if (start != string::npos && currentLine[start] != ')')
4381 					break;
4382 			}
4383 			goForward(1);
4384 			if (formattedLine.length() > 0)
4385 				formattedLine.append(1, currentLine[charNum]);
4386 			else
4387 				spacePadNum--;
4388 		}
4389 	}
4390 	// don't pad before scope resolution operator
4391 	if (isAfterScopeResolution)
4392 	{
4393 		size_t lastText = formattedLine.find_last_not_of(" \t");
4394 		if (lastText != string::npos && lastText + 1 < formattedLine.length())
4395 			formattedLine.erase(lastText + 1);
4396 	}
4397 	// if no space before * then add one
4398 	else if (formattedLine.length() > 0
4399 	         && (formattedLine.length() <= startNum + 1
4400 	             || !isWhiteSpace(formattedLine[startNum + 1])))
4401 	{
4402 		formattedLine.insert(startNum + 1, 1, ' ');
4403 		spacePadNum++;
4404 	}
4405 	appendSequence(sequenceToInsert, false);
4406 	// if old pointer or reference is centered, remove a space
4407 	if (isOldPRCentered
4408 	        && formattedLine.length() > startNum + 1
4409 	        && isWhiteSpace(formattedLine[startNum + 1])
4410 	        && peekedChar != '*'		// check for '* *'
4411 	        && !isBeforeAnyComment())
4412 	{
4413 		formattedLine.erase(startNum + 1, 1);
4414 		spacePadNum--;
4415 	}
4416 	// don't convert to *= or &=
4417 	if (peekedChar == '=')
4418 	{
4419 		appendSpaceAfter();
4420 		// if more than one space before, delete one
4421 		if (formattedLine.length() > startNum
4422 		        && isWhiteSpace(formattedLine[startNum + 1])
4423 		        && isWhiteSpace(formattedLine[startNum + 2]))
4424 		{
4425 			formattedLine.erase(startNum + 1, 1);
4426 			spacePadNum--;
4427 		}
4428 	}
4429 	// update the formattedLine split point
4430 	if (maxCodeLength != string::npos)
4431 	{
4432 		size_t index = formattedLine.find_last_of(" \t");
4433 		if (index != string::npos
4434 		        && index < formattedLine.length() - 1
4435 		        && (formattedLine[index + 1] == '*'
4436 		            || formattedLine[index + 1] == '&'
4437 		            || formattedLine[index + 1] == '^'))
4438 		{
4439 			updateFormattedLineSplitPointsPointerOrReference(index);
4440 			testForTimeToSplitFormattedLine();
4441 		}
4442 	}
4443 }
4444 
4445 /**
4446  * format pointer or reference cast
4447  * currentChar contains the pointer or reference
4448  * NOTE: the pointers and references in function definitions
4449  *       are processed as a cast (e.g. void foo(void*, void*))
4450  *       is processed here.
4451  */
formatPointerOrReferenceCast()4452 void ASFormatter::formatPointerOrReferenceCast()
4453 {
4454 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
4455 	assert(!isJavaStyle());
4456 
4457 	int pa = pointerAlignment;
4458 	int ra = referenceAlignment;
4459 	int itemAlignment = (currentChar == '*' || currentChar == '^')
4460 	                    ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra);
4461 
4462 	string sequenceToInsert(1, currentChar);
4463 	if (isSequenceReached("**") || isSequenceReached("&&"))
4464 	{
4465 		goForward(1);
4466 		sequenceToInsert.append(1, currentLine[charNum]);
4467 	}
4468 	if (itemAlignment == PTR_ALIGN_NONE)
4469 	{
4470 		appendSequence(sequenceToInsert, false);
4471 		return;
4472 	}
4473 	// remove preceding whitespace
4474 	char prevCh = ' ';
4475 	size_t prevNum = formattedLine.find_last_not_of(" \t");
4476 	if (prevNum != string::npos)
4477 	{
4478 		prevCh = formattedLine[prevNum];
4479 		if (itemAlignment == PTR_ALIGN_TYPE && currentChar == '*' && prevCh == '*')
4480 		{
4481 			// '* *' may be a multiply followed by a dereference
4482 			if (prevNum + 2 < formattedLine.length()
4483 			        && isWhiteSpace(formattedLine[prevNum + 2]))
4484 			{
4485 				spacePadNum -= (formattedLine.length() - 2 - prevNum);
4486 				formattedLine.erase(prevNum + 2);
4487 			}
4488 		}
4489 		else if (prevNum + 1 < formattedLine.length()
4490 		         && isWhiteSpace(formattedLine[prevNum + 1])
4491 		         && prevCh != '(')
4492 		{
4493 			spacePadNum -= (formattedLine.length() - 1 - prevNum);
4494 			formattedLine.erase(prevNum + 1);
4495 		}
4496 	}
4497 	bool isAfterScopeResolution = previousNonWSChar == ':';
4498 	if ((itemAlignment == PTR_ALIGN_MIDDLE || itemAlignment == PTR_ALIGN_NAME)
4499 	        && !isAfterScopeResolution && prevCh != '(')
4500 	{
4501 		appendSpacePad();
4502 		// in this case appendSpacePad may or may not update the split point
4503 		if (maxCodeLength != string::npos && formattedLine.length() > 0)
4504 			updateFormattedLineSplitPointsPointerOrReference(formattedLine.length() - 1);
4505 		appendSequence(sequenceToInsert, false);
4506 	}
4507 	else
4508 		appendSequence(sequenceToInsert, false);
4509 }
4510 
4511 /**
4512  * add or remove space padding to parens
4513  * currentChar contains the paren
4514  * the parens and necessary padding will be appended to formattedLine
4515  * the calling function should have a continue statement after calling this method
4516  */
padParens()4517 void ASFormatter::padParens()
4518 {
4519 	assert(currentChar == '(' || currentChar == ')');
4520 	assert(shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen);
4521 
4522 	int spacesOutsideToDelete = 0;
4523 	int spacesInsideToDelete = 0;
4524 
4525 	if (currentChar == '(')
4526 	{
4527 		spacesOutsideToDelete = formattedLine.length() - 1;
4528 		spacesInsideToDelete = 0;
4529 
4530 		// compute spaces outside the opening paren to delete
4531 		if (shouldUnPadParens)
4532 		{
4533 			char lastChar = ' ';
4534 			bool prevIsParenHeader = false;
4535 			size_t i = formattedLine.find_last_not_of(" \t");
4536 			if (i != string::npos)
4537 			{
4538 				// if last char is a brace the previous whitespace is an indent
4539 				if (formattedLine[i] == '{')
4540 					spacesOutsideToDelete = 0;
4541 				else if (isCharImmediatelyPostPointerOrReference)
4542 					spacesOutsideToDelete = 0;
4543 				else
4544 				{
4545 					spacesOutsideToDelete -= i;
4546 					lastChar = formattedLine[i];
4547 					// if previous word is a header, it will be a paren header
4548 					string prevWord = getPreviousWord(formattedLine, formattedLine.length());
4549 					const string* prevWordH = nullptr;
4550 					if (shouldPadHeader
4551 					        && prevWord.length() > 0
4552 					        && isCharPotentialHeader(prevWord, 0))
4553 						prevWordH = ASBase::findHeader(prevWord, 0, headers);
4554 					if (prevWordH != nullptr)
4555 						prevIsParenHeader = true;    // don't unpad
4556 					else if (prevWord == AS_RETURN)
4557 						prevIsParenHeader = true;    // don't unpad
4558 					else if ((prevWord == AS_NEW || prevWord == AS_DELETE)
4559 					         && shouldPadHeader)
4560 						prevIsParenHeader = true;    // don't unpad
4561 					else if (isCStyle() && prevWord == AS_THROW && shouldPadHeader)
4562 						prevIsParenHeader = true;    // don't unpad
4563 					else if (prevWord == "and" || prevWord == "or" || prevWord == "in")
4564 						prevIsParenHeader = true;    // don't unpad
4565 					// don't unpad variables
4566 					else if (isNumericVariable(prevWord))
4567 						prevIsParenHeader = true;    // don't unpad
4568 				}
4569 			}
4570 			// do not unpad operators, but leave them if already padded
4571 			if (shouldPadParensOutside || prevIsParenHeader)
4572 				spacesOutsideToDelete--;
4573 			else if (lastChar == '|'          // check for ||
4574 			         || lastChar == '&'       // check for &&
4575 			         || lastChar == ','
4576 			         || (lastChar == '(' && shouldPadParensInside)
4577 			         || (lastChar == '>' && !foundCastOperator)
4578 			         || lastChar == '<'
4579 			         || lastChar == '?'
4580 			         || lastChar == ':'
4581 			         || lastChar == ';'
4582 			         || lastChar == '='
4583 			         || lastChar == '+'
4584 			         || lastChar == '-'
4585 			         || lastChar == '*'
4586 			         || lastChar == '/'
4587 			         || lastChar == '%'
4588 			         || lastChar == '^'
4589 			        )
4590 				spacesOutsideToDelete--;
4591 
4592 			if (spacesOutsideToDelete > 0)
4593 			{
4594 				formattedLine.erase(i + 1, spacesOutsideToDelete);
4595 				spacePadNum -= spacesOutsideToDelete;
4596 			}
4597 		}
4598 
4599 		// pad open paren outside
4600 		char peekedCharOutside = peekNextChar();
4601 		if (shouldPadFirstParen && previousChar != '(' && peekedCharOutside != ')')
4602 			appendSpacePad();
4603 		else if (shouldPadParensOutside)
4604 		{
4605 			if (!(currentChar == '(' && peekedCharOutside == ')'))
4606 				appendSpacePad();
4607 		}
4608 
4609 		appendCurrentChar();
4610 
4611 		// unpad open paren inside
4612 		if (shouldUnPadParens)
4613 		{
4614 			size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
4615 			if (j != string::npos)
4616 				spacesInsideToDelete = j - charNum - 1;
4617 			if (shouldPadParensInside)
4618 				spacesInsideToDelete--;
4619 			if (spacesInsideToDelete > 0)
4620 			{
4621 				currentLine.erase(charNum + 1, spacesInsideToDelete);
4622 				spacePadNum -= spacesInsideToDelete;
4623 			}
4624 			// convert tab to space if requested
4625 			if (shouldConvertTabs
4626 			        && (int) currentLine.length() > charNum + 1
4627 			        && currentLine[charNum + 1] == '\t')
4628 				currentLine[charNum + 1] = ' ';
4629 		}
4630 
4631 		// pad open paren inside
4632 		char peekedCharInside = peekNextChar();
4633 		if (shouldPadParensInside)
4634 			if (!(currentChar == '(' && peekedCharInside == ')'))
4635 				appendSpaceAfter();
4636 	}
4637 	else if (currentChar == ')')
4638 	{
4639 		// unpad close paren inside
4640 		if (shouldUnPadParens)
4641 		{
4642 			spacesInsideToDelete = formattedLine.length();
4643 			size_t i = formattedLine.find_last_not_of(" \t");
4644 			if (i != string::npos)
4645 				spacesInsideToDelete = formattedLine.length() - 1 - i;
4646 			if (shouldPadParensInside)
4647 				spacesInsideToDelete--;
4648 			if (spacesInsideToDelete > 0)
4649 			{
4650 				formattedLine.erase(i + 1, spacesInsideToDelete);
4651 				spacePadNum -= spacesInsideToDelete;
4652 			}
4653 		}
4654 
4655 		// pad close paren inside
4656 		if (shouldPadParensInside)
4657 			if (!(previousChar == '(' && currentChar == ')'))
4658 				appendSpacePad();
4659 
4660 		appendCurrentChar();
4661 
4662 		// unpad close paren outside
4663 		// close parens outside are left unchanged
4664 		if (shouldUnPadParens)
4665 		{
4666 			//spacesOutsideToDelete = 0;
4667 			//size_t j = currentLine.find_first_not_of(" \t", charNum + 1);
4668 			//if (j != string::npos)
4669 			//	spacesOutsideToDelete = j - charNum - 1;
4670 			//if (shouldPadParensOutside)
4671 			//	spacesOutsideToDelete--;
4672 
4673 			//if (spacesOutsideToDelete > 0)
4674 			//{
4675 			//	currentLine.erase(charNum + 1, spacesOutsideToDelete);
4676 			//	spacePadNum -= spacesOutsideToDelete;
4677 			//}
4678 		}
4679 
4680 		// pad close paren outside
4681 		char peekedCharOutside = peekNextChar();
4682 		if (shouldPadParensOutside)
4683 			if (peekedCharOutside != ';'
4684 			        && peekedCharOutside != ','
4685 			        && peekedCharOutside != '.'
4686 			        && peekedCharOutside != '+'    // check for ++
4687 			        && peekedCharOutside != '-'    // check for --
4688 			        && peekedCharOutside != ']')
4689 				appendSpaceAfter();
4690 	}
4691 }
4692 
4693 /**
4694 * add or remove space padding to objective-c method prefix (- or +)
4695 * if this is a '(' it begins a return type
4696 * these options have precedence over the padParens methods
4697 * the padParens method has already been called, this method adjusts
4698 */
padObjCMethodPrefix()4699 void ASFormatter::padObjCMethodPrefix()
4700 {
4701 	assert(isInObjCMethodDefinition && isImmediatelyPostObjCMethodPrefix);
4702 	assert(shouldPadMethodPrefix || shouldUnPadMethodPrefix);
4703 
4704 	size_t prefix = formattedLine.find_first_of("+-");
4705 	if (prefix == string::npos)
4706 		return;
4707 	size_t firstChar = formattedLine.find_first_not_of(" \t", prefix + 1);
4708 	if (firstChar == string::npos)
4709 		firstChar = formattedLine.length();
4710 	int spaces = firstChar - prefix - 1;
4711 
4712 	if (shouldPadMethodPrefix)
4713 	{
4714 		if (spaces == 0)
4715 		{
4716 			formattedLine.insert(prefix + 1, 1, ' ');
4717 			spacePadNum += 1;
4718 		}
4719 		else if (spaces > 1)
4720 		{
4721 			formattedLine.erase(prefix + 1, spaces - 1);
4722 			formattedLine[prefix + 1] = ' ';  // convert any tab to space
4723 			spacePadNum -= spaces - 1;
4724 		}
4725 	}
4726 	// this option will be ignored if used with pad-method-prefix
4727 	else if (shouldUnPadMethodPrefix)
4728 	{
4729 		if (spaces > 0)
4730 		{
4731 			formattedLine.erase(prefix + 1, spaces);
4732 			spacePadNum -= spaces;
4733 		}
4734 	}
4735 }
4736 
4737 /**
4738 * add or remove space padding to objective-c parens
4739 * these options have precedence over the padParens methods
4740 * the padParens method has already been called, this method adjusts
4741 */
padObjCReturnType()4742 void ASFormatter::padObjCReturnType()
4743 {
4744 	assert(currentChar == ')' && isInObjCReturnType);
4745 	assert(shouldPadReturnType || shouldUnPadReturnType);
4746 
4747 	size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
4748 	if (nextText == string::npos)
4749 		return;
4750 	int spaces = nextText - charNum - 1;
4751 
4752 	if (shouldPadReturnType)
4753 	{
4754 		if (spaces == 0)
4755 		{
4756 			// this will already be padded if pad-paren is used
4757 			if (formattedLine[formattedLine.length() - 1] != ' ')
4758 			{
4759 				formattedLine.append(" ");
4760 				spacePadNum += 1;
4761 			}
4762 		}
4763 		else if (spaces > 1)
4764 		{
4765 			// do not use goForward here
4766 			currentLine.erase(charNum + 1, spaces - 1);
4767 			currentLine[charNum + 1] = ' ';  // convert any tab to space
4768 			spacePadNum -= spaces - 1;
4769 		}
4770 	}
4771 	// this option will be ignored if used with pad-return-type
4772 	else if (shouldUnPadReturnType)
4773 	{
4774 		// this will already be padded if pad-paren is used
4775 		if (formattedLine[formattedLine.length() - 1] == ' ')
4776 		{
4777 			int lastText = formattedLine.find_last_not_of(" \t");
4778 			spacePadNum -= formattedLine.length() - lastText - 1;
4779 			formattedLine.resize(lastText + 1);
4780 		}
4781 		// do not use goForward here
4782 		currentLine.erase(charNum + 1, spaces);
4783 		spacePadNum -= spaces;
4784 	}
4785 }
4786 
4787 /**
4788 * add or remove space padding to objective-c parens
4789 * these options have precedence over the padParens methods
4790 * the padParens method has already been called, this method adjusts
4791 */
padObjCParamType()4792 void ASFormatter::padObjCParamType()
4793 {
4794 	assert((currentChar == '(' || currentChar == ')') && isInObjCMethodDefinition);
4795 	assert(!isImmediatelyPostObjCMethodPrefix && !isInObjCReturnType);
4796 	assert(shouldPadParamType || shouldUnPadParamType);
4797 
4798 	if (currentChar == '(')
4799 	{
4800 		// open paren has already been attached to formattedLine by padParen
4801 		size_t paramOpen = formattedLine.rfind('(');
4802 		assert(paramOpen != string::npos);
4803 		size_t prevText = formattedLine.find_last_not_of(" \t", paramOpen - 1);
4804 		if (prevText == string::npos)
4805 			return;
4806 		int spaces = paramOpen - prevText - 1;
4807 
4808 		if (shouldPadParamType
4809 		        || objCColonPadMode == COLON_PAD_ALL
4810 		        || objCColonPadMode == COLON_PAD_AFTER)
4811 		{
4812 			if (spaces == 0)
4813 			{
4814 				formattedLine.insert(paramOpen, 1, ' ');
4815 				spacePadNum += 1;
4816 			}
4817 			if (spaces > 1)
4818 			{
4819 				formattedLine.erase(prevText + 1, spaces - 1);
4820 				formattedLine[prevText + 1] = ' ';  // convert any tab to space
4821 				spacePadNum -= spaces - 1;
4822 			}
4823 		}
4824 		// this option will be ignored if used with pad-param-type
4825 		else if (shouldUnPadParamType
4826 		         || objCColonPadMode == COLON_PAD_NONE
4827 		         || objCColonPadMode == COLON_PAD_BEFORE)
4828 		{
4829 			if (spaces > 0)
4830 			{
4831 				formattedLine.erase(prevText + 1, spaces);
4832 				spacePadNum -= spaces;
4833 			}
4834 		}
4835 	}
4836 	else if (currentChar == ')')
4837 	{
4838 		size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
4839 		if (nextText == string::npos)
4840 			return;
4841 		int spaces = nextText - charNum - 1;
4842 
4843 		if (shouldPadParamType)
4844 		{
4845 			if (spaces == 0)
4846 			{
4847 				// this will already be padded if pad-paren is used
4848 				if (formattedLine[formattedLine.length() - 1] != ' ')
4849 				{
4850 					formattedLine.append(" ");
4851 					spacePadNum += 1;
4852 				}
4853 			}
4854 			else if (spaces > 1)
4855 			{
4856 				// do not use goForward here
4857 				currentLine.erase(charNum + 1, spaces - 1);
4858 				currentLine[charNum + 1] = ' ';  // convert any tab to space
4859 				spacePadNum -= spaces - 1;
4860 			}
4861 		}
4862 		// this option will be ignored if used with pad-param-type
4863 		else if (shouldUnPadParamType)
4864 		{
4865 			// this will already be padded if pad-paren is used
4866 			if (formattedLine[formattedLine.length() - 1] == ' ')
4867 			{
4868 				spacePadNum -= 1;
4869 				int lastText = formattedLine.find_last_not_of(" \t");
4870 				formattedLine.resize(lastText + 1);
4871 			}
4872 			if (spaces > 0)
4873 			{
4874 				// do not use goForward here
4875 				currentLine.erase(charNum + 1, spaces);
4876 				spacePadNum -= spaces;
4877 			}
4878 		}
4879 	}
4880 }
4881 
4882 /**
4883  * format opening brace as attached or broken
4884  * currentChar contains the brace
4885  * the braces will be appended to the current formattedLine or a new formattedLine as necessary
4886  * the calling function should have a continue statement after calling this method
4887  *
4888  * @param braceType    the type of brace to be formatted.
4889  */
formatOpeningBrace(BraceType braceType)4890 void ASFormatter::formatOpeningBrace(BraceType braceType)
4891 {
4892 	assert(!isBraceType(braceType, ARRAY_TYPE));
4893 	assert(currentChar == '{');
4894 
4895 	parenStack->emplace_back(0);
4896 
4897 	bool breakBrace = isCurrentBraceBroken();
4898 
4899 	if (breakBrace)
4900 	{
4901 		if (isBeforeAnyComment() && isOkToBreakBlock(braceType) && sourceIterator->hasMoreLines())
4902 		{
4903 			// if comment is at line end leave the comment on this line
4904 			if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
4905 			{
4906 				currentChar = ' ';              // remove brace from current line
4907 				if (parenStack->size() > 1)
4908 					parenStack->pop_back();
4909 				currentLine[charNum] = currentChar;
4910 				appendOpeningBrace = true;      // append brace to following line
4911 			}
4912 			// else put comment after the brace
4913 			else if (!isBeforeMultipleLineEndComments(charNum))
4914 				breakLine();
4915 		}
4916 		else if (!isBraceType(braceType, SINGLE_LINE_TYPE))
4917 		{
4918 			formattedLine = rtrim(formattedLine);
4919 			breakLine();
4920 		}
4921 		else if ((shouldBreakOneLineBlocks || isBraceType(braceType, BREAK_BLOCK_TYPE))
4922 		         && !isBraceType(braceType, EMPTY_BLOCK_TYPE))
4923 			breakLine();
4924 		else if (!isInLineBreak)
4925 			appendSpacePad();
4926 
4927 		appendCurrentChar();
4928 
4929 		// should a following comment break from the brace?
4930 		// must break the line AFTER the brace
4931 		if (isBeforeComment()
4932 		        && formattedLine.length() > 0
4933 		        && formattedLine[0] == '{'
4934 		        && isOkToBreakBlock(braceType)
4935 		        && (braceFormatMode == BREAK_MODE
4936 		            || braceFormatMode == LINUX_MODE))
4937 		{
4938 			shouldBreakLineAtNextChar = true;
4939 		}
4940 	}
4941 	else    // attach brace
4942 	{
4943 		// are there comments before the brace?
4944 		if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment)
4945 		{
4946 			if (isOkToBreakBlock(braceType)
4947 			        && !(isCharImmediatelyPostComment && isCharImmediatelyPostLineComment)	// don't attach if two comments on the line
4948 			        && !isImmediatelyPostPreprocessor
4949 //			        && peekNextChar() != '}'		// don't attach { }		// removed release 2.03
4950 			        && previousCommandChar != '{'	// don't attach { {
4951 			        && previousCommandChar != '}'	// don't attach } {
4952 			        && previousCommandChar != ';')	// don't attach ; {
4953 			{
4954 				appendCharInsideComments();
4955 			}
4956 			else
4957 			{
4958 				appendCurrentChar();				// don't attach
4959 			}
4960 		}
4961 		else if (previousCommandChar == '{'
4962 		         || (previousCommandChar == '}' && !isInClassInitializer)
4963 		         || previousCommandChar == ';')		// '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';'
4964 		{
4965 			appendCurrentChar();					// don't attach
4966 		}
4967 		else
4968 		{
4969 			// if a blank line precedes this don't attach
4970 			if (isEmptyLine(formattedLine))
4971 				appendCurrentChar();				// don't attach
4972 			else if (isOkToBreakBlock(braceType)
4973 			         && !(isImmediatelyPostPreprocessor
4974 			              && currentLineBeginsWithBrace))
4975 			{
4976 				if (!isBraceType(braceType, EMPTY_BLOCK_TYPE))
4977 				{
4978 					appendSpacePad();
4979 					appendCurrentChar(false);				// OK to attach
4980 					testForTimeToSplitFormattedLine();		// line length will have changed
4981 					// should a following comment attach with the brace?
4982 					// insert spaces to reposition the comment
4983 					if (isBeforeComment()
4984 					        && !isBeforeMultipleLineEndComments(charNum)
4985 					        && (!isBeforeAnyLineEndComment(charNum)	|| currentLineBeginsWithBrace))
4986 					{
4987 						shouldBreakLineAtNextChar = true;
4988 						currentLine.insert(charNum + 1, charNum + 1, ' ');
4989 					}
4990 					else if (!isBeforeAnyComment())		// added in release 2.03
4991 					{
4992 						shouldBreakLineAtNextChar = true;
4993 					}
4994 				}
4995 				else
4996 				{
4997 					if (currentLineBeginsWithBrace && (size_t) charNum == currentLineFirstBraceNum)
4998 					{
4999 						appendSpacePad();
5000 						appendCurrentChar(false);		// attach
5001 						shouldBreakLineAtNextChar = true;
5002 					}
5003 					else
5004 					{
5005 						appendSpacePad();
5006 						appendCurrentChar();		// don't attach
5007 					}
5008 				}
5009 			}
5010 			else
5011 			{
5012 				if (!isInLineBreak)
5013 					appendSpacePad();
5014 				appendCurrentChar();				// don't attach
5015 			}
5016 		}
5017 	}
5018 }
5019 
5020 /**
5021  * format closing brace
5022  * currentChar contains the brace
5023  * the calling function should have a continue statement after calling this method
5024  *
5025  * @param braceType    the type of the opening brace for this closing brace.
5026  */
formatClosingBrace(BraceType braceType)5027 void ASFormatter::formatClosingBrace(BraceType braceType)
5028 {
5029 	assert(!isBraceType(braceType, ARRAY_TYPE));
5030 	assert(currentChar == '}');
5031 
5032 	// parenStack must contain one entry
5033 	if (parenStack->size() > 1)
5034 		parenStack->pop_back();
5035 
5036 	// mark state of immediately after empty block
5037 	// this state will be used for locating braces that appear immediately AFTER an empty block (e.g. '{} \n}').
5038 	if (previousCommandChar == '{')
5039 		isImmediatelyPostEmptyBlock = true;
5040 
5041 	if (attachClosingBraceMode)
5042 	{
5043 		// for now, namespaces and classes will be attached. Uncomment the lines below to break.
5044 		if ((isEmptyLine(formattedLine)			// if a blank line precedes this
5045 		        || isCharImmediatelyPostLineComment
5046 		        || isCharImmediatelyPostComment
5047 		        || (isImmediatelyPostPreprocessor && (int) currentLine.find_first_not_of(" \t") == charNum)
5048 //		        || (isBraceType(braceType, CLASS_TYPE) && isOkToBreakBlock(braceType) && previousNonWSChar != '{')
5049 //		        || (isBraceType(braceType, NAMESPACE_TYPE) && isOkToBreakBlock(braceType) && previousNonWSChar != '{')
5050 		    )
5051 		        && (!isBraceType(braceType, SINGLE_LINE_TYPE) || isOkToBreakBlock(braceType)))
5052 		{
5053 			breakLine();
5054 			appendCurrentChar();				// don't attach
5055 		}
5056 		else
5057 		{
5058 			if (previousNonWSChar != '{'
5059 			        && (!isBraceType(braceType, SINGLE_LINE_TYPE)
5060 			            || isOkToBreakBlock(braceType)))
5061 				appendSpacePad();
5062 			appendCurrentChar(false);			// attach
5063 		}
5064 	}
5065 	else if (!isBraceType(braceType, EMPTY_BLOCK_TYPE)
5066 	         && (isBraceType(braceType, BREAK_BLOCK_TYPE)
5067 	             || isOkToBreakBlock(braceType)))
5068 	{
5069 		breakLine();
5070 		appendCurrentChar();
5071 	}
5072 	else
5073 	{
5074 		appendCurrentChar();
5075 	}
5076 
5077 	// if a declaration follows a definition, space pad
5078 	if (isLegalNameChar(peekNextChar()))
5079 		appendSpaceAfter();
5080 
5081 	if (shouldBreakBlocks
5082 	        && currentHeader != nullptr
5083 	        && !isHeaderInMultiStatementLine
5084 	        && parenStack->back() == 0)
5085 	{
5086 		if (currentHeader == &AS_CASE || currentHeader == &AS_DEFAULT)
5087 		{
5088 			// do not yet insert a line if "break" statement is outside the braces
5089 			string nextText = peekNextText(currentLine.substr(charNum + 1));
5090 			if (nextText.length() > 0
5091 			        && nextText.substr(0, 5) != "break")
5092 				isAppendPostBlockEmptyLineRequested = true;
5093 		}
5094 		else
5095 			isAppendPostBlockEmptyLineRequested = true;
5096 	}
5097 }
5098 
5099 /**
5100  * format array braces as attached or broken
5101  * determine if the braces can have an inStatement indent
5102  * currentChar contains the brace
5103  * the braces will be appended to the current formattedLine or a new formattedLine as necessary
5104  * the calling function should have a continue statement after calling this method
5105  *
5106  * @param braceType            the type of brace to be formatted, must be an ARRAY_TYPE.
5107  * @param isOpeningArrayBrace  indicates if this is the opening brace for the array block.
5108  */
formatArrayBraces(BraceType braceType,bool isOpeningArrayBrace)5109 void ASFormatter::formatArrayBraces(BraceType braceType, bool isOpeningArrayBrace)
5110 {
5111 	assert(isBraceType(braceType, ARRAY_TYPE));
5112 	assert(currentChar == '{' || currentChar == '}');
5113 
5114 	if (currentChar == '{')
5115 	{
5116 		// is this the first opening brace in the array?
5117 		if (isOpeningArrayBrace)
5118 		{
5119 			if (braceFormatMode == ATTACH_MODE
5120 			        || braceFormatMode == LINUX_MODE)
5121 			{
5122 				// break an enum if mozilla
5123 				if (isBraceType(braceType, ENUM_TYPE)
5124 				        && formattingStyle == STYLE_MOZILLA)
5125 				{
5126 					isInLineBreak = true;
5127 					appendCurrentChar();                // don't attach
5128 				}
5129 				// don't attach to a preprocessor directive or '\' line
5130 				else if ((isImmediatelyPostPreprocessor
5131 				          || (formattedLine.length() > 0
5132 				              && formattedLine[formattedLine.length() - 1] == '\\'))
5133 				         && currentLineBeginsWithBrace)
5134 				{
5135 					isInLineBreak = true;
5136 					appendCurrentChar();                // don't attach
5137 				}
5138 				else if (isCharImmediatelyPostComment)
5139 				{
5140 					// TODO: attach brace to line-end comment
5141 					appendCurrentChar();                // don't attach
5142 				}
5143 				else if (isCharImmediatelyPostLineComment && !isBraceType(braceType, SINGLE_LINE_TYPE))
5144 				{
5145 					appendCharInsideComments();
5146 				}
5147 				else
5148 				{
5149 					// if a blank line precedes this don't attach
5150 					if (isEmptyLine(formattedLine))
5151 						appendCurrentChar();            // don't attach
5152 					else
5153 					{
5154 						// if brace is broken or not an assignment
5155 						if (currentLineBeginsWithBrace
5156 						        && !isBraceType(braceType, SINGLE_LINE_TYPE))
5157 						{
5158 							appendSpacePad();
5159 							appendCurrentChar(false);				// OK to attach
5160 							// TODO: debug the following line
5161 							testForTimeToSplitFormattedLine();		// line length will have changed
5162 
5163 							if (currentLineBeginsWithBrace
5164 							        && currentLineFirstBraceNum == (size_t) charNum)
5165 								shouldBreakLineAtNextChar = true;
5166 						}
5167 						else
5168 						{
5169 							if (previousNonWSChar != '(')
5170 							{
5171 								// don't space pad C++11 uniform initialization
5172 								if (!isBraceType(braceType, INIT_TYPE))
5173 									appendSpacePad();
5174 							}
5175 							appendCurrentChar();
5176 						}
5177 					}
5178 				}
5179 			}
5180 			else if (braceFormatMode == BREAK_MODE)
5181 			{
5182 				if (isWhiteSpace(peekNextChar()) && !isInVirginLine)
5183 					breakLine();
5184 				else if (isBeforeAnyComment() && sourceIterator->hasMoreLines())
5185 				{
5186 					// do not break unless comment is at line end
5187 					if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
5188 					{
5189 						currentChar = ' ';            // remove brace from current line
5190 						appendOpeningBrace = true;    // append brace to following line
5191 					}
5192 				}
5193 				if (!isInLineBreak && previousNonWSChar != '(')
5194 				{
5195 					// don't space pad C++11 uniform initialization
5196 					if (!isBraceType(braceType, INIT_TYPE))
5197 						appendSpacePad();
5198 				}
5199 				appendCurrentChar();
5200 
5201 				if (currentLineBeginsWithBrace
5202 				        && currentLineFirstBraceNum == (size_t) charNum
5203 				        && !isBraceType(braceType, SINGLE_LINE_TYPE))
5204 					shouldBreakLineAtNextChar = true;
5205 			}
5206 			else if (braceFormatMode == RUN_IN_MODE)
5207 			{
5208 				if (isWhiteSpace(peekNextChar()) && !isInVirginLine)
5209 					breakLine();
5210 				else if (isBeforeAnyComment() && sourceIterator->hasMoreLines())
5211 				{
5212 					// do not break unless comment is at line end
5213 					if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBrace)
5214 					{
5215 						currentChar = ' ';            // remove brace from current line
5216 						appendOpeningBrace = true;    // append brace to following line
5217 					}
5218 				}
5219 				if (!isInLineBreak && previousNonWSChar != '(')
5220 				{
5221 					// don't space pad C++11 uniform initialization
5222 					if (!isBraceType(braceType, INIT_TYPE))
5223 						appendSpacePad();
5224 				}
5225 				appendCurrentChar();
5226 			}
5227 			else if (braceFormatMode == NONE_MODE)
5228 			{
5229 				if (currentLineBeginsWithBrace
5230 				        && (size_t) charNum == currentLineFirstBraceNum)
5231 				{
5232 					appendCurrentChar();                // don't attach
5233 				}
5234 				else
5235 				{
5236 					if (previousNonWSChar != '(')
5237 					{
5238 						// don't space pad C++11 uniform initialization
5239 						if (!isBraceType(braceType, INIT_TYPE))
5240 							appendSpacePad();
5241 					}
5242 					appendCurrentChar(false);           // OK to attach
5243 				}
5244 			}
5245 		}
5246 		else	     // not the first opening brace
5247 		{
5248 			if (braceFormatMode == RUN_IN_MODE)
5249 			{
5250 				if (previousNonWSChar == '{'
5251 				        && braceTypeStack->size() > 2
5252 				        && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2],
5253 				                        SINGLE_LINE_TYPE))
5254 					formatArrayRunIn();
5255 			}
5256 			else if (!isInLineBreak
5257 			         && !isWhiteSpace(peekNextChar())
5258 			         && previousNonWSChar == '{'
5259 			         && braceTypeStack->size() > 2
5260 			         && !isBraceType((*braceTypeStack)[braceTypeStack->size() - 2],
5261 			                         SINGLE_LINE_TYPE))
5262 				formatArrayRunIn();
5263 
5264 			appendCurrentChar();
5265 		}
5266 	}
5267 	else if (currentChar == '}')
5268 	{
5269 		if (attachClosingBraceMode)
5270 		{
5271 			if (isEmptyLine(formattedLine)			// if a blank line precedes this
5272 			        || isImmediatelyPostPreprocessor
5273 			        || isCharImmediatelyPostLineComment
5274 			        || isCharImmediatelyPostComment)
5275 				appendCurrentChar();				// don't attach
5276 			else
5277 			{
5278 				appendSpacePad();
5279 				appendCurrentChar(false);			// attach
5280 			}
5281 		}
5282 		else
5283 		{
5284 			// does this close the first opening brace in the array?
5285 			// must check if the block is still a single line because of anonymous statements
5286 			if (!isBraceType(braceType, INIT_TYPE)
5287 			        && (!isBraceType(braceType, SINGLE_LINE_TYPE)
5288 			            || formattedLine.find('{') == string::npos))
5289 				breakLine();
5290 			appendCurrentChar();
5291 		}
5292 
5293 		// if a declaration follows an enum definition, space pad
5294 		char peekedChar = peekNextChar();
5295 		if ((isLegalNameChar(peekedChar) && peekedChar != '.')
5296 		        || peekedChar == '[')
5297 			appendSpaceAfter();
5298 	}
5299 }
5300 
5301 /**
5302  * determine if a run-in can be attached.
5303  * if it can insert the indents in formattedLine and reset the current line break.
5304  */
formatRunIn()5305 void ASFormatter::formatRunIn()
5306 {
5307 	assert(braceFormatMode == RUN_IN_MODE || braceFormatMode == NONE_MODE);
5308 
5309 	// keep one line blocks returns true without indenting the run-in
5310 	if (formattingStyle != STYLE_PICO
5311 	        && !isOkToBreakBlock(braceTypeStack->back()))
5312 		return; // true;
5313 
5314 	// make sure the line begins with a brace
5315 	size_t lastText = formattedLine.find_last_not_of(" \t");
5316 	if (lastText == string::npos || formattedLine[lastText] != '{')
5317 		return; // false;
5318 
5319 	// make sure the brace is broken
5320 	if (formattedLine.find_first_not_of(" \t{") != string::npos)
5321 		return; // false;
5322 
5323 	if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
5324 		return; // false;
5325 
5326 	bool extraIndent = false;
5327 	bool extraHalfIndent = false;
5328 	isInLineBreak = true;
5329 
5330 	// cannot attach a class modifier without indent-classes
5331 	if (isCStyle()
5332 	        && isCharPotentialHeader(currentLine, charNum)
5333 	        && (isBraceType(braceTypeStack->back(), CLASS_TYPE)
5334 	            || (isBraceType(braceTypeStack->back(), STRUCT_TYPE)
5335 	                && isInIndentableStruct)))
5336 	{
5337 		if (findKeyword(currentLine, charNum, AS_PUBLIC)
5338 		        || findKeyword(currentLine, charNum, AS_PRIVATE)
5339 		        || findKeyword(currentLine, charNum, AS_PROTECTED))
5340 		{
5341 			if (getModifierIndent())
5342 				extraHalfIndent = true;
5343 			else if (!getClassIndent())
5344 				return; // false;
5345 		}
5346 		else if (getClassIndent())
5347 			extraIndent = true;
5348 	}
5349 
5350 	// cannot attach a 'case' statement without indent-switches
5351 	if (!getSwitchIndent()
5352 	        && isCharPotentialHeader(currentLine, charNum)
5353 	        && (findKeyword(currentLine, charNum, AS_CASE)
5354 	            || findKeyword(currentLine, charNum, AS_DEFAULT)))
5355 		return; // false;
5356 
5357 	// extra indent for switch statements
5358 	if (getSwitchIndent()
5359 	        && !preBraceHeaderStack->empty()
5360 	        && preBraceHeaderStack->back() == &AS_SWITCH
5361 	        && ((isLegalNameChar(currentChar)
5362 	             && !findKeyword(currentLine, charNum, AS_CASE))))
5363 		extraIndent = true;
5364 
5365 	isInLineBreak = false;
5366 	// remove for extra whitespace
5367 	if (formattedLine.length() > lastText + 1
5368 	        && formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos)
5369 		formattedLine.erase(lastText + 1);
5370 
5371 	if (extraHalfIndent)
5372 	{
5373 		int indentLength_ = getIndentLength();
5374 		runInIndentChars = indentLength_ / 2;
5375 		formattedLine.append(runInIndentChars - 1, ' ');
5376 	}
5377 	else if (getForceTabIndentation() && getIndentLength() != getTabLength())
5378 	{
5379 		// insert the space indents
5380 		string indent;
5381 		int indentLength_ = getIndentLength();
5382 		int tabLength_ = getTabLength();
5383 		indent.append(indentLength_, ' ');
5384 		if (extraIndent)
5385 			indent.append(indentLength_, ' ');
5386 		// replace spaces indents with tab indents
5387 		size_t tabCount = indent.length() / tabLength_;		// truncate extra spaces
5388 		indent.replace(0U, tabCount * tabLength_, tabCount, '\t');
5389 		runInIndentChars = indentLength_;
5390 		if (indent[0] == ' ')			// allow for brace
5391 			indent.erase(0, 1);
5392 		formattedLine.append(indent);
5393 	}
5394 	else if (getIndentString() == "\t")
5395 	{
5396 		appendChar('\t', false);
5397 		runInIndentChars = 2;	// one for { and one for tab
5398 		if (extraIndent)
5399 		{
5400 			appendChar('\t', false);
5401 			runInIndentChars++;
5402 		}
5403 	}
5404 	else // spaces
5405 	{
5406 		int indentLength_ = getIndentLength();
5407 		formattedLine.append(indentLength_ - 1, ' ');
5408 		runInIndentChars = indentLength_;
5409 		if (extraIndent)
5410 		{
5411 			formattedLine.append(indentLength_, ' ');
5412 			runInIndentChars += indentLength_;
5413 		}
5414 	}
5415 	isInBraceRunIn = true;
5416 }
5417 
5418 /**
5419  * remove whitespace and add indentation for an array run-in.
5420  */
formatArrayRunIn()5421 void ASFormatter::formatArrayRunIn()
5422 {
5423 	assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE));
5424 
5425 	// make sure the brace is broken
5426 	if (formattedLine.find_first_not_of(" \t{") != string::npos)
5427 		return;
5428 
5429 	size_t lastText = formattedLine.find_last_not_of(" \t");
5430 	if (lastText == string::npos || formattedLine[lastText] != '{')
5431 		return;
5432 
5433 	// check for extra whitespace
5434 	if (formattedLine.length() > lastText + 1
5435 	        && formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos)
5436 		formattedLine.erase(lastText + 1);
5437 
5438 	if (getIndentString() == "\t")
5439 	{
5440 		appendChar('\t', false);
5441 		runInIndentChars = 2;	// one for { and one for tab
5442 	}
5443 	else
5444 	{
5445 		int indent = getIndentLength();
5446 		formattedLine.append(indent - 1, ' ');
5447 		runInIndentChars = indent;
5448 	}
5449 	isInBraceRunIn = true;
5450 	isInLineBreak = false;
5451 }
5452 
5453 /**
5454  * delete a braceTypeStack vector object
5455  * BraceTypeStack did not work with the DeleteContainer template
5456  */
deleteContainer(vector<BraceType> * & container)5457 void ASFormatter::deleteContainer(vector<BraceType>*& container)
5458 {
5459 	if (container != nullptr)
5460 	{
5461 		container->clear();
5462 		delete (container);
5463 		container = nullptr;
5464 	}
5465 }
5466 
5467 /**
5468  * delete a vector object
5469  * T is the type of vector
5470  * used for all vectors except braceTypeStack
5471  */
5472 template<typename T>
deleteContainer(T & container)5473 void ASFormatter::deleteContainer(T& container)
5474 {
5475 	if (container != nullptr)
5476 	{
5477 		container->clear();
5478 		delete (container);
5479 		container = nullptr;
5480 	}
5481 }
5482 
5483 /**
5484  * initialize a braceType vector object
5485  * braceType did not work with the DeleteContainer template
5486  */
initContainer(vector<BraceType> * & container,vector<BraceType> * value)5487 void ASFormatter::initContainer(vector<BraceType>*& container, vector<BraceType>* value)
5488 {
5489 	if (container != nullptr)
5490 		deleteContainer(container);
5491 	container = value;
5492 }
5493 
5494 /**
5495  * initialize a vector object
5496  * T is the type of vector
5497  * used for all vectors except braceTypeStack
5498  */
5499 template<typename T>
initContainer(T & container,T value)5500 void ASFormatter::initContainer(T& container, T value)
5501 {
5502 	// since the ASFormatter object is never deleted,
5503 	// the existing vectors must be deleted before creating new ones
5504 	if (container != nullptr)
5505 		deleteContainer(container);
5506 	container = value;
5507 }
5508 
5509 /**
5510  * convert a tab to spaces.
5511  * charNum points to the current character to convert to spaces.
5512  * tabIncrementIn is the increment that must be added for tab indent characters
5513  *     to get the correct column for the current tab.
5514  * replaces the tab in currentLine with the required number of spaces.
5515  * replaces the value of currentChar.
5516  */
convertTabToSpaces()5517 void ASFormatter::convertTabToSpaces()
5518 {
5519 	assert(currentChar == '\t');
5520 
5521 	// do NOT replace if in quotes
5522 	if (isInQuote || isInQuoteContinuation)
5523 		return;
5524 
5525 	size_t tabSize = getTabLength();
5526 	size_t numSpaces = tabSize - ((tabIncrementIn + charNum) % tabSize);
5527 	currentLine.replace(charNum, 1, numSpaces, ' ');
5528 	currentChar = currentLine[charNum];
5529 }
5530 
5531 /**
5532 * is it ok to break this block?
5533 */
isOkToBreakBlock(BraceType braceType) const5534 bool ASFormatter::isOkToBreakBlock(BraceType braceType) const
5535 {
5536 	// Actually, there should not be an ARRAY_TYPE brace here.
5537 	// But this will avoid breaking a one line block when there is.
5538 	// Otherwise they will be formatted differently on consecutive runs.
5539 	if (isBraceType(braceType, ARRAY_TYPE)
5540 	        && isBraceType(braceType, SINGLE_LINE_TYPE))
5541 		return false;
5542 	if (isBraceType(braceType, COMMAND_TYPE)
5543 	        && isBraceType(braceType, EMPTY_BLOCK_TYPE))
5544 		return false;
5545 	if (!isBraceType(braceType, SINGLE_LINE_TYPE)
5546 	        || isBraceType(braceType, BREAK_BLOCK_TYPE)
5547 	        || shouldBreakOneLineBlocks)
5548 		return true;
5549 	return false;
5550 }
5551 
5552 /**
5553 * check if a sharp header is a paren or non-paren header
5554 */
isSharpStyleWithParen(const string * header) const5555 bool ASFormatter::isSharpStyleWithParen(const string* header) const
5556 {
5557 	return (isSharpStyle() && peekNextChar() == '('
5558 	        && (header == &AS_CATCH
5559 	            || header == &AS_DELEGATE));
5560 }
5561 
5562 /**
5563  * Check for a following header when a comment is reached.
5564  * firstLine must contain the start of the comment.
5565  * return value is a pointer to the header or nullptr.
5566  */
checkForHeaderFollowingComment(const string & firstLine) const5567 const string* ASFormatter::checkForHeaderFollowingComment(const string& firstLine) const
5568 {
5569 	assert(isInComment || isInLineComment);
5570 	assert(shouldBreakElseIfs || shouldBreakBlocks || isInSwitchStatement());
5571 	// look ahead to find the next non-comment text
5572 	bool endOnEmptyLine = (currentHeader == nullptr);
5573 	if (isInSwitchStatement())
5574 		endOnEmptyLine = false;
5575 	string nextText = peekNextText(firstLine, endOnEmptyLine);
5576 
5577 	if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
5578 		return nullptr;
5579 
5580 	return ASBase::findHeader(nextText, 0, headers);
5581 }
5582 
5583 /**
5584  * process preprocessor statements.
5585  * charNum should be the index of the #.
5586  *
5587  * delete braceTypeStack entries added by #if if a #else is found.
5588  * prevents double entries in the braceTypeStack.
5589  */
processPreprocessor()5590 void ASFormatter::processPreprocessor()
5591 {
5592 	assert(currentChar == '#');
5593 
5594 	const size_t preproc = currentLine.find_first_not_of(" \t", charNum + 1);
5595 
5596 	if (preproc == string::npos)
5597 		return;
5598 
5599 	if (currentLine.compare(preproc, 2, "if") == 0)
5600 	{
5601 		preprocBraceTypeStackSize = braceTypeStack->size();
5602 	}
5603 	else if (currentLine.compare(preproc, 4, "else") == 0)
5604 	{
5605 		// delete stack entries added in #if
5606 		// should be replaced by #else
5607 		if (preprocBraceTypeStackSize > 0)
5608 		{
5609 			int addedPreproc = braceTypeStack->size() - preprocBraceTypeStackSize;
5610 			for (int i = 0; i < addedPreproc; i++)
5611 				braceTypeStack->pop_back();
5612 		}
5613 	}
5614 }
5615 
5616 /**
5617  * determine if the next line starts a comment
5618  * and a header follows the comment or comments.
5619  */
commentAndHeaderFollows()5620 bool ASFormatter::commentAndHeaderFollows()
5621 {
5622 	// called ONLY IF shouldDeleteEmptyLines and shouldBreakBlocks are TRUE.
5623 	assert(shouldDeleteEmptyLines && shouldBreakBlocks);
5624 
5625 	// is the next line a comment
5626 	auto stream = make_shared<ASPeekStream>(sourceIterator);
5627 	if (!stream->hasMoreLines())
5628 		return false;
5629 	string nextLine_ = stream->peekNextLine();
5630 	size_t firstChar = nextLine_.find_first_not_of(" \t");
5631 	if (firstChar == string::npos
5632 	        || !(nextLine_.compare(firstChar, 2, "//") == 0
5633 	             || nextLine_.compare(firstChar, 2, "/*") == 0))
5634 		return false;
5635 
5636 	// find the next non-comment text, and reset
5637 	string nextText = peekNextText(nextLine_, false, stream);
5638 	if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0))
5639 		return false;
5640 
5641 	const string* newHeader = ASBase::findHeader(nextText, 0, headers);
5642 
5643 	if (newHeader == nullptr)
5644 		return false;
5645 
5646 	// if a closing header, reset break unless break is requested
5647 	if (isClosingHeader(newHeader) && !shouldBreakClosingHeaderBlocks)
5648 	{
5649 		isAppendPostBlockEmptyLineRequested = false;
5650 		return false;
5651 	}
5652 
5653 	return true;
5654 }
5655 
5656 /**
5657  * determine if a brace should be attached or broken
5658  * uses braces in the braceTypeStack
5659  * the last brace in the braceTypeStack is the one being formatted
5660  * returns true if the brace should be broken
5661  */
isCurrentBraceBroken() const5662 bool ASFormatter::isCurrentBraceBroken() const
5663 {
5664 	assert(braceTypeStack->size() > 1);
5665 
5666 	bool breakBrace = false;
5667 	size_t stackEnd = braceTypeStack->size() - 1;
5668 
5669 	// check brace modifiers
5670 	if (shouldAttachExternC
5671 	        && isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE))
5672 	{
5673 		return false;
5674 	}
5675 	if (shouldAttachNamespace
5676 	        && isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE))
5677 	{
5678 		return false;
5679 	}
5680 	if (shouldAttachClass
5681 	        && (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE)
5682 	            || isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE)))
5683 	{
5684 		return false;
5685 	}
5686 	if (shouldAttachInline
5687 	        && isCStyle()			// for C++ only
5688 	        && braceFormatMode != RUN_IN_MODE
5689 	        && !(currentLineBeginsWithBrace && peekNextChar() == '/')
5690 	        && isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE))
5691 	{
5692 		size_t i;
5693 		for (i = 1; i < braceTypeStack->size(); i++)
5694 			if (isBraceType((*braceTypeStack)[i], CLASS_TYPE)
5695 			        || isBraceType((*braceTypeStack)[i], STRUCT_TYPE))
5696 				return false;
5697 	}
5698 
5699 	// check braces
5700 	if (isBraceType((*braceTypeStack)[stackEnd], EXTERN_TYPE))
5701 	{
5702 		if (currentLineBeginsWithBrace
5703 		        || braceFormatMode == RUN_IN_MODE)
5704 			breakBrace = true;
5705 	}
5706 	else if (braceFormatMode == NONE_MODE)
5707 	{
5708 		if (currentLineBeginsWithBrace
5709 		        && currentLineFirstBraceNum == (size_t) charNum)
5710 			breakBrace = true;
5711 	}
5712 	else if (braceFormatMode == BREAK_MODE || braceFormatMode == RUN_IN_MODE)
5713 	{
5714 		breakBrace = true;
5715 	}
5716 	else if (braceFormatMode == LINUX_MODE)
5717 	{
5718 		// break a namespace if NOT stroustrup or mozilla
5719 		if (isBraceType((*braceTypeStack)[stackEnd], NAMESPACE_TYPE))
5720 		{
5721 			if (formattingStyle != STYLE_STROUSTRUP
5722 			        && formattingStyle != STYLE_MOZILLA)
5723 				breakBrace = true;
5724 		}
5725 		// break a class or interface if NOT stroustrup
5726 		else if (isBraceType((*braceTypeStack)[stackEnd], CLASS_TYPE)
5727 		         || isBraceType((*braceTypeStack)[stackEnd], INTERFACE_TYPE))
5728 		{
5729 			if (formattingStyle != STYLE_STROUSTRUP)
5730 				breakBrace = true;
5731 		}
5732 		// break a struct if mozilla - an enum is processed as an array brace
5733 		else if (isBraceType((*braceTypeStack)[stackEnd], STRUCT_TYPE))
5734 		{
5735 			if (formattingStyle == STYLE_MOZILLA)
5736 				breakBrace = true;
5737 		}
5738 		// break the first brace if a function
5739 		else if (isBraceType((*braceTypeStack)[stackEnd], COMMAND_TYPE))
5740 		{
5741 			if (stackEnd == 1)
5742 			{
5743 				breakBrace = true;
5744 			}
5745 			else if (stackEnd > 1)
5746 			{
5747 				// break the first brace after these if a function
5748 				if (isBraceType((*braceTypeStack)[stackEnd - 1], NAMESPACE_TYPE)
5749 				        || isBraceType((*braceTypeStack)[stackEnd - 1], CLASS_TYPE)
5750 				        || isBraceType((*braceTypeStack)[stackEnd - 1], ARRAY_TYPE)
5751 				        || isBraceType((*braceTypeStack)[stackEnd - 1], STRUCT_TYPE)
5752 				        || isBraceType((*braceTypeStack)[stackEnd - 1], EXTERN_TYPE))
5753 				{
5754 					breakBrace = true;
5755 				}
5756 			}
5757 		}
5758 	}
5759 	return breakBrace;
5760 }
5761 
5762 /**
5763  * format comment body
5764  * the calling function should have a continue statement after calling this method
5765  */
formatCommentBody()5766 void ASFormatter::formatCommentBody()
5767 {
5768 	assert(isInComment);
5769 
5770 	// append the comment
5771 	while (charNum < (int) currentLine.length())
5772 	{
5773 		currentChar = currentLine[charNum];
5774 		if (isSequenceReached("*/"))
5775 		{
5776 			formatCommentCloser();
5777 			break;
5778 		}
5779 		if (currentChar == '\t' && shouldConvertTabs)
5780 			convertTabToSpaces();
5781 		appendCurrentChar();
5782 		++charNum;
5783 	}
5784 	if (shouldStripCommentPrefix)
5785 		stripCommentPrefix();
5786 }
5787 
5788 /**
5789  * format a comment opener
5790  * the comment opener will be appended to the current formattedLine or a new formattedLine as necessary
5791  * the calling function should have a continue statement after calling this method
5792  */
formatCommentOpener()5793 void ASFormatter::formatCommentOpener()
5794 {
5795 	assert(isSequenceReached("/*"));
5796 
5797 	isInComment = isInCommentStartLine = true;
5798 	isImmediatelyPostLineComment = false;
5799 	if (previousNonWSChar == '}')
5800 		resetEndOfStatement();
5801 
5802 	// Check for a following header.
5803 	// For speed do not check multiple comment lines more than once.
5804 	// For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'.
5805 	const string* followingHeader = nullptr;
5806 	if ((doesLineStartComment
5807 	        && !isImmediatelyPostCommentOnly
5808 	        && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
5809 	        && (shouldBreakElseIfs
5810 	            || isInSwitchStatement()
5811 	            || (shouldBreakBlocks
5812 	                && !isImmediatelyPostEmptyLine
5813 	                && previousCommandChar != '{')))
5814 		followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum));
5815 
5816 	if (spacePadNum != 0 && !isInLineBreak)
5817 		adjustComments();
5818 	formattedLineCommentNum = formattedLine.length();
5819 
5820 	// must be done BEFORE appendSequence
5821 	if (previousCommandChar == '{'
5822 	        && !isImmediatelyPostComment
5823 	        && !isImmediatelyPostLineComment)
5824 	{
5825 		if (isBraceType(braceTypeStack->back(), NAMESPACE_TYPE))
5826 		{
5827 			// namespace run-in is always broken.
5828 			isInLineBreak = true;
5829 		}
5830 		else if (braceFormatMode == NONE_MODE)
5831 		{
5832 			// should a run-in statement be attached?
5833 			if (currentLineBeginsWithBrace)
5834 				formatRunIn();
5835 		}
5836 		else if (braceFormatMode == ATTACH_MODE)
5837 		{
5838 			// if the brace was not attached?
5839 			if (formattedLine.length() > 0 && formattedLine[0] == '{'
5840 			        && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE))
5841 				isInLineBreak = true;
5842 		}
5843 		else if (braceFormatMode == RUN_IN_MODE)
5844 		{
5845 			// should a run-in statement be attached?
5846 			if (formattedLine.length() > 0 && formattedLine[0] == '{')
5847 				formatRunIn();
5848 		}
5849 	}
5850 	else if (!doesLineStartComment)
5851 		noTrimCommentContinuation = true;
5852 
5853 	// ASBeautifier needs to know the following statements
5854 	if (shouldBreakElseIfs && followingHeader == &AS_ELSE)
5855 		elseHeaderFollowsComments = true;
5856 	if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT)
5857 		caseHeaderFollowsComments = true;
5858 
5859 	// appendSequence will write the previous line
5860 	appendSequence(AS_OPEN_COMMENT);
5861 	goForward(1);
5862 
5863 	// must be done AFTER appendSequence
5864 
5865 	// Break before the comment if a header follows the line comment.
5866 	// But not break if previous line is empty, a comment, or a '{'.
5867 	if (shouldBreakBlocks
5868 	        && followingHeader != nullptr
5869 	        && !isImmediatelyPostEmptyLine
5870 	        && previousCommandChar != '{')
5871 	{
5872 		if (isClosingHeader(followingHeader))
5873 		{
5874 			if (!shouldBreakClosingHeaderBlocks)
5875 				isPrependPostBlockEmptyLineRequested = false;
5876 		}
5877 		// if an opening header, break before the comment
5878 		else
5879 			isPrependPostBlockEmptyLineRequested = true;
5880 	}
5881 
5882 	if (previousCommandChar == '}')
5883 		currentHeader = nullptr;
5884 }
5885 
5886 /**
5887  * format a comment closer
5888  * the comment closer will be appended to the current formattedLine
5889  */
formatCommentCloser()5890 void ASFormatter::formatCommentCloser()
5891 {
5892 	assert(isSequenceReached("*/"));
5893 	isInComment = false;
5894 	noTrimCommentContinuation = false;
5895 	isImmediatelyPostComment = true;
5896 	appendSequence(AS_CLOSE_COMMENT);
5897 	goForward(1);
5898 	if (doesLineStartComment
5899 	        && (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos))
5900 		lineEndsInCommentOnly = true;
5901 	if (peekNextChar() == '}'
5902 	        && previousCommandChar != ';'
5903 	        && !isBraceType(braceTypeStack->back(), ARRAY_TYPE)
5904 	        && !isInPreprocessor
5905 	        && isOkToBreakBlock(braceTypeStack->back()))
5906 	{
5907 		isInLineBreak = true;
5908 		shouldBreakLineAtNextChar = true;
5909 	}
5910 }
5911 
5912 /**
5913  * format a line comment body
5914  * the calling function should have a continue statement after calling this method
5915  */
formatLineCommentBody()5916 void ASFormatter::formatLineCommentBody()
5917 {
5918 	assert(isInLineComment);
5919 
5920 	// append the comment
5921 	while (charNum < (int) currentLine.length())
5922 //	        && !isLineReady	// commented out in release 2.04, unnecessary
5923 	{
5924 		currentChar = currentLine[charNum];
5925 		if (currentChar == '\t' && shouldConvertTabs)
5926 			convertTabToSpaces();
5927 		appendCurrentChar();
5928 		++charNum;
5929 	}
5930 
5931 	// explicitly break a line when a line comment's end is found.
5932 	if (charNum == (int) currentLine.length())
5933 	{
5934 		isInLineBreak = true;
5935 		isInLineComment = false;
5936 		isImmediatelyPostLineComment = true;
5937 		currentChar = 0;  //make sure it is a neutral char.
5938 	}
5939 }
5940 
5941 /**
5942  * format a line comment opener
5943  * the line comment opener will be appended to the current formattedLine or a new formattedLine as necessary
5944  * the calling function should have a continue statement after calling this method
5945  */
formatLineCommentOpener()5946 void ASFormatter::formatLineCommentOpener()
5947 {
5948 	assert(isSequenceReached("//"));
5949 
5950 	if ((int) currentLine.length() > charNum + 2
5951 	        && currentLine[charNum + 2] == '\xf2')     // check for windows line marker
5952 		isAppendPostBlockEmptyLineRequested = false;
5953 
5954 	isInLineComment = true;
5955 	isCharImmediatelyPostComment = false;
5956 	if (previousNonWSChar == '}')
5957 		resetEndOfStatement();
5958 
5959 	// Check for a following header.
5960 	// For speed do not check multiple comment lines more than once.
5961 	// For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'.
5962 	const string* followingHeader = nullptr;
5963 	if ((lineIsLineCommentOnly
5964 	        && !isImmediatelyPostCommentOnly
5965 	        && isBraceType(braceTypeStack->back(), COMMAND_TYPE))
5966 	        && (shouldBreakElseIfs
5967 	            || isInSwitchStatement()
5968 	            || (shouldBreakBlocks
5969 	                && !isImmediatelyPostEmptyLine
5970 	                && previousCommandChar != '{')))
5971 		followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum));
5972 
5973 	// do not indent if in column 1 or 2
5974 	// or in a namespace before the opening brace
5975 	if ((!shouldIndentCol1Comments && !lineCommentNoIndent)
5976 	        || foundNamespaceHeader)
5977 	{
5978 		if (charNum == 0)
5979 			lineCommentNoIndent = true;
5980 		else if (charNum == 1 && currentLine[0] == ' ')
5981 			lineCommentNoIndent = true;
5982 	}
5983 	// move comment if spaces were added or deleted
5984 	if (!lineCommentNoIndent && spacePadNum != 0 && !isInLineBreak)
5985 		adjustComments();
5986 	formattedLineCommentNum = formattedLine.length();
5987 
5988 	// must be done BEFORE appendSequence
5989 	// check for run-in statement
5990 	if (previousCommandChar == '{'
5991 	        && !isImmediatelyPostComment
5992 	        && !isImmediatelyPostLineComment)
5993 	{
5994 		if (braceFormatMode == NONE_MODE)
5995 		{
5996 			if (currentLineBeginsWithBrace)
5997 				formatRunIn();
5998 		}
5999 		else if (braceFormatMode == RUN_IN_MODE)
6000 		{
6001 			if (!lineCommentNoIndent)
6002 				formatRunIn();
6003 			else
6004 				isInLineBreak = true;
6005 		}
6006 		else if (braceFormatMode == BREAK_MODE)
6007 		{
6008 			if (formattedLine.length() > 0 && formattedLine[0] == '{')
6009 				isInLineBreak = true;
6010 		}
6011 		else
6012 		{
6013 			if (currentLineBeginsWithBrace)
6014 				isInLineBreak = true;
6015 		}
6016 	}
6017 
6018 	// ASBeautifier needs to know the following statements
6019 	if (shouldBreakElseIfs && followingHeader == &AS_ELSE)
6020 		elseHeaderFollowsComments = true;
6021 	if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT)
6022 		caseHeaderFollowsComments = true;
6023 
6024 	// appendSequence will write the previous line
6025 	appendSequence(AS_OPEN_LINE_COMMENT);
6026 	goForward(1);
6027 
6028 	// must be done AFTER appendSequence
6029 
6030 	// Break before the comment if a header follows the line comment.
6031 	// But do not break if previous line is empty, a comment, or a '{'.
6032 	if (shouldBreakBlocks
6033 	        && followingHeader != nullptr
6034 	        && !isImmediatelyPostEmptyLine
6035 	        && previousCommandChar != '{')
6036 	{
6037 		if (isClosingHeader(followingHeader))
6038 		{
6039 			if (!shouldBreakClosingHeaderBlocks)
6040 				isPrependPostBlockEmptyLineRequested = false;
6041 		}
6042 		// if an opening header, break before the comment
6043 		else
6044 			isPrependPostBlockEmptyLineRequested = true;
6045 	}
6046 
6047 	if (previousCommandChar == '}')
6048 		currentHeader = nullptr;
6049 
6050 	// if tabbed input don't convert the immediately following tabs to spaces
6051 	if (getIndentString() == "\t" && lineCommentNoIndent)
6052 	{
6053 		while (charNum + 1 < (int) currentLine.length()
6054 		        && currentLine[charNum + 1] == '\t')
6055 		{
6056 			currentChar = currentLine[++charNum];
6057 			appendCurrentChar();
6058 		}
6059 	}
6060 
6061 	// explicitly break a line when a line comment's end is found.
6062 	if (charNum + 1 == (int) currentLine.length())
6063 	{
6064 		isInLineBreak = true;
6065 		isInLineComment = false;
6066 		isImmediatelyPostLineComment = true;
6067 		currentChar = 0;  //make sure it is a neutral char.
6068 	}
6069 }
6070 
6071 /**
6072  * format quote body
6073  * the calling function should have a continue statement after calling this method
6074  */
formatQuoteBody()6075 void ASFormatter::formatQuoteBody()
6076 {
6077 	assert(isInQuote);
6078 
6079 	if (isSpecialChar)
6080 	{
6081 		isSpecialChar = false;
6082 	}
6083 	else if (currentChar == '\\' && !isInVerbatimQuote)
6084 	{
6085 		if (peekNextChar() == ' ')              // is this '\' at end of line
6086 			haveLineContinuationChar = true;
6087 		else
6088 			isSpecialChar = true;
6089 	}
6090 	else if (isInVerbatimQuote && currentChar == '"')
6091 	{
6092 		if (isCStyle())
6093 		{
6094 			string delim = ')' + verbatimDelimiter;
6095 			int delimStart = charNum - delim.length();
6096 			if (delimStart > 0 && currentLine.substr(delimStart, delim.length()) == delim)
6097 			{
6098 				isInQuote = false;
6099 				isInVerbatimQuote = false;
6100 			}
6101 		}
6102 		else if (isSharpStyle())
6103 		{
6104 			if ((int) currentLine.length() > charNum + 1
6105 			        && currentLine[charNum + 1] == '"')			// check consecutive quotes
6106 			{
6107 				appendSequence("\"\"");
6108 				goForward(1);
6109 				return;
6110 			}
6111 			isInQuote = false;
6112 			isInVerbatimQuote = false;
6113 		}
6114 	}
6115 	else if (quoteChar == currentChar)
6116 	{
6117 		isInQuote = false;
6118 	}
6119 
6120 	appendCurrentChar();
6121 
6122 	// append the text to the ending quoteChar or an escape sequence
6123 	// tabs in quotes are NOT changed by convert-tabs
6124 	if (isInQuote && currentChar != '\\')
6125 	{
6126 		while (charNum + 1 < (int) currentLine.length()
6127 		        && currentLine[charNum + 1] != quoteChar
6128 		        && currentLine[charNum + 1] != '\\')
6129 		{
6130 			currentChar = currentLine[++charNum];
6131 			appendCurrentChar();
6132 		}
6133 	}
6134 	if (charNum + 1 >= (int) currentLine.length()
6135 	        && currentChar != '\\'
6136 	        && !isInVerbatimQuote)
6137 		isInQuote = false;				// missing closing quote
6138 }
6139 
6140 /**
6141  * format a quote opener
6142  * the quote opener will be appended to the current formattedLine or a new formattedLine as necessary
6143  * the calling function should have a continue statement after calling this method
6144  */
formatQuoteOpener()6145 void ASFormatter::formatQuoteOpener()
6146 {
6147 	assert(currentChar == '"'
6148 	       || (currentChar == '\'' && !isDigitSeparator(currentLine, charNum)));
6149 
6150 	isInQuote = true;
6151 	quoteChar = currentChar;
6152 	if (isCStyle() && previousChar == 'R')
6153 	{
6154 		int parenPos = currentLine.find('(', charNum);
6155 		if (parenPos != -1)
6156 		{
6157 			isInVerbatimQuote = true;
6158 			verbatimDelimiter = currentLine.substr(charNum + 1, parenPos - charNum - 1);
6159 		}
6160 	}
6161 	else if (isSharpStyle() && previousChar == '@')
6162 		isInVerbatimQuote = true;
6163 
6164 	// a quote following a brace is an array
6165 	if (previousCommandChar == '{'
6166 	        && !isImmediatelyPostComment
6167 	        && !isImmediatelyPostLineComment
6168 	        && isNonInStatementArray
6169 	        && !isBraceType(braceTypeStack->back(), SINGLE_LINE_TYPE)
6170 	        && !isWhiteSpace(peekNextChar()))
6171 	{
6172 		if (braceFormatMode == NONE_MODE)
6173 		{
6174 			if (currentLineBeginsWithBrace)
6175 				formatRunIn();
6176 		}
6177 		else if (braceFormatMode == RUN_IN_MODE)
6178 		{
6179 			formatRunIn();
6180 		}
6181 		else if (braceFormatMode == BREAK_MODE)
6182 		{
6183 			if (formattedLine.length() > 0 && formattedLine[0] == '{')
6184 				isInLineBreak = true;
6185 		}
6186 		else
6187 		{
6188 			if (currentLineBeginsWithBrace)
6189 				isInLineBreak = true;
6190 		}
6191 	}
6192 	previousCommandChar = ' ';
6193 	appendCurrentChar();
6194 }
6195 
6196 /**
6197  * get the next line comment adjustment that results from breaking a closing brace.
6198  * the brace must be on the same line as the closing header.
6199  * i.e "} else" changed to "} <NL> else".
6200  */
getNextLineCommentAdjustment()6201 int ASFormatter::getNextLineCommentAdjustment()
6202 {
6203 	assert(foundClosingHeader && previousNonWSChar == '}');
6204 	if (charNum < 1)			// "else" is in column 1
6205 		return 0;
6206 	size_t lastBrace = currentLine.rfind('}', charNum - 1);
6207 	if (lastBrace != string::npos)
6208 		return (lastBrace - charNum);	// return a negative number
6209 	return 0;
6210 }
6211 
6212 // for console build only
getLineEndFormat() const6213 LineEndFormat ASFormatter::getLineEndFormat() const
6214 {
6215 	return lineEnd;
6216 }
6217 
6218 /**
6219  * get the current line comment adjustment that results from attaching
6220  * a closing header to a closing brace.
6221  * the brace must be on the line previous to the closing header.
6222  * the adjustment is 2 chars, one for the brace and one for the space.
6223  * i.e "} <NL> else" changed to "} else".
6224  */
getCurrentLineCommentAdjustment()6225 int ASFormatter::getCurrentLineCommentAdjustment()
6226 {
6227 	assert(foundClosingHeader && previousNonWSChar == '}');
6228 	if (charNum < 1)
6229 		return 2;
6230 	size_t lastBrace = currentLine.rfind('}', charNum - 1);
6231 	if (lastBrace == string::npos)
6232 		return 2;
6233 	return 0;
6234 }
6235 
6236 /**
6237  * get the previous word on a line
6238  * the argument 'currPos' must point to the current position.
6239  *
6240  * @return is the previous word or an empty string if none found.
6241  */
getPreviousWord(const string & line,int currPos) const6242 string ASFormatter::getPreviousWord(const string& line, int currPos) const
6243 {
6244 	// get the last legal word (may be a number)
6245 	if (currPos == 0)
6246 		return string();
6247 
6248 	size_t end = line.find_last_not_of(" \t", currPos - 1);
6249 	if (end == string::npos || !isLegalNameChar(line[end]))
6250 		return string();
6251 
6252 	int start;          // start of the previous word
6253 	for (start = end; start > -1; start--)
6254 	{
6255 		if (!isLegalNameChar(line[start]) || line[start] == '.')
6256 			break;
6257 	}
6258 	start++;
6259 
6260 	return (line.substr(start, end - start + 1));
6261 }
6262 
6263 /**
6264  * check if a line break is needed when a closing brace
6265  * is followed by a closing header.
6266  * the break depends on the braceFormatMode and other factors.
6267  */
isLineBreakBeforeClosingHeader()6268 void ASFormatter::isLineBreakBeforeClosingHeader()
6269 {
6270 	assert(foundClosingHeader && previousNonWSChar == '}');
6271 
6272 	if (currentHeader == &AS_WHILE && shouldAttachClosingWhile)
6273 	{
6274 		appendClosingHeader();
6275 		return;
6276 	}
6277 
6278 	if (braceFormatMode == BREAK_MODE
6279 	        || braceFormatMode == RUN_IN_MODE
6280 	        || attachClosingBraceMode)
6281 	{
6282 		isInLineBreak = true;
6283 	}
6284 	else if (braceFormatMode == NONE_MODE)
6285 	{
6286 		if (shouldBreakClosingHeaderBraces
6287 		        || getBraceIndent() || getBlockIndent())
6288 		{
6289 			isInLineBreak = true;
6290 		}
6291 		else
6292 		{
6293 			appendSpacePad();
6294 			// is closing brace broken?
6295 			size_t i = currentLine.find_first_not_of(" \t");
6296 			if (i != string::npos && currentLine[i] == '}')
6297 				isInLineBreak = false;
6298 
6299 			if (shouldBreakBlocks)
6300 				isAppendPostBlockEmptyLineRequested = false;
6301 		}
6302 	}
6303 	// braceFormatMode == ATTACH_MODE, LINUX_MODE
6304 	else
6305 	{
6306 		if (shouldBreakClosingHeaderBraces
6307 		        || getBraceIndent() || getBlockIndent())
6308 		{
6309 			isInLineBreak = true;
6310 		}
6311 		else
6312 		{
6313 			appendClosingHeader();
6314 			if (shouldBreakBlocks)
6315 				isAppendPostBlockEmptyLineRequested = false;
6316 		}
6317 	}
6318 }
6319 
6320 /**
6321  * Append a closing header to the previous closing brace, if possible
6322  */
appendClosingHeader()6323 void ASFormatter::appendClosingHeader()
6324 {
6325 	// if a blank line does not precede this
6326 	// or last line is not a one line block, attach header
6327 	bool previousLineIsEmpty = isEmptyLine(formattedLine);
6328 	int previousLineIsOneLineBlock = 0;
6329 	size_t firstBrace = findNextChar(formattedLine, '{');
6330 	if (firstBrace != string::npos)
6331 		previousLineIsOneLineBlock = isOneLineBlockReached(formattedLine, firstBrace);
6332 	if (!previousLineIsEmpty
6333 	        && previousLineIsOneLineBlock == 0)
6334 	{
6335 		isInLineBreak = false;
6336 		appendSpacePad();
6337 		spacePadNum = 0;	// don't count as comment padding
6338 	}
6339 }
6340 
6341 /**
6342  * Add braces to a single line statement following a header.
6343  * braces are not added if the proper conditions are not met.
6344  * braces are added to the currentLine.
6345  */
addBracesToStatement()6346 bool ASFormatter::addBracesToStatement()
6347 {
6348 	assert(isImmediatelyPostHeader);
6349 
6350 	if (currentHeader != &AS_IF
6351 	        && currentHeader != &AS_ELSE
6352 	        && currentHeader != &AS_FOR
6353 	        && currentHeader != &AS_WHILE
6354 	        && currentHeader != &AS_DO
6355 	        && currentHeader != &AS_FOREACH
6356 	        && currentHeader != &AS_QFOREACH
6357 	        && currentHeader != &AS_QFOREVER
6358 	        && currentHeader != &AS_FOREVER)
6359 		return false;
6360 
6361 	if (currentHeader == &AS_WHILE && foundClosingHeader)	// do-while
6362 		return false;
6363 
6364 	// do not brace an empty statement
6365 	if (currentChar == ';')
6366 		return false;
6367 
6368 	// do not add if a header follows
6369 	if (isCharPotentialHeader(currentLine, charNum))
6370 		if (findHeader(headers) != nullptr)
6371 			return false;
6372 
6373 	// find the next semi-colon
6374 	size_t nextSemiColon = charNum;
6375 	if (currentChar != ';')
6376 		nextSemiColon = findNextChar(currentLine, ';', charNum + 1);
6377 	if (nextSemiColon == string::npos)
6378 		return false;
6379 
6380 	// add closing brace before changing the line length
6381 	if (nextSemiColon == currentLine.length() - 1)
6382 		currentLine.append(" }");
6383 	else
6384 		currentLine.insert(nextSemiColon + 1, " }");
6385 	// add opening brace
6386 	currentLine.insert(charNum, "{ ");
6387 	assert(computeChecksumIn("{}"));
6388 	currentChar = '{';
6389 	if ((int) currentLine.find_first_not_of(" \t") == charNum)
6390 		currentLineBeginsWithBrace = true;
6391 	// remove extra spaces
6392 	if (!shouldAddOneLineBraces)
6393 	{
6394 		size_t lastText = formattedLine.find_last_not_of(" \t");
6395 		if ((formattedLine.length() - 1) - lastText > 1)
6396 			formattedLine.erase(lastText + 1);
6397 	}
6398 	return true;
6399 }
6400 
6401 /**
6402  * Remove braces from a single line statement following a header.
6403  * braces are not removed if the proper conditions are not met.
6404  * The first brace is replaced by a space.
6405  */
removeBracesFromStatement()6406 bool ASFormatter::removeBracesFromStatement()
6407 {
6408 	assert(isImmediatelyPostHeader);
6409 	assert(currentChar == '{');
6410 
6411 	if (currentHeader != &AS_IF
6412 	        && currentHeader != &AS_ELSE
6413 	        && currentHeader != &AS_FOR
6414 	        && currentHeader != &AS_WHILE
6415 	        && currentHeader != &AS_FOREACH)
6416 		return false;
6417 
6418 	if (currentHeader == &AS_WHILE && foundClosingHeader)	// do-while
6419 		return false;
6420 
6421 	bool isFirstLine = true;
6422 	string nextLine_;
6423 	// leave nextLine_ empty if end of line comment follows
6424 	if (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBrace)
6425 		nextLine_ = currentLine.substr(charNum + 1);
6426 	size_t nextChar = 0;
6427 
6428 	// find the first non-blank text
6429 	ASPeekStream stream(sourceIterator);
6430 	while (stream.hasMoreLines() || isFirstLine)
6431 	{
6432 		if (isFirstLine)
6433 			isFirstLine = false;
6434 		else
6435 		{
6436 			nextLine_ = stream.peekNextLine();
6437 			nextChar = 0;
6438 		}
6439 
6440 		nextChar = nextLine_.find_first_not_of(" \t", nextChar);
6441 		if (nextChar != string::npos)
6442 			break;
6443 	}
6444 	if (!stream.hasMoreLines())
6445 		return false;
6446 
6447 	// don't remove if comments or a header follow the brace
6448 	if ((nextLine_.compare(nextChar, 2, "/*") == 0)
6449 	        || (nextLine_.compare(nextChar, 2, "//") == 0)
6450 	        || (isCharPotentialHeader(nextLine_, nextChar)
6451 	            && ASBase::findHeader(nextLine_, nextChar, headers) != nullptr))
6452 		return false;
6453 
6454 	// find the next semi-colon
6455 	size_t nextSemiColon = nextChar;
6456 	if (nextLine_[nextChar] != ';')
6457 		nextSemiColon = findNextChar(nextLine_, ';', nextChar + 1);
6458 	if (nextSemiColon == string::npos)
6459 		return false;
6460 
6461 	// find the closing brace
6462 	isFirstLine = true;
6463 	nextChar = nextSemiColon + 1;
6464 	while (stream.hasMoreLines() || isFirstLine)
6465 	{
6466 		if (isFirstLine)
6467 			isFirstLine = false;
6468 		else
6469 		{
6470 			nextLine_ = stream.peekNextLine();
6471 			nextChar = 0;
6472 		}
6473 		nextChar = nextLine_.find_first_not_of(" \t", nextChar);
6474 		if (nextChar != string::npos)
6475 			break;
6476 	}
6477 	if (nextLine_.length() == 0 || nextLine_[nextChar] != '}')
6478 		return false;
6479 
6480 	// remove opening brace
6481 	currentLine[charNum] = currentChar = ' ';
6482 	assert(adjustChecksumIn(-'{'));
6483 	return true;
6484 }
6485 
6486 /**
6487  * Find the next character that is not in quotes or a comment.
6488  *
6489  * @param line         the line to be searched.
6490  * @param searchChar   the char to find.
6491  * @param searchStart  the start position on the line (default is 0).
6492  * @return the position on the line or string::npos if not found.
6493  */
findNextChar(const string & line,char searchChar,int searchStart) const6494 size_t ASFormatter::findNextChar(const string& line, char searchChar, int searchStart /*0*/) const
6495 {
6496 	// find the next searchChar
6497 	size_t i;
6498 	for (i = searchStart; i < line.length(); i++)
6499 	{
6500 		if (line.compare(i, 2, "//") == 0)
6501 			return string::npos;
6502 		if (line.compare(i, 2, "/*") == 0)
6503 		{
6504 			size_t endComment = line.find("*/", i + 2);
6505 			if (endComment == string::npos)
6506 				return string::npos;
6507 			i = endComment + 2;
6508 			if (i >= line.length())
6509 				return string::npos;
6510 		}
6511 		if (line[i] == '"'
6512 		        || (line[i] == '\'' && !isDigitSeparator(line, i)))
6513 		{
6514 			char quote = line[i];
6515 			while (i < line.length())
6516 			{
6517 				size_t endQuote = line.find(quote, i + 1);
6518 				if (endQuote == string::npos)
6519 					return string::npos;
6520 				i = endQuote;
6521 				if (line[endQuote - 1] != '\\')	// check for '\"'
6522 					break;
6523 				if (line[endQuote - 2] == '\\')	// check for '\\'
6524 					break;
6525 			}
6526 		}
6527 
6528 		if (line[i] == searchChar)
6529 			break;
6530 
6531 		// for now don't process C# 'delegate' braces
6532 		// do this last in case the search char is a '{'
6533 		if (line[i] == '{')
6534 			return string::npos;
6535 	}
6536 	if (i >= line.length())	// didn't find searchChar
6537 		return string::npos;
6538 
6539 	return i;
6540 }
6541 
6542 /**
6543  * Find split point for break/attach return type.
6544  */
findReturnTypeSplitPoint(const string & firstLine)6545 void ASFormatter::findReturnTypeSplitPoint(const string& firstLine)
6546 {
6547 	assert((isBraceType(braceTypeStack->back(), NULL_TYPE)
6548 	        || isBraceType(braceTypeStack->back(), DEFINITION_TYPE)));
6549 	assert(shouldBreakReturnType || shouldBreakReturnTypeDecl
6550 	       || shouldAttachReturnType || shouldAttachReturnTypeDecl);
6551 
6552 	bool isFirstLine     = true;
6553 	bool isInComment_    = false;
6554 	bool isInQuote_      = false;
6555 	bool foundSplitPoint = false;
6556 	bool isAlreadyBroken = false;
6557 	char quoteChar_      = ' ';
6558 	char currNonWSChar   = ' ';
6559 	char prevNonWSChar   = ' ';
6560 	size_t parenCount    = 0;
6561 	size_t squareCount   = 0;
6562 	size_t angleCount    = 0;
6563 	size_t breakLineNum  = 0;
6564 	size_t breakCharNum  = string::npos;
6565 	string line          = firstLine;
6566 
6567 	// Process the lines until a ';' or '{'.
6568 	ASPeekStream stream(sourceIterator);
6569 	while (stream.hasMoreLines() || isFirstLine)
6570 	{
6571 		if (isFirstLine)
6572 			isFirstLine = false;
6573 		else
6574 		{
6575 			if (isInQuote_)
6576 				return;
6577 			line = stream.peekNextLine();
6578 			if (!foundSplitPoint)
6579 				++breakLineNum;
6580 		}
6581 		size_t firstCharNum = line.find_first_not_of(" \t");
6582 		if (firstCharNum == string::npos)
6583 			continue;
6584 		if (line[firstCharNum] == '#')
6585 		{
6586 			// don't attach to a preprocessor
6587 			if (shouldAttachReturnType || shouldAttachReturnTypeDecl)
6588 				return;
6589 			else
6590 				continue;
6591 		}
6592 		// parse the line
6593 		for (size_t i = 0; i < line.length(); i++)
6594 		{
6595 			if (!isWhiteSpace(line[i]))
6596 			{
6597 				prevNonWSChar = currNonWSChar;
6598 				currNonWSChar = line[i];
6599 			}
6600 			else if (line[i] == '\t' && shouldConvertTabs)
6601 			{
6602 				size_t tabSize = getTabLength();
6603 				size_t numSpaces = tabSize - ((tabIncrementIn + i) % tabSize);
6604 				line.replace(i, 1, numSpaces, ' ');
6605 				currentChar = line[i];
6606 			}
6607 			if (line.compare(i, 2, "/*") == 0)
6608 				isInComment_ = true;
6609 			if (isInComment_)
6610 			{
6611 				if (line.compare(i, 2, "*/") == 0)
6612 				{
6613 					isInComment_ = false;
6614 					++i;
6615 				}
6616 				continue;
6617 			}
6618 			if (line[i] == '\\')
6619 			{
6620 				++i;
6621 				continue;
6622 			}
6623 
6624 			if (isInQuote_)
6625 			{
6626 				if (line[i] == quoteChar_)
6627 					isInQuote_ = false;
6628 				continue;
6629 			}
6630 
6631 			if (line[i] == '"'
6632 			        || (line[i] == '\'' && !isDigitSeparator(line, i)))
6633 			{
6634 				isInQuote_ = true;
6635 				quoteChar_ = line[i];
6636 				continue;
6637 			}
6638 			if (line.compare(i, 2, "//") == 0)
6639 			{
6640 				i = line.length();
6641 				continue;
6642 			}
6643 			// not in quote or comment
6644 			if (!foundSplitPoint)
6645 			{
6646 				if (line[i] == '<')
6647 				{
6648 					++angleCount;
6649 					continue;
6650 				}
6651 				if (line[i] == '>')
6652 				{
6653 					if (angleCount)
6654 						--angleCount;
6655 					if (!angleCount)
6656 					{
6657 						size_t nextCharNum = line.find_first_not_of(" \t*&", i + 1);
6658 						if (nextCharNum == string::npos)
6659 						{
6660 							breakCharNum  = string::npos;
6661 							continue;
6662 						}
6663 						if (line[nextCharNum] != ':')		// scope operator
6664 							breakCharNum  = nextCharNum;
6665 					}
6666 					continue;
6667 				}
6668 				if (angleCount)
6669 					continue;
6670 				if (line[i] == '[')
6671 				{
6672 					++squareCount;
6673 					continue;
6674 				}
6675 				if (line[i] == ']')
6676 				{
6677 					if (squareCount)
6678 						--squareCount;
6679 					continue;
6680 				}
6681 				// an assignment before the parens is not a function
6682 				if (line[i] == '=')
6683 					return;
6684 				if (isWhiteSpace(line[i]) || line[i] == '*' || line[i] == '&')
6685 				{
6686 					size_t nextNum = line.find_first_not_of(" \t", i + 1);
6687 					if (nextNum == string::npos)
6688 						breakCharNum = string::npos;
6689 					else
6690 					{
6691 						if (line.length() > nextNum + 1
6692 						        && line[nextNum] == ':' && line[nextNum + 1] == ':')
6693 							i = --nextNum;
6694 						else if (line[nextNum] != '(')
6695 							breakCharNum = string::npos;
6696 					}
6697 					continue;
6698 				}
6699 				if ((isLegalNameChar(line[i]) || line[i] == '~')
6700 				        && breakCharNum == string::npos)
6701 				{
6702 					breakCharNum = i;
6703 					if (isLegalNameChar(line[i])
6704 					        && findKeyword(line, i, AS_OPERATOR))
6705 					{
6706 						if (breakCharNum == firstCharNum)
6707 							isAlreadyBroken = true;
6708 						foundSplitPoint = true;
6709 						// find the operator, may be parens
6710 						size_t parenNum =
6711 						    line.find_first_not_of(" \t", i + AS_OPERATOR.length());
6712 						if (parenNum == string::npos)
6713 							return;
6714 						// find paren after the operator
6715 						parenNum = line.find('(', parenNum + 1);
6716 						if (parenNum == string::npos)
6717 							return;
6718 						i = --parenNum;
6719 					}
6720 					continue;
6721 				}
6722 				if (line[i] == ':'
6723 				        && line.length() > i + 1
6724 				        && line[i + 1] == ':')
6725 				{
6726 					size_t nextCharNum = line.find_first_not_of(" \t:", i + 1);
6727 					if (nextCharNum == string::npos)
6728 						return;
6729 
6730 					if (isLegalNameChar(line[nextCharNum])
6731 					        && findKeyword(line, nextCharNum, AS_OPERATOR))
6732 					{
6733 						i = nextCharNum;
6734 						if (breakCharNum == firstCharNum)
6735 							isAlreadyBroken = true;
6736 						foundSplitPoint = true;
6737 						// find the operator, may be parens
6738 						size_t parenNum =
6739 						    line.find_first_not_of(" \t", i + AS_OPERATOR.length());
6740 						if (parenNum == string::npos)
6741 							return;
6742 						// find paren after the operator
6743 						parenNum = line.find('(', parenNum + 1);
6744 						if (parenNum == string::npos)
6745 							return;
6746 						i = --parenNum;
6747 					}
6748 					else
6749 						i = --nextCharNum;
6750 					continue;
6751 				}
6752 				if (line[i] == '(' && !squareCount)
6753 				{
6754 					// is line is already broken?
6755 					if (breakCharNum == firstCharNum && breakLineNum > 0)
6756 						isAlreadyBroken = true;
6757 					++parenCount;
6758 					foundSplitPoint = true;
6759 					continue;
6760 				}
6761 			}
6762 			// end !foundSplitPoint
6763 			if (line[i] == '(')
6764 			{
6765 				// consecutive ')(' parens is probably a function pointer
6766 				if (prevNonWSChar == ')' && !parenCount)
6767 					return;
6768 				++parenCount;
6769 				continue;
6770 			}
6771 			if (line[i] == ')')
6772 			{
6773 				if (parenCount)
6774 					--parenCount;
6775 				continue;
6776 			}
6777 			if (line[i] == '{')
6778 			{
6779 				if (shouldBreakReturnType && foundSplitPoint && !isAlreadyBroken)
6780 				{
6781 					methodBreakCharNum = breakCharNum;
6782 					methodBreakLineNum = breakLineNum;
6783 				}
6784 				if (shouldAttachReturnType && foundSplitPoint && isAlreadyBroken)
6785 				{
6786 					methodAttachCharNum = breakCharNum;
6787 					methodAttachLineNum = breakLineNum;
6788 				}
6789 				return;
6790 			}
6791 			if (line[i] == ';')
6792 			{
6793 				if (shouldBreakReturnTypeDecl && foundSplitPoint && !isAlreadyBroken)
6794 				{
6795 					methodBreakCharNum = breakCharNum;
6796 					methodBreakLineNum = breakLineNum;
6797 				}
6798 				if ((shouldAttachReturnTypeDecl && foundSplitPoint && isAlreadyBroken))
6799 				{
6800 					methodAttachCharNum = breakCharNum;
6801 					methodAttachLineNum = breakLineNum;
6802 				}
6803 				return;
6804 			}
6805 			if (line[i] == '}')
6806 				return;
6807 		}   // end of for loop
6808 		if (!foundSplitPoint)
6809 			breakCharNum = string::npos;
6810 	}   // end of while loop
6811 }
6812 
6813 /**
6814  * Look ahead in the file to see if a struct has access modifiers.
6815  *
6816  * @param firstLine     a reference to the line to indent.
6817  * @param index         the current line index.
6818  * @return              true if the struct has access modifiers.
6819  */
isStructAccessModified(const string & firstLine,size_t index) const6820 bool ASFormatter::isStructAccessModified(const string& firstLine, size_t index) const
6821 {
6822 	assert(firstLine[index] == '{');
6823 	assert(isCStyle());
6824 
6825 	bool isFirstLine = true;
6826 	size_t braceCount = 1;
6827 	string nextLine_ = firstLine.substr(index + 1);
6828 	ASPeekStream stream(sourceIterator);
6829 
6830 	// find the first non-blank text, bypassing all comments and quotes.
6831 	bool isInComment_ = false;
6832 	bool isInQuote_ = false;
6833 	char quoteChar_ = ' ';
6834 	while (stream.hasMoreLines() || isFirstLine)
6835 	{
6836 		if (isFirstLine)
6837 			isFirstLine = false;
6838 		else
6839 			nextLine_ = stream.peekNextLine();
6840 		// parse the line
6841 		for (size_t i = 0; i < nextLine_.length(); i++)
6842 		{
6843 			if (isWhiteSpace(nextLine_[i]))
6844 				continue;
6845 			if (nextLine_.compare(i, 2, "/*") == 0)
6846 				isInComment_ = true;
6847 			if (isInComment_)
6848 			{
6849 				if (nextLine_.compare(i, 2, "*/") == 0)
6850 				{
6851 					isInComment_ = false;
6852 					++i;
6853 				}
6854 				continue;
6855 			}
6856 			if (nextLine_[i] == '\\')
6857 			{
6858 				++i;
6859 				continue;
6860 			}
6861 
6862 			if (isInQuote_)
6863 			{
6864 				if (nextLine_[i] == quoteChar_)
6865 					isInQuote_ = false;
6866 				continue;
6867 			}
6868 
6869 			if (nextLine_[i] == '"'
6870 			        || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
6871 			{
6872 				isInQuote_ = true;
6873 				quoteChar_ = nextLine_[i];
6874 				continue;
6875 			}
6876 			if (nextLine_.compare(i, 2, "//") == 0)
6877 			{
6878 				i = nextLine_.length();
6879 				continue;
6880 			}
6881 			// handle braces
6882 			if (nextLine_[i] == '{')
6883 				++braceCount;
6884 			if (nextLine_[i] == '}')
6885 				--braceCount;
6886 			if (braceCount == 0)
6887 				return false;
6888 			// check for access modifiers
6889 			if (isCharPotentialHeader(nextLine_, i))
6890 			{
6891 				if (findKeyword(nextLine_, i, AS_PUBLIC)
6892 				        || findKeyword(nextLine_, i, AS_PRIVATE)
6893 				        || findKeyword(nextLine_, i, AS_PROTECTED))
6894 					return true;
6895 				string name = getCurrentWord(nextLine_, i);
6896 				i += name.length() - 1;
6897 			}
6898 		}	// end of for loop
6899 	}	// end of while loop
6900 
6901 	return false;
6902 }
6903 
6904 /**
6905 * Look ahead in the file to see if a preprocessor block is indentable.
6906 *
6907 * @param firstLine     a reference to the line to indent.
6908 * @param index         the current line index.
6909 * @return              true if the block is indentable.
6910 */
isIndentablePreprocessorBlock(const string & firstLine,size_t index)6911 bool ASFormatter::isIndentablePreprocessorBlock(const string& firstLine, size_t index)
6912 {
6913 	assert(firstLine[index] == '#');
6914 
6915 	bool isFirstLine = true;
6916 	bool isInIndentableBlock = false;
6917 	bool blockContainsBraces = false;
6918 	bool blockContainsDefineContinuation = false;
6919 	bool isInClassConstructor = false;
6920 	bool isPotentialHeaderGuard = false;	// ifndef is first preproc statement
6921 	bool isPotentialHeaderGuard2 = false;	// define is within the first proproc
6922 	int  numBlockIndents = 0;
6923 	int  lineParenCount = 0;
6924 	string nextLine_ = firstLine.substr(index);
6925 	auto stream = make_shared<ASPeekStream>(sourceIterator);
6926 
6927 	// find end of the block, bypassing all comments and quotes.
6928 	bool isInComment_ = false;
6929 	bool isInQuote_ = false;
6930 	char quoteChar_ = ' ';
6931 	while (stream->hasMoreLines() || isFirstLine)
6932 	{
6933 		if (isFirstLine)
6934 			isFirstLine = false;
6935 		else
6936 			nextLine_ = stream->peekNextLine();
6937 		// parse the line
6938 		for (size_t i = 0; i < nextLine_.length(); i++)
6939 		{
6940 			if (isWhiteSpace(nextLine_[i]))
6941 				continue;
6942 			if (nextLine_.compare(i, 2, "/*") == 0)
6943 				isInComment_ = true;
6944 			if (isInComment_)
6945 			{
6946 				if (nextLine_.compare(i, 2, "*/") == 0)
6947 				{
6948 					isInComment_ = false;
6949 					++i;
6950 				}
6951 				continue;
6952 			}
6953 			if (nextLine_[i] == '\\')
6954 			{
6955 				++i;
6956 				continue;
6957 			}
6958 			if (isInQuote_)
6959 			{
6960 				if (nextLine_[i] == quoteChar_)
6961 					isInQuote_ = false;
6962 				continue;
6963 			}
6964 
6965 			if (nextLine_[i] == '"'
6966 			        || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
6967 			{
6968 				isInQuote_ = true;
6969 				quoteChar_ = nextLine_[i];
6970 				continue;
6971 			}
6972 			if (nextLine_.compare(i, 2, "//") == 0)
6973 			{
6974 				i = nextLine_.length();
6975 				continue;
6976 			}
6977 			// handle preprocessor statement
6978 			if (nextLine_[i] == '#')
6979 			{
6980 				string preproc = ASBeautifier::extractPreprocessorStatement(nextLine_);
6981 				if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef
6982 				{
6983 					numBlockIndents += 1;
6984 					isInIndentableBlock = true;
6985 					// flag first preprocessor conditional for header include guard check
6986 					if (!processedFirstConditional)
6987 					{
6988 						processedFirstConditional = true;
6989 						isFirstPreprocConditional = true;
6990 						if (isNDefPreprocStatement(nextLine_, preproc))
6991 							isPotentialHeaderGuard = true;
6992 					}
6993 				}
6994 				else if (preproc == "endif")
6995 				{
6996 					if (numBlockIndents > 0)
6997 						numBlockIndents -= 1;
6998 					// must exit BOTH loops
6999 					if (numBlockIndents == 0)
7000 						goto EndOfWhileLoop;
7001 				}
7002 				else if (preproc == "define")
7003 				{
7004 					if (nextLine_[nextLine_.length() - 1] == '\\')
7005 						blockContainsDefineContinuation = true;
7006 					// check for potential header include guards
7007 					else if (isPotentialHeaderGuard && numBlockIndents == 1)
7008 						isPotentialHeaderGuard2 = true;
7009 				}
7010 				i = nextLine_.length();
7011 				continue;
7012 			}
7013 			// handle exceptions
7014 			if (nextLine_[i] == '{' || nextLine_[i] == '}')
7015 				blockContainsBraces = true;
7016 			else if (nextLine_[i] == '(')
7017 				++lineParenCount;
7018 			else if (nextLine_[i] == ')')
7019 				--lineParenCount;
7020 			else if (nextLine_[i] == ':')
7021 			{
7022 				// check for '::'
7023 				if (nextLine_.length() > i + 1 && nextLine_[i + 1] == ':')
7024 					++i;
7025 				else
7026 					isInClassConstructor = true;
7027 			}
7028 			// bypass unnecessary parsing - must exit BOTH loops
7029 			if (blockContainsBraces || isInClassConstructor || blockContainsDefineContinuation)
7030 				goto EndOfWhileLoop;
7031 		}	// end of for loop, end of line
7032 		if (lineParenCount != 0)
7033 			break;
7034 	}	// end of while loop
7035 EndOfWhileLoop:
7036 	preprocBlockEnd = sourceIterator->tellg();
7037 	if (preprocBlockEnd < 0)
7038 		preprocBlockEnd = sourceIterator->getStreamLength();
7039 	if (blockContainsBraces
7040 	        || isInClassConstructor
7041 	        || blockContainsDefineContinuation
7042 	        || lineParenCount != 0
7043 	        || numBlockIndents != 0)
7044 		isInIndentableBlock = false;
7045 	// find next executable instruction
7046 	// this WILL RESET the get pointer
7047 	string nextText = peekNextText("", false, stream);
7048 	// bypass header include guards
7049 	if (isFirstPreprocConditional)
7050 	{
7051 		isFirstPreprocConditional = false;
7052 		if (nextText.empty() && isPotentialHeaderGuard2)
7053 		{
7054 			isInIndentableBlock = false;
7055 			preprocBlockEnd = 0;
7056 		}
7057 	}
7058 	// this allows preprocessor blocks within this block to be indented
7059 	if (!isInIndentableBlock)
7060 		preprocBlockEnd = 0;
7061 	// peekReset() is done by previous peekNextText()
7062 	return isInIndentableBlock;
7063 }
7064 
isNDefPreprocStatement(const string & nextLine_,const string & preproc) const7065 bool ASFormatter::isNDefPreprocStatement(const string& nextLine_, const string& preproc) const
7066 {
7067 	if (preproc == "ifndef")
7068 		return true;
7069 	// check for '!defined'
7070 	if (preproc == "if")
7071 	{
7072 		size_t i = nextLine_.find('!');
7073 		if (i == string::npos)
7074 			return false;
7075 		i = nextLine_.find_first_not_of(" \t", ++i);
7076 		if (i != string::npos && nextLine_.compare(i, 7, "defined") == 0)
7077 			return true;
7078 	}
7079 	return false;
7080 }
7081 
7082 /**
7083  * Check to see if this is an EXEC SQL statement.
7084  *
7085  * @param line          a reference to the line to indent.
7086  * @param index         the current line index.
7087  * @return              true if the statement is EXEC SQL.
7088  */
isExecSQL(const string & line,size_t index) const7089 bool ASFormatter::isExecSQL(const string& line, size_t index) const
7090 {
7091 	if (line[index] != 'e' && line[index] != 'E')	// quick check to reject most
7092 		return false;
7093 	string word;
7094 	if (isCharPotentialHeader(line, index))
7095 		word = getCurrentWord(line, index);
7096 	for (size_t i = 0; i < word.length(); i++)
7097 		word[i] = (char) toupper(word[i]);
7098 	if (word != "EXEC")
7099 		return false;
7100 	size_t index2 = index + word.length();
7101 	index2 = line.find_first_not_of(" \t", index2);
7102 	if (index2 == string::npos)
7103 		return false;
7104 	word.erase();
7105 	if (isCharPotentialHeader(line, index2))
7106 		word = getCurrentWord(line, index2);
7107 	for (size_t i = 0; i < word.length(); i++)
7108 		word[i] = (char) toupper(word[i]);
7109 	if (word != "SQL")
7110 		return false;
7111 	return true;
7112 }
7113 
7114 /**
7115  * The continuation lines must be adjusted so the leading spaces
7116  *     is equivalent to the text on the opening line.
7117  *
7118  * Updates currentLine and charNum.
7119  */
trimContinuationLine()7120 void ASFormatter::trimContinuationLine()
7121 {
7122 	size_t len = currentLine.length();
7123 	size_t tabSize = getTabLength();
7124 	charNum = 0;
7125 
7126 	if (leadingSpaces > 0 && len > 0)
7127 	{
7128 		size_t i;
7129 		size_t continuationIncrementIn = 0;
7130 		for (i = 0; (i < len) && (i + continuationIncrementIn < leadingSpaces); i++)
7131 		{
7132 			if (!isWhiteSpace(currentLine[i]))		// don't delete any text
7133 			{
7134 				if (i < continuationIncrementIn)
7135 					leadingSpaces = i + tabIncrementIn;
7136 				continuationIncrementIn = tabIncrementIn;
7137 				break;
7138 			}
7139 			if (currentLine[i] == '\t')
7140 				continuationIncrementIn += tabSize - 1 - ((continuationIncrementIn + i) % tabSize);
7141 		}
7142 
7143 		if ((int) continuationIncrementIn == tabIncrementIn)
7144 			charNum = i;
7145 		else
7146 		{
7147 			// build a new line with the equivalent leading chars
7148 			string newLine;
7149 			int leadingChars = 0;
7150 			if ((int) leadingSpaces > tabIncrementIn)
7151 				leadingChars = leadingSpaces - tabIncrementIn;
7152 			newLine.append(leadingChars, ' ');
7153 			newLine.append(currentLine, i, len - i);
7154 			currentLine = newLine;
7155 			charNum = leadingChars;
7156 			if (currentLine.length() == 0)
7157 				currentLine = string(" ");        // a null is inserted if this is not done
7158 		}
7159 		if (i >= len)
7160 			charNum = 0;
7161 	}
7162 }
7163 
7164 /**
7165  * Determine if a header is a closing header
7166  *
7167  * @return      true if the header is a closing header.
7168  */
isClosingHeader(const string * header) const7169 bool ASFormatter::isClosingHeader(const string* header) const
7170 {
7171 	return (header == &AS_ELSE
7172 	        || header == &AS_CATCH
7173 	        || header == &AS_FINALLY);
7174 }
7175 
7176 /**
7177  * Determine if a * following a closing paren is immediately.
7178  * after a cast. If so it is a deference and not a multiply.
7179  * e.g. "(int*) *ptr" is a deference.
7180  */
isImmediatelyPostCast() const7181 bool ASFormatter::isImmediatelyPostCast() const
7182 {
7183 	assert(previousNonWSChar == ')' && currentChar == '*');
7184 	// find preceding closing paren on currentLine or readyFormattedLine
7185 	string line;		// currentLine or readyFormattedLine
7186 	size_t paren = currentLine.rfind(')', charNum);
7187 	if (paren != string::npos)
7188 		line = currentLine;
7189 	// if not on currentLine it must be on the previous line
7190 	else
7191 	{
7192 		line = readyFormattedLine;
7193 		paren = line.rfind(')');
7194 		if (paren == string::npos)
7195 			return false;
7196 	}
7197 	if (paren == 0)
7198 		return false;
7199 
7200 	// find character preceding the closing paren
7201 	size_t lastChar = line.find_last_not_of(" \t", paren - 1);
7202 	if (lastChar == string::npos)
7203 		return false;
7204 	// check for pointer cast
7205 	if (line[lastChar] == '*')
7206 		return true;
7207 	return false;
7208 }
7209 
7210 /**
7211  * Determine if a < is a template definition or instantiation.
7212  * Sets the class variables isInTemplate and templateDepth.
7213  */
checkIfTemplateOpener()7214 void ASFormatter::checkIfTemplateOpener()
7215 {
7216 	assert(!isInTemplate && currentChar == '<');
7217 
7218 	// find first char after the '<' operators
7219 	size_t firstChar = currentLine.find_first_not_of("< \t", charNum);
7220 	if (firstChar == string::npos
7221 	        || currentLine[firstChar] == '=')
7222 	{
7223 		// this is not a template -> leave...
7224 		isInTemplate = false;
7225 		return;
7226 	}
7227 
7228 	bool isFirstLine = true;
7229 	int parenDepth_ = 0;
7230 	int maxTemplateDepth = 0;
7231 	templateDepth = 0;
7232 	string nextLine_ = currentLine.substr(charNum);
7233 	ASPeekStream stream(sourceIterator);
7234 
7235 	// find the angle braces, bypassing all comments and quotes.
7236 	bool isInComment_ = false;
7237 	bool isInQuote_ = false;
7238 	char quoteChar_ = ' ';
7239 	while (stream.hasMoreLines() || isFirstLine)
7240 	{
7241 		if (isFirstLine)
7242 			isFirstLine = false;
7243 		else
7244 			nextLine_ = stream.peekNextLine();
7245 		// parse the line
7246 		for (size_t i = 0; i < nextLine_.length(); i++)
7247 		{
7248 			char currentChar_ = nextLine_[i];
7249 			if (isWhiteSpace(currentChar_))
7250 				continue;
7251 			if (nextLine_.compare(i, 2, "/*") == 0)
7252 				isInComment_ = true;
7253 			if (isInComment_)
7254 			{
7255 				if (nextLine_.compare(i, 2, "*/") == 0)
7256 				{
7257 					isInComment_ = false;
7258 					++i;
7259 				}
7260 				continue;
7261 			}
7262 			if (currentChar_ == '\\')
7263 			{
7264 				++i;
7265 				continue;
7266 			}
7267 
7268 			if (isInQuote_)
7269 			{
7270 				if (currentChar_ == quoteChar_)
7271 					isInQuote_ = false;
7272 				continue;
7273 			}
7274 
7275 			if (currentChar_ == '"'
7276 			        || (currentChar_ == '\'' && !isDigitSeparator(nextLine_, i)))
7277 			{
7278 				isInQuote_ = true;
7279 				quoteChar_ = currentChar_;
7280 				continue;
7281 			}
7282 			if (nextLine_.compare(i, 2, "//") == 0)
7283 			{
7284 				i = nextLine_.length();
7285 				continue;
7286 			}
7287 
7288 			// not in a comment or quote
7289 			if (currentChar_ == '<')
7290 			{
7291 				++templateDepth;
7292 				++maxTemplateDepth;
7293 				continue;
7294 			}
7295 			else if (currentChar_ == '>')
7296 			{
7297 				--templateDepth;
7298 				if (templateDepth == 0)
7299 				{
7300 					if (parenDepth_ == 0)
7301 					{
7302 						// this is a template!
7303 						isInTemplate = true;
7304 						templateDepth = maxTemplateDepth;
7305 					}
7306 					return;
7307 				}
7308 				continue;
7309 			}
7310 			else if (currentChar_ == '(' || currentChar_ == ')')
7311 			{
7312 				if (currentChar_ == '(')
7313 					++parenDepth_;
7314 				else
7315 					--parenDepth_;
7316 				if (parenDepth_ >= 0)
7317 					continue;
7318 				// this is not a template -> leave...
7319 				isInTemplate = false;
7320 				templateDepth = 0;
7321 				return;
7322 			}
7323 			else if (nextLine_.compare(i, 2, AS_AND) == 0
7324 			         || nextLine_.compare(i, 2, AS_OR) == 0)
7325 			{
7326 				// this is not a template -> leave...
7327 				isInTemplate = false;
7328 				templateDepth = 0;
7329 				return;
7330 			}
7331 			else if (currentChar_ == ','  // comma,     e.g. A<int, char>
7332 			         || currentChar_ == '&'    // reference, e.g. A<int&>
7333 			         || currentChar_ == '*'    // pointer,   e.g. A<int*>
7334 			         || currentChar_ == '^'    // C++/CLI managed pointer, e.g. A<int^>
7335 			         || currentChar_ == ':'    // ::,        e.g. std::string
7336 			         || currentChar_ == '='    // assign     e.g. default parameter
7337 			         || currentChar_ == '['    // []         e.g. string[]
7338 			         || currentChar_ == ']'    // []         e.g. string[]
7339 			         || currentChar_ == '('    // (...)      e.g. function definition
7340 			         || currentChar_ == ')'    // (...)      e.g. function definition
7341 			         || (isJavaStyle() && currentChar_ == '?')   // Java wildcard
7342 			        )
7343 			{
7344 				continue;
7345 			}
7346 			else if (!isLegalNameChar(currentChar_))
7347 			{
7348 				// this is not a template -> leave...
7349 				isInTemplate = false;
7350 				templateDepth = 0;
7351 				return;
7352 			}
7353 			string name = getCurrentWord(nextLine_, i);
7354 			i += name.length() - 1;
7355 		}	// end for loop
7356 	}	// end while loop
7357 }
7358 
updateFormattedLineSplitPoints(char appendedChar)7359 void ASFormatter::updateFormattedLineSplitPoints(char appendedChar)
7360 {
7361 	assert(maxCodeLength != string::npos);
7362 	assert(formattedLine.length() > 0);
7363 
7364 	if (!isOkToSplitFormattedLine())
7365 		return;
7366 
7367 	char nextChar = peekNextChar();
7368 
7369 	// don't split before an end of line comment
7370 	if (nextChar == '/')
7371 		return;
7372 
7373 	// don't split before or after a brace
7374 	if (appendedChar == '{' || appendedChar == '}'
7375 	        || previousNonWSChar == '{' || previousNonWSChar == '}'
7376 	        || nextChar == '{' || nextChar == '}'
7377 	        || currentChar == '{' || currentChar == '}')	// currentChar tests for an appended brace
7378 		return;
7379 
7380 	// don't split before or after a block paren
7381 	if (appendedChar == '[' || appendedChar == ']'
7382 	        || previousNonWSChar == '['
7383 	        || nextChar == '[' || nextChar == ']')
7384 		return;
7385 
7386 	if (isWhiteSpace(appendedChar))
7387 	{
7388 		if (nextChar != ')'						// space before a closing paren
7389 		        && nextChar != '('				// space before an opening paren
7390 		        && nextChar != '/'				// space before a comment
7391 		        && nextChar != ':'				// space before a colon
7392 		        && currentChar != ')'			// appended space before and after a closing paren
7393 		        && currentChar != '('			// appended space before and after a opening paren
7394 		        && previousNonWSChar != '('		// decided at the '('
7395 		        // don't break before a pointer or reference aligned to type
7396 		        && !(nextChar == '*'
7397 		             && !isCharPotentialOperator(previousNonWSChar)
7398 		             && pointerAlignment == PTR_ALIGN_TYPE)
7399 		        && !(nextChar == '&'
7400 		             && !isCharPotentialOperator(previousNonWSChar)
7401 		             && (referenceAlignment == REF_ALIGN_TYPE
7402 		                 || (referenceAlignment == REF_SAME_AS_PTR && pointerAlignment == PTR_ALIGN_TYPE)))
7403 		   )
7404 		{
7405 			if (formattedLine.length() - 1 <= maxCodeLength)
7406 				maxWhiteSpace = formattedLine.length() - 1;
7407 			else
7408 				maxWhiteSpacePending = formattedLine.length() - 1;
7409 		}
7410 	}
7411 	// unpadded closing parens may split after the paren (counts as whitespace)
7412 	else if (appendedChar == ')')
7413 	{
7414 		if (nextChar != ')'
7415 		        && nextChar != ' '
7416 		        && nextChar != ';'
7417 		        && nextChar != ','
7418 		        && nextChar != '.'
7419 		        && !(nextChar == '-' && pointerSymbolFollows()))	// check for ->
7420 		{
7421 			if (formattedLine.length() <= maxCodeLength)
7422 				maxWhiteSpace = formattedLine.length();
7423 			else
7424 				maxWhiteSpacePending = formattedLine.length();
7425 		}
7426 	}
7427 	// unpadded commas may split after the comma
7428 	else if (appendedChar == ',')
7429 	{
7430 		if (formattedLine.length() <= maxCodeLength)
7431 			maxComma = formattedLine.length();
7432 		else
7433 			maxCommaPending = formattedLine.length();
7434 	}
7435 	else if (appendedChar == '(')
7436 	{
7437 		if (nextChar != ')' && nextChar != '(' && nextChar != '"' && nextChar != '\'')
7438 		{
7439 			// if follows an operator break before
7440 			size_t parenNum;
7441 			if (previousNonWSChar != ' ' && isCharPotentialOperator(previousNonWSChar))
7442 				parenNum = formattedLine.length() - 1;
7443 			else
7444 				parenNum = formattedLine.length();
7445 			if (formattedLine.length() <= maxCodeLength)
7446 				maxParen = parenNum;
7447 			else
7448 				maxParenPending = parenNum;
7449 		}
7450 	}
7451 	else if (appendedChar == ';')
7452 	{
7453 		if (nextChar != ' '  && nextChar != '}' && nextChar != '/')	// check for following comment
7454 		{
7455 			if (formattedLine.length() <= maxCodeLength)
7456 				maxSemi = formattedLine.length();
7457 			else
7458 				maxSemiPending = formattedLine.length();
7459 		}
7460 	}
7461 }
7462 
updateFormattedLineSplitPointsOperator(const string & sequence)7463 void ASFormatter::updateFormattedLineSplitPointsOperator(const string& sequence)
7464 {
7465 	assert(maxCodeLength != string::npos);
7466 	assert(formattedLine.length() > 0);
7467 
7468 	if (!isOkToSplitFormattedLine())
7469 		return;
7470 
7471 	char nextChar = peekNextChar();
7472 
7473 	// don't split before an end of line comment
7474 	if (nextChar == '/')
7475 		return;
7476 
7477 	// check for logical conditional
7478 	if (sequence == "||" || sequence == "&&" || sequence == "or" || sequence == "and")
7479 	{
7480 		if (shouldBreakLineAfterLogical)
7481 		{
7482 			if (formattedLine.length() <= maxCodeLength)
7483 				maxAndOr = formattedLine.length();
7484 			else
7485 				maxAndOrPending = formattedLine.length();
7486 		}
7487 		else
7488 		{
7489 			// adjust for leading space in the sequence
7490 			size_t sequenceLength = sequence.length();
7491 			if (formattedLine.length() > sequenceLength
7492 			        && isWhiteSpace(formattedLine[formattedLine.length() - sequenceLength - 1]))
7493 				sequenceLength++;
7494 			if (formattedLine.length() - sequenceLength <= maxCodeLength)
7495 				maxAndOr = formattedLine.length() - sequenceLength;
7496 			else
7497 				maxAndOrPending = formattedLine.length() - sequenceLength;
7498 		}
7499 	}
7500 	// comparison operators will split after the operator (counts as whitespace)
7501 	else if (sequence == "==" || sequence == "!=" || sequence == ">=" || sequence == "<=")
7502 	{
7503 		if (formattedLine.length() <= maxCodeLength)
7504 			maxWhiteSpace = formattedLine.length();
7505 		else
7506 			maxWhiteSpacePending = formattedLine.length();
7507 	}
7508 	// unpadded operators that will split BEFORE the operator (counts as whitespace)
7509 	else if (sequence == "+" || sequence == "-" || sequence == "?")
7510 	{
7511 		if (charNum > 0
7512 		        && !(sequence == "+" && isInExponent())
7513 		        && !(sequence == "-"  && isInExponent())
7514 		        && (isLegalNameChar(currentLine[charNum - 1])
7515 		            || currentLine[charNum - 1] == ')'
7516 		            || currentLine[charNum - 1] == ']'
7517 		            || currentLine[charNum - 1] == '\"'))
7518 		{
7519 			if (formattedLine.length() - 1 <= maxCodeLength)
7520 				maxWhiteSpace = formattedLine.length() - 1;
7521 			else
7522 				maxWhiteSpacePending = formattedLine.length() - 1;
7523 		}
7524 	}
7525 	// unpadded operators that will USUALLY split AFTER the operator (counts as whitespace)
7526 	else if (sequence == "=" || sequence == ":")
7527 	{
7528 		// split BEFORE if the line is too long
7529 		// do NOT use <= here, must allow for a brace attached to an array
7530 		size_t splitPoint = 0;
7531 		if (formattedLine.length() < maxCodeLength)
7532 			splitPoint = formattedLine.length();
7533 		else
7534 			splitPoint = formattedLine.length() - 1;
7535 		// padded or unpadded arrays
7536 		if (previousNonWSChar == ']')
7537 		{
7538 			if (formattedLine.length() - 1 <= maxCodeLength)
7539 				maxWhiteSpace = splitPoint;
7540 			else
7541 				maxWhiteSpacePending = splitPoint;
7542 		}
7543 		else if (charNum > 0
7544 		         && (isLegalNameChar(currentLine[charNum - 1])
7545 		             || currentLine[charNum - 1] == ')'
7546 		             || currentLine[charNum - 1] == ']'))
7547 		{
7548 			if (formattedLine.length() <= maxCodeLength)
7549 				maxWhiteSpace = splitPoint;
7550 			else
7551 				maxWhiteSpacePending = splitPoint;
7552 		}
7553 	}
7554 }
7555 
7556 /**
7557  * Update the split point when a pointer or reference is formatted.
7558  * The argument is the maximum index of the last whitespace character.
7559  */
updateFormattedLineSplitPointsPointerOrReference(size_t index)7560 void ASFormatter::updateFormattedLineSplitPointsPointerOrReference(size_t index)
7561 {
7562 	assert(maxCodeLength != string::npos);
7563 	assert(formattedLine.length() > 0);
7564 	assert(index < formattedLine.length());
7565 
7566 	if (!isOkToSplitFormattedLine())
7567 		return;
7568 
7569 	if (index < maxWhiteSpace)		// just in case
7570 		return;
7571 
7572 	if (index <= maxCodeLength)
7573 		maxWhiteSpace = index;
7574 	else
7575 		maxWhiteSpacePending = index;
7576 }
7577 
isOkToSplitFormattedLine()7578 bool ASFormatter::isOkToSplitFormattedLine()
7579 {
7580 	assert(maxCodeLength != string::npos);
7581 	// Is it OK to split the line?
7582 	if (shouldKeepLineUnbroken
7583 	        || isInLineComment
7584 	        || isInComment
7585 	        || isInQuote
7586 	        || isInCase
7587 	        || isInPreprocessor
7588 	        || isInExecSQL
7589 	        || isInAsm || isInAsmOneLine || isInAsmBlock
7590 	        || isInTemplate)
7591 		return false;
7592 
7593 	if (!isOkToBreakBlock(braceTypeStack->back()) && currentChar != '{')
7594 	{
7595 		shouldKeepLineUnbroken = true;
7596 		clearFormattedLineSplitPoints();
7597 		return false;
7598 	}
7599 	if (isBraceType(braceTypeStack->back(), ARRAY_TYPE))
7600 	{
7601 		shouldKeepLineUnbroken = true;
7602 		if (!isBraceType(braceTypeStack->back(), ARRAY_NIS_TYPE))
7603 			clearFormattedLineSplitPoints();
7604 		return false;
7605 	}
7606 	return true;
7607 }
7608 
7609 /* This is called if the option maxCodeLength is set.
7610  */
testForTimeToSplitFormattedLine()7611 void ASFormatter::testForTimeToSplitFormattedLine()
7612 {
7613 	//	DO NOT ASSERT maxCodeLength HERE
7614 	// should the line be split
7615 	if (formattedLine.length() > maxCodeLength && !isLineReady)
7616 	{
7617 		size_t splitPoint = findFormattedLineSplitPoint();
7618 		if (splitPoint > 0 && splitPoint < formattedLine.length())
7619 		{
7620 			string splitLine = formattedLine.substr(splitPoint);
7621 			formattedLine = formattedLine.substr(0, splitPoint);
7622 			breakLine(true);
7623 			formattedLine = splitLine;
7624 			// if break-blocks is requested and this is a one-line statement
7625 			string nextWord = ASBeautifier::getNextWord(currentLine, charNum - 1);
7626 			if (isAppendPostBlockEmptyLineRequested
7627 			        && (nextWord == "break" || nextWord == "continue"))
7628 			{
7629 				isAppendPostBlockEmptyLineRequested = false;
7630 				isPrependPostBlockEmptyLineRequested = true;
7631 			}
7632 			else
7633 				isPrependPostBlockEmptyLineRequested = false;
7634 			// adjust max split points
7635 			maxAndOr = (maxAndOr > splitPoint) ? (maxAndOr - splitPoint) : 0;
7636 			maxSemi = (maxSemi > splitPoint) ? (maxSemi - splitPoint) : 0;
7637 			maxComma = (maxComma > splitPoint) ? (maxComma - splitPoint) : 0;
7638 			maxParen = (maxParen > splitPoint) ? (maxParen - splitPoint) : 0;
7639 			maxWhiteSpace = (maxWhiteSpace > splitPoint) ? (maxWhiteSpace - splitPoint) : 0;
7640 			if (maxSemiPending > 0)
7641 			{
7642 				maxSemi = (maxSemiPending > splitPoint) ? (maxSemiPending - splitPoint) : 0;
7643 				maxSemiPending = 0;
7644 			}
7645 			if (maxAndOrPending > 0)
7646 			{
7647 				maxAndOr = (maxAndOrPending > splitPoint) ? (maxAndOrPending - splitPoint) : 0;
7648 				maxAndOrPending = 0;
7649 			}
7650 			if (maxCommaPending > 0)
7651 			{
7652 				maxComma = (maxCommaPending > splitPoint) ? (maxCommaPending - splitPoint) : 0;
7653 				maxCommaPending = 0;
7654 			}
7655 			if (maxParenPending > 0)
7656 			{
7657 				maxParen = (maxParenPending > splitPoint) ? (maxParenPending - splitPoint) : 0;
7658 				maxParenPending = 0;
7659 			}
7660 			if (maxWhiteSpacePending > 0)
7661 			{
7662 				maxWhiteSpace = (maxWhiteSpacePending > splitPoint) ? (maxWhiteSpacePending - splitPoint) : 0;
7663 				maxWhiteSpacePending = 0;
7664 			}
7665 			// don't allow an empty formatted line
7666 			size_t firstText = formattedLine.find_first_not_of(" \t");
7667 			if (firstText == string::npos && formattedLine.length() > 0)
7668 			{
7669 				formattedLine.erase();
7670 				clearFormattedLineSplitPoints();
7671 				if (isWhiteSpace(currentChar))
7672 					for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++)
7673 						goForward(1);
7674 			}
7675 			else if (firstText > 0)
7676 			{
7677 				formattedLine.erase(0, firstText);
7678 				maxSemi = (maxSemi > firstText) ? (maxSemi - firstText) : 0;
7679 				maxAndOr = (maxAndOr > firstText) ? (maxAndOr - firstText) : 0;
7680 				maxComma = (maxComma > firstText) ? (maxComma - firstText) : 0;
7681 				maxParen = (maxParen > firstText) ? (maxParen - firstText) : 0;
7682 				maxWhiteSpace = (maxWhiteSpace > firstText) ? (maxWhiteSpace - firstText) : 0;
7683 			}
7684 			// reset formattedLineCommentNum
7685 			if (formattedLineCommentNum != string::npos)
7686 			{
7687 				formattedLineCommentNum = formattedLine.find("//");
7688 				if (formattedLineCommentNum == string::npos)
7689 					formattedLineCommentNum = formattedLine.find("/*");
7690 			}
7691 		}
7692 	}
7693 }
7694 
findFormattedLineSplitPoint() const7695 size_t ASFormatter::findFormattedLineSplitPoint() const
7696 {
7697 	assert(maxCodeLength != string::npos);
7698 	// determine where to split
7699 	size_t minCodeLength = 10;
7700 	size_t splitPoint = 0;
7701 	splitPoint = maxSemi;
7702 	if (maxAndOr >= minCodeLength)
7703 		splitPoint = maxAndOr;
7704 	if (splitPoint < minCodeLength)
7705 	{
7706 		splitPoint = maxWhiteSpace;
7707 		// use maxParen instead if it is long enough
7708 		if (maxParen > splitPoint
7709 		        || maxParen >= maxCodeLength * .7)
7710 			splitPoint = maxParen;
7711 		// use maxComma instead if it is long enough
7712 		// increasing the multiplier causes more splits at whitespace
7713 		if (maxComma > splitPoint
7714 		        || maxComma >= maxCodeLength * .3)
7715 			splitPoint = maxComma;
7716 	}
7717 	// replace split point with first available break point
7718 	if (splitPoint < minCodeLength)
7719 	{
7720 		splitPoint = string::npos;
7721 		if (maxSemiPending > 0 && maxSemiPending < splitPoint)
7722 			splitPoint = maxSemiPending;
7723 		if (maxAndOrPending > 0 && maxAndOrPending < splitPoint)
7724 			splitPoint = maxAndOrPending;
7725 		if (maxCommaPending > 0 && maxCommaPending < splitPoint)
7726 			splitPoint = maxCommaPending;
7727 		if (maxParenPending > 0 && maxParenPending < splitPoint)
7728 			splitPoint = maxParenPending;
7729 		if (maxWhiteSpacePending > 0 && maxWhiteSpacePending < splitPoint)
7730 			splitPoint = maxWhiteSpacePending;
7731 		if (splitPoint == string::npos)
7732 			splitPoint = 0;
7733 	}
7734 	// if remaining line after split is too long
7735 	else if (formattedLine.length() - splitPoint > maxCodeLength)
7736 	{
7737 		// if end of the currentLine, find a new split point
7738 		size_t newCharNum;
7739 		if (!isWhiteSpace(currentChar) && isCharPotentialHeader(currentLine, charNum))
7740 			newCharNum = getCurrentWord(currentLine, charNum).length() + charNum;
7741 		else
7742 			newCharNum = charNum + 2;
7743 		if (newCharNum + 1 > currentLine.length())
7744 		{
7745 			// don't move splitPoint from before a conditional to after
7746 			if (maxWhiteSpace > splitPoint + 3)
7747 				splitPoint = maxWhiteSpace;
7748 			if (maxParen > splitPoint)
7749 				splitPoint = maxParen;
7750 		}
7751 	}
7752 
7753 	return splitPoint;
7754 }
7755 
clearFormattedLineSplitPoints()7756 void ASFormatter::clearFormattedLineSplitPoints()
7757 {
7758 	maxSemi = 0;
7759 	maxAndOr = 0;
7760 	maxComma = 0;
7761 	maxParen = 0;
7762 	maxWhiteSpace = 0;
7763 	maxSemiPending = 0;
7764 	maxAndOrPending = 0;
7765 	maxCommaPending = 0;
7766 	maxParenPending = 0;
7767 	maxWhiteSpacePending = 0;
7768 }
7769 
7770 /**
7771  * Check if a pointer symbol (->) follows on the currentLine.
7772  */
pointerSymbolFollows() const7773 bool ASFormatter::pointerSymbolFollows() const
7774 {
7775 	size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1);
7776 	if (peekNum == string::npos || currentLine.compare(peekNum, 2, "->") != 0)
7777 		return false;
7778 	return true;
7779 }
7780 
7781 /**
7782  * Compute the input checksum.
7783  * This is called as an assert so it for is debug config only
7784  */
computeChecksumIn(const string & currentLine_)7785 bool ASFormatter::computeChecksumIn(const string& currentLine_)
7786 {
7787 	for (size_t i = 0; i < currentLine_.length(); i++)
7788 		if (!isWhiteSpace(currentLine_[i]))
7789 			checksumIn += currentLine_[i];
7790 	return true;
7791 }
7792 
7793 /**
7794  * Adjust the input checksum for deleted chars.
7795  * This is called as an assert so it for is debug config only
7796  */
adjustChecksumIn(int adjustment)7797 bool ASFormatter::adjustChecksumIn(int adjustment)
7798 {
7799 	checksumIn += adjustment;
7800 	return true;
7801 }
7802 
7803 /**
7804  * get the value of checksumIn for unit testing
7805  *
7806  * @return   checksumIn.
7807  */
getChecksumIn() const7808 size_t ASFormatter::getChecksumIn() const
7809 {
7810 	return checksumIn;
7811 }
7812 
7813 /**
7814  * Compute the output checksum.
7815  * This is called as an assert so it is for debug config only
7816  */
computeChecksumOut(const string & beautifiedLine)7817 bool ASFormatter::computeChecksumOut(const string& beautifiedLine)
7818 {
7819 	for (size_t i = 0; i < beautifiedLine.length(); i++)
7820 		if (!isWhiteSpace(beautifiedLine[i]))
7821 			checksumOut += beautifiedLine[i];
7822 	return true;
7823 }
7824 
7825 /**
7826  * Return isLineReady for the final check at end of file.
7827  */
getIsLineReady() const7828 bool ASFormatter::getIsLineReady() const
7829 {
7830 	return isLineReady;
7831 }
7832 
7833 /**
7834  * get the value of checksumOut for unit testing
7835  *
7836  * @return   checksumOut.
7837  */
getChecksumOut() const7838 size_t ASFormatter::getChecksumOut() const
7839 {
7840 	return checksumOut;
7841 }
7842 
7843 /**
7844  * Return the difference in checksums.
7845  * If zero all is okay.
7846  */
getChecksumDiff() const7847 int ASFormatter::getChecksumDiff() const
7848 {
7849 	return checksumOut - checksumIn;
7850 }
7851 
7852 // for unit testing
getFormatterFileType() const7853 int ASFormatter::getFormatterFileType() const
7854 {
7855 	return formatterFileType;
7856 }
7857 
7858 // Check if an operator follows the next word.
7859 // The next word must be a legal name.
getFollowingOperator() const7860 const string* ASFormatter::getFollowingOperator() const
7861 {
7862 	// find next word
7863 	size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
7864 	if (nextNum == string::npos)
7865 		return nullptr;
7866 
7867 	if (!isLegalNameChar(currentLine[nextNum]))
7868 		return nullptr;
7869 
7870 	// bypass next word and following spaces
7871 	while (nextNum < currentLine.length())
7872 	{
7873 		if (!isLegalNameChar(currentLine[nextNum])
7874 		        && !isWhiteSpace(currentLine[nextNum]))
7875 			break;
7876 		nextNum++;
7877 	}
7878 
7879 	if (nextNum >= currentLine.length()
7880 	        || !isCharPotentialOperator(currentLine[nextNum])
7881 	        || currentLine[nextNum] == '/')		// comment
7882 		return nullptr;
7883 
7884 	const string* newOperator = ASBase::findOperator(currentLine, nextNum, operators);
7885 	return newOperator;
7886 }
7887 
7888 // Check following data to determine if the current character is an array operator.
isArrayOperator() const7889 bool ASFormatter::isArrayOperator() const
7890 {
7891 	assert(currentChar == '*' || currentChar == '&' || currentChar == '^');
7892 	assert(isBraceType(braceTypeStack->back(), ARRAY_TYPE));
7893 
7894 	// find next word
7895 	size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1);
7896 	if (nextNum == string::npos)
7897 		return false;
7898 
7899 	if (!isLegalNameChar(currentLine[nextNum]))
7900 		return false;
7901 
7902 	// bypass next word and following spaces
7903 	while (nextNum < currentLine.length())
7904 	{
7905 		if (!isLegalNameChar(currentLine[nextNum])
7906 		        && !isWhiteSpace(currentLine[nextNum]))
7907 			break;
7908 		nextNum++;
7909 	}
7910 
7911 	// check for characters that indicate an operator
7912 	if (currentLine[nextNum] == ','
7913 	        || currentLine[nextNum] == '}'
7914 	        || currentLine[nextNum] == ')'
7915 	        || currentLine[nextNum] == '(')
7916 		return true;
7917 	return false;
7918 }
7919 
7920 // Reset the flags that indicate various statement information.
resetEndOfStatement()7921 void ASFormatter::resetEndOfStatement()
7922 {
7923 	foundQuestionMark = false;
7924 	foundNamespaceHeader = false;
7925 	foundClassHeader = false;
7926 	foundStructHeader = false;
7927 	foundInterfaceHeader = false;
7928 	foundPreDefinitionHeader = false;
7929 	foundPreCommandHeader = false;
7930 	foundPreCommandMacro = false;
7931 	foundTrailingReturnType = false;
7932 	foundCastOperator = false;
7933 	isInPotentialCalculation = false;
7934 	isSharpAccessor = false;
7935 	isSharpDelegate = false;
7936 	isInObjCMethodDefinition = false;
7937 	isImmediatelyPostObjCMethodPrefix = false;
7938 	isInObjCReturnType = false;
7939 	isInObjCParam = false;
7940 	isInObjCInterface = false;
7941 	isInObjCSelector = false;
7942 	isInEnum = false;
7943 	isInExternC = false;
7944 	elseHeaderFollowsComments = false;
7945 	returnTypeChecked = false;
7946 	nonInStatementBrace = 0;
7947 	while (!questionMarkStack->empty())
7948 		questionMarkStack->pop_back();
7949 }
7950 
7951 // Find the colon alignment for Objective-C method definitions and method calls.
findObjCColonAlignment() const7952 int ASFormatter::findObjCColonAlignment() const
7953 {
7954 	assert(currentChar == '+' || currentChar == '-' || currentChar == '[');
7955 	assert(getAlignMethodColon());
7956 
7957 	bool isFirstLine = true;
7958 	bool haveFirstColon = false;
7959 	bool foundMethodColon = false;
7960 	bool isInComment_ = false;
7961 	bool isInQuote_ = false;
7962 	bool haveTernary = false;
7963 	char quoteChar_ = ' ';
7964 	int  sqBracketCount = 0;
7965 	int  colonAdjust = 0;
7966 	int  colonAlign = 0;
7967 	string nextLine_ = currentLine;
7968 	ASPeekStream stream(sourceIterator);
7969 
7970 	// peek next line
7971 	while (sourceIterator->hasMoreLines() || isFirstLine)
7972 	{
7973 		if (!isFirstLine)
7974 			nextLine_ = stream.peekNextLine();
7975 		// parse the line
7976 		haveFirstColon = false;
7977 		nextLine_ = ASBeautifier::trim(nextLine_);
7978 		for (size_t i = 0; i < nextLine_.length(); i++)
7979 		{
7980 			if (isWhiteSpace(nextLine_[i]))
7981 				continue;
7982 			if (nextLine_.compare(i, 2, "/*") == 0)
7983 				isInComment_ = true;
7984 			if (isInComment_)
7985 			{
7986 				if (nextLine_.compare(i, 2, "*/") == 0)
7987 				{
7988 					isInComment_ = false;
7989 					++i;
7990 				}
7991 				continue;
7992 			}
7993 			if (nextLine_[i] == '\\')
7994 			{
7995 				++i;
7996 				continue;
7997 			}
7998 			if (isInQuote_)
7999 			{
8000 				if (nextLine_[i] == quoteChar_)
8001 					isInQuote_ = false;
8002 				continue;
8003 			}
8004 
8005 			if (nextLine_[i] == '"'
8006 			        || (nextLine_[i] == '\'' && !isDigitSeparator(nextLine_, i)))
8007 			{
8008 				isInQuote_ = true;
8009 				quoteChar_ = nextLine_[i];
8010 				continue;
8011 			}
8012 			if (nextLine_.compare(i, 2, "//") == 0)
8013 			{
8014 				i = nextLine_.length();
8015 				continue;
8016 			}
8017 			// process the current char
8018 			if ((nextLine_[i] == '{' && (currentChar == '-' || currentChar == '+'))
8019 			        || nextLine_[i] == ';')
8020 				goto EndOfWhileLoop;       // end of method definition
8021 			if (nextLine_[i] == ']')
8022 			{
8023 				--sqBracketCount;
8024 				if (sqBracketCount == 0)
8025 					goto EndOfWhileLoop;   // end of method call
8026 			}
8027 			if (nextLine_[i] == '[')
8028 				++sqBracketCount;
8029 			if (isFirstLine)	 // colon align does not include the first line
8030 				continue;
8031 			if (sqBracketCount > 1)
8032 				continue;
8033 			if (haveFirstColon)  // multiple colons per line
8034 				continue;
8035 			if (nextLine_[i] == '?')
8036 			{
8037 				haveTernary = true;
8038 				continue;
8039 			}
8040 			// compute colon adjustment
8041 			if (nextLine_[i] == ':')
8042 			{
8043 				if (haveTernary)
8044 				{
8045 					haveTernary = false;
8046 					continue;
8047 				}
8048 				haveFirstColon = true;
8049 				foundMethodColon = true;
8050 				if (shouldPadMethodColon)
8051 				{
8052 					int spacesStart;
8053 					for (spacesStart = i; spacesStart > 0; spacesStart--)
8054 						if (!isWhiteSpace(nextLine_[spacesStart - 1]))
8055 							break;
8056 					int spaces = i - spacesStart;
8057 					if (objCColonPadMode == COLON_PAD_ALL || objCColonPadMode == COLON_PAD_BEFORE)
8058 						colonAdjust = 1 - spaces;
8059 					else if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_AFTER)
8060 						colonAdjust = 0 - spaces;
8061 				}
8062 				// compute alignment
8063 				int colonPosition = i + colonAdjust;
8064 				if (colonPosition > colonAlign)
8065 					colonAlign = colonPosition;
8066 			}
8067 		}	// end of for loop
8068 		isFirstLine = false;
8069 	}	// end of while loop
8070 EndOfWhileLoop:
8071 	if (!foundMethodColon)
8072 		colonAlign = -1;
8073 	return colonAlign;
8074 }
8075 
8076 // pad an Objective-C method colon
padObjCMethodColon()8077 void ASFormatter::padObjCMethodColon()
8078 {
8079 	assert(currentChar == ':');
8080 	int commentAdjust = 0;
8081 	char nextChar = peekNextChar();
8082 	if (objCColonPadMode == COLON_PAD_NONE
8083 	        || objCColonPadMode == COLON_PAD_AFTER
8084 	        || nextChar == ')')
8085 	{
8086 		// remove spaces before
8087 		for (int i = formattedLine.length() - 1; (i > -1) && isWhiteSpace(formattedLine[i]); i--)
8088 		{
8089 			formattedLine.erase(i);
8090 			--commentAdjust;
8091 		}
8092 	}
8093 	else
8094 	{
8095 		// pad space before
8096 		for (int i = formattedLine.length() - 1; (i > 0) && isWhiteSpace(formattedLine[i]); i--)
8097 			if (isWhiteSpace(formattedLine[i - 1]))
8098 			{
8099 				formattedLine.erase(i);
8100 				--commentAdjust;
8101 			}
8102 		if (formattedLine.length() > 0)
8103 		{
8104 			appendSpacePad();
8105 			formattedLine.back() = ' ';  // convert any tab to space
8106 		}
8107 	}
8108 	if (objCColonPadMode == COLON_PAD_NONE
8109 	        || objCColonPadMode == COLON_PAD_BEFORE
8110 	        || nextChar == ')')
8111 	{
8112 		// remove spaces after
8113 		size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
8114 		if (nextText == string::npos)
8115 			nextText = currentLine.length();
8116 		int spaces = nextText - charNum - 1;
8117 		if (spaces > 0)
8118 		{
8119 			// do not use goForward here
8120 			currentLine.erase(charNum + 1, spaces);
8121 			spacePadNum -= spaces;
8122 		}
8123 	}
8124 	else
8125 	{
8126 		// pad space after
8127 		size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1);
8128 		if (nextText == string::npos)
8129 			nextText = currentLine.length();
8130 		int spaces = nextText - charNum - 1;
8131 		if (spaces == 0)
8132 		{
8133 			currentLine.insert(charNum + 1, 1, ' ');
8134 			spacePadNum += 1;
8135 		}
8136 		else if (spaces > 1)
8137 		{
8138 			// do not use goForward here
8139 			currentLine.erase(charNum + 1, spaces - 1);
8140 			currentLine[charNum + 1] = ' ';  // convert any tab to space
8141 			spacePadNum -= spaces - 1;
8142 		}
8143 	}
8144 	spacePadNum += commentAdjust;
8145 }
8146 
8147 // Remove the leading '*' from a comment line and indent to the next tab.
stripCommentPrefix()8148 void ASFormatter::stripCommentPrefix()
8149 {
8150 	int firstChar = formattedLine.find_first_not_of(" \t");
8151 	if (firstChar < 0)
8152 		return;
8153 
8154 	if (isInCommentStartLine)
8155 	{
8156 		// comment opener must begin the line
8157 		if (formattedLine.compare(firstChar, 2, "/*") != 0)
8158 			return;
8159 		int commentOpener = firstChar;
8160 		// ignore single line comments
8161 		int commentEnd = formattedLine.find("*/", firstChar + 2);
8162 		if (commentEnd != -1)
8163 			return;
8164 		// first char after the comment opener must be at least one indent
8165 		int followingText = formattedLine.find_first_not_of(" \t", commentOpener + 2);
8166 		if (followingText < 0)
8167 			return;
8168 		if (formattedLine[followingText] == '*' || formattedLine[followingText] == '!')
8169 			followingText = formattedLine.find_first_not_of(" \t", followingText + 1);
8170 		if (followingText < 0)
8171 			return;
8172 		if (formattedLine[followingText] == '*')
8173 			return;
8174 		int indentLen = getIndentLength();
8175 		int followingTextIndent = followingText - commentOpener;
8176 		if (followingTextIndent < indentLen)
8177 		{
8178 			string stringToInsert(indentLen - followingTextIndent, ' ');
8179 			formattedLine.insert(followingText, stringToInsert);
8180 		}
8181 		return;
8182 	}
8183 	// comment body including the closer
8184 	if (formattedLine[firstChar] == '*')
8185 	{
8186 		if (formattedLine.compare(firstChar, 2, "*/") == 0)
8187 		{
8188 			// line starts with an end comment
8189 			formattedLine = "*/";
8190 		}
8191 		else
8192 		{
8193 			// build a new line with one indent
8194 			int secondChar = formattedLine.find_first_not_of(" \t", firstChar + 1);
8195 			if (secondChar < 0)
8196 			{
8197 				adjustChecksumIn(-'*');
8198 				formattedLine.erase();
8199 				return;
8200 			}
8201 			if (formattedLine[secondChar] == '*')
8202 				return;
8203 			// replace the leading '*'
8204 			int indentLen = getIndentLength();
8205 			adjustChecksumIn(-'*');
8206 			// second char must be at least one indent
8207 			if (formattedLine.substr(0, secondChar).find('\t') != string::npos)
8208 			{
8209 				formattedLine.erase(firstChar, 1);
8210 			}
8211 			else
8212 			{
8213 				int spacesToInsert = 0;
8214 				if (secondChar >= indentLen)
8215 					spacesToInsert = secondChar;
8216 				else
8217 					spacesToInsert = indentLen;
8218 				formattedLine = string(spacesToInsert, ' ') + formattedLine.substr(secondChar);
8219 			}
8220 			// remove a trailing '*'
8221 			int lastChar = formattedLine.find_last_not_of(" \t");
8222 			if (lastChar > -1 && formattedLine[lastChar] == '*')
8223 			{
8224 				adjustChecksumIn(-'*');
8225 				formattedLine[lastChar] = ' ';
8226 			}
8227 		}
8228 	}
8229 	else
8230 	{
8231 		// first char not a '*'
8232 		// first char must be at least one indent
8233 		if (formattedLine.substr(0, firstChar).find('\t') == string::npos)
8234 		{
8235 			int indentLen = getIndentLength();
8236 			if (firstChar < indentLen)
8237 			{
8238 				string stringToInsert(indentLen, ' ');
8239 				formattedLine = stringToInsert + formattedLine.substr(firstChar);
8240 			}
8241 		}
8242 	}
8243 }
8244 
8245 }   // end namespace astyle
8246