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