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