Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 This file is a self-contained interactive indenter for C++ and Qt
44 Script.
45
46 The general problem of indenting a C++ program is ill posed. On
47 the one hand, an indenter has to analyze programs written in a
48 free-form formal language that is best described in terms of
49 tokens, not characters, not lines. On the other hand, indentation
50 applies to lines and white space characters matter, and otherwise
51 the programs to indent are formally invalid in general, as they
52 are begin edited.
53
54 The approach taken here works line by line. We receive a program
55 consisting of N lines or more, and we want to compute the
56 indentation appropriate for the Nth line. Lines beyond the Nth
57 lines are of no concern to us, so for simplicity we pretend the
58 program has exactly N lines and we call the Nth line the "bottom
59 line". Typically, we have to indent the bottom line when it's
60 still empty, so we concentrate our analysis on the N - 1 lines
61 that precede.
62
63 By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
64 backwards, we determine the kind of the bottom line and indent it
65 accordingly.
66
67 * The bottom line is a comment line. See
68 bottomLineStartsInCComment() and
69 indentWhenBottomLineStartsInCComment().
70 * The bottom line is a continuation line. See isContinuationLine()
71 and indentForContinuationLine().
72 * The bottom line is a standalone line. See
73 indentForStandaloneLine().
74
75 Certain tokens that influence the indentation, notably braces,
76 are looked for in the lines. This is done by simple string
77 comparison, without a real tokenizer. Confusing constructs such
78 as comments and string literals are removed beforehand.
79*/
80
81#include <qregexp.h>
82#include <qstringlist.h>
83
84QT_BEGIN_NAMESPACE
85
86/* qmake ignore Q_OBJECT */
87
88/*
89 The indenter avoids getting stuck in almost infinite loops by
90 imposing arbitrary limits on the number of lines it analyzes when
91 looking for a construct.
92
93 For example, the indenter never considers more than BigRoof lines
94 backwards when looking for the start of a C-style comment.
95*/
96static const int SmallRoof = 40;
97static const int BigRoof = 400;
98
99/*
100 The indenter supports a few parameters:
101
102 * ppHardwareTabSize is the size of a '\t' in your favorite editor.
103 * ppIndentSize is the size of an indentation, or software tab
104 size.
105 * ppContinuationIndentSize is the extra indent for a continuation
106 line, when there is nothing to align against on the previous
107 line.
108 * ppCommentOffset is the indentation within a C-style comment,
109 when it cannot be picked up.
110*/
111
112static int ppHardwareTabSize = 8;
113static int ppIndentSize = 4;
114static int ppContinuationIndentSize = 8;
115
116static const int ppCommentOffset = 2;
117
118void setTabSize( int size )
119{
120 ppHardwareTabSize = size;
121}
122
123void setIndentSize( int size )
124{
125 ppIndentSize = size;
126 ppContinuationIndentSize = 2 * size;
127}
128
129static QRegExp *literal = 0;
130static QRegExp *label = 0;
131static QRegExp *inlineCComment = 0;
132static QRegExp *braceX = 0;
133static QRegExp *iflikeKeyword = 0;
134
135/*
136 Returns the first non-space character in the string t, or
137 QChar::Null if the string is made only of white space.
138*/
139static QChar firstNonWhiteSpace( const QString& t )
140{
141 int i = 0;
142 while ( i < (int) t.length() ) {
143 if ( !t[i].isSpace() )
144 return t[i];
145 i++;
146 }
147 return QChar::Null;
148}
149
150/*
151 Returns true if string t is made only of white space; otherwise
152 returns false.
153*/
154static bool isOnlyWhiteSpace( const QString& t )
155{
156 return firstNonWhiteSpace( t ).isNull();
157}
158
159/*
160 Assuming string t is a line, returns the column number of a given
161 index. Column numbers and index are identical for strings that don't
162 contain '\t's.
163*/
164int columnForIndex( const QString& t, int index )
165{
166 int col = 0;
167 if ( index > (int) t.length() )
168 index = t.length();
169
170 for ( int i = 0; i < index; i++ ) {
171 if ( t[i] == QChar('\t') ) {
172 col = ( (col / ppHardwareTabSize) + 1 ) * ppHardwareTabSize;
173 } else {
174 col++;
175 }
176 }
177 return col;
178}
179
180/*
181 Returns the indentation size of string t.
182*/
183int indentOfLine( const QString& t )
184{
185 return columnForIndex( t, t.indexOf(firstNonWhiteSpace(t)) );
186}
187
188/*
189 Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
190 left alone since they break the "index equals column" rule. No
191 provisions are taken against '\n' or '\r', which shouldn't occur in
192 t anyway.
193*/
194static inline void eraseChar( QString& t, int k, QChar ch )
195{
196 if ( t[k] != '\t' )
197 t[k] = ch;
198}
199
200/*
201 Removes some nefast constructs from a code line and returns the
202 resulting line.
203*/
204static QString trimmedCodeLine( const QString& t )
205{
206 QString trimmed = t;
207 int k;
208
209 /*
210 Replace character and string literals by X's, since they may
211 contain confusing characters (such as '{' and ';'). "Hello!" is
212 replaced by XXXXXXXX. The literals are rigourously of the same
213 length before and after; otherwise, we would break alignment of
214 continuation lines.
215 */
216 k = 0;
217 while ( (k = trimmed.indexOf(*literal, k)) != -1 ) {
218 for ( int i = 0; i < literal->matchedLength(); i++ )
219 eraseChar( trimmed, k + i, 'X' );
220 k += literal->matchedLength();
221 }
222
223 /*
224 Replace inline C-style comments by spaces. Other comments are
225 handled elsewhere.
226 */
227 k = 0;
228 while ( (k = trimmed.indexOf(*inlineCComment, k)) != -1 ) {
229 for ( int i = 0; i < inlineCComment->matchedLength(); i++ )
230 eraseChar( trimmed, k + i, ' ' );
231 k += inlineCComment->matchedLength();
232 }
233
234 /*
235 Replace goto and switch labels by whitespace, but be careful
236 with this case:
237
238 foo1: bar1;
239 bar2;
240 */
241 while ( trimmed.lastIndexOf(':') != -1 && trimmed.indexOf(*label) != -1 ) {
242 QString cap1 = label->cap( 1 );
243 int pos1 = label->pos( 1 );
244 int stop = cap1.length();
245
246 if ( pos1 + stop < (int) trimmed.length() && ppIndentSize < stop )
247 stop = ppIndentSize;
248
249 int i = 0;
250 while ( i < stop ) {
251 eraseChar( trimmed, pos1 + i, ' ' );
252 i++;
253 }
254 while ( i < (int) cap1.length() ) {
255 eraseChar( trimmed, pos1 + i, ';' );
256 i++;
257 }
258 }
259
260 /*
261 Remove C++-style comments.
262 */
263 k = trimmed.indexOf( "//" );
264 if ( k != -1 )
265 trimmed.truncate( k );
266
267 return trimmed;
268}
269
270/*
271 Returns '(' if the last parenthesis is opening, ')' if it is
272 closing, and QChar::Null if there are no parentheses in t.
273*/
274static inline QChar lastParen( const QString& t )
275{
276 int i = t.length();
277 while ( i > 0 ) {
278 i--;
279 if ( t[i] == QChar('(') || t[i] == QChar(')') )
280 return t[i];
281 }
282 return QChar::Null;
283}
284
285/*
286 Returns true if typedIn the same as okayCh or is null; otherwise
287 returns false.
288*/
289static inline bool okay( QChar typedIn, QChar okayCh )
290{
291 return typedIn == QChar::Null || typedIn == okayCh;
292}
293
294/*
295 The "linizer" is a group of functions and variables to iterate
296 through the source code of the program to indent. The program is
297 given as a list of strings, with the bottom line being the line
298 to indent. The actual program might contain extra lines, but
299 those are uninteresting and not passed over to us.
300*/
301
302struct LinizerState
303{
304 QString line;
305 int braceDepth;
306 bool leftBraceFollows;
307
308 QStringList::ConstIterator iter;
309 bool inCComment;
310 bool pendingRightBrace;
311};
312
313static QStringList *yyProgram = 0;
314static LinizerState *yyLinizerState = 0;
315
316// shorthands
317static const QString *yyLine = 0;
318static const int *yyBraceDepth = 0;
319static const bool *yyLeftBraceFollows = 0;
320
321/*
322 Saves and restores the state of the global linizer. This enables
323 backtracking.
324*/
325#define YY_SAVE() \
326 LinizerState savedState = *yyLinizerState
327#define YY_RESTORE() \
328 *yyLinizerState = savedState
329
330/*
331 Advances to the previous line in yyProgram and update yyLine
332 accordingly. yyLine is cleaned from comments and other damageable
333 constructs. Empty lines are skipped.
334*/
335static bool readLine()
336{
337 int k;
338
339 yyLinizerState->leftBraceFollows =
340 ( firstNonWhiteSpace(yyLinizerState->line) == QChar('{') );
341
342 do {
343 if ( yyLinizerState->iter == yyProgram->begin() ) {
344 yyLinizerState->line.clear();
345 return false;
346 }
347
348 --yyLinizerState->iter;
349 yyLinizerState->line = *yyLinizerState->iter;
350
351 yyLinizerState->line = trimmedCodeLine( yyLinizerState->line );
352
353 /*
354 Remove C-style comments that span multiple lines. If the
355 bottom line starts in a C-style comment, we are not aware
356 of that and eventually yyLine will contain a slash-aster.
357
358 Notice that both if's can be executed, since
359 yyLinizerState->inCComment is potentially set to false in
360 the first if. The order of the if's is also important.
361 */
362
363 if ( yyLinizerState->inCComment ) {
364 QString slashAster( "/*" );
365
366 k = yyLinizerState->line.indexOf( slashAster );
367 if ( k == -1 ) {
368 yyLinizerState->line.clear();
369 } else {
370 yyLinizerState->line.truncate( k );
371 yyLinizerState->inCComment = false;
372 }
373 }
374
375 if ( !yyLinizerState->inCComment ) {
376 QString asterSlash( "*/" );
377
378 k = yyLinizerState->line.indexOf( asterSlash );
379 if ( k != -1 ) {
380 for ( int i = 0; i < k + 2; i++ )
381 eraseChar( yyLinizerState->line, i, ' ' );
382 yyLinizerState->inCComment = true;
383 }
384 }
385
386 /*
387 Remove preprocessor directives.
388 */
389 k = 0;
390 while ( k < (int) yyLinizerState->line.length() ) {
391 QChar ch = yyLinizerState->line[k];
392 if ( ch == QChar('#') ) {
393 yyLinizerState->line.clear();
394 } else if ( !ch.isSpace() ) {
395 break;
396 }
397 k++;
398 }
399
400 /*
401 Remove trailing spaces.
402 */
403 k = yyLinizerState->line.length();
404 while ( k > 0 && yyLinizerState->line[k - 1].isSpace() )
405 k--;
406 yyLinizerState->line.truncate( k );
407
408 /*
409 '}' increment the brace depth and '{' decrements it and not
410 the other way around, as we are parsing backwards.
411 */
412 yyLinizerState->braceDepth +=
413 yyLinizerState->line.count( '}' ) -
414 yyLinizerState->line.count( '{' );
415
416 /*
417 We use a dirty trick for
418
419 } else ...
420
421 We don't count the '}' yet, so that it's more or less
422 equivalent to the friendly construct
423
424 }
425 else ...
426 */
427 if ( yyLinizerState->pendingRightBrace )
428 yyLinizerState->braceDepth++;
429 yyLinizerState->pendingRightBrace =
430 ( yyLinizerState->line.indexOf(*braceX) == 0 );
431 if ( yyLinizerState->pendingRightBrace )
432 yyLinizerState->braceDepth--;
433 } while ( yyLinizerState->line.isEmpty() );
434
435 return true;
436}
437
438/*
439 Resets the linizer to its initial state, with yyLine containing the
440 line above the bottom line of the program.
441*/
442static void startLinizer()
443{
444 yyLinizerState->braceDepth = 0;
445 yyLinizerState->inCComment = false;
446 yyLinizerState->pendingRightBrace = false;
447
448 yyLine = &yyLinizerState->line;
449 yyBraceDepth = &yyLinizerState->braceDepth;
450 yyLeftBraceFollows = &yyLinizerState->leftBraceFollows;
451
452 yyLinizerState->iter = yyProgram->end();
453 --yyLinizerState->iter;
454 yyLinizerState->line = *yyLinizerState->iter;
455 readLine();
456}
457
458/*
459 Returns true if the start of the bottom line of yyProgram (and
460 potentially the whole line) is part of a C-style comment;
461 otherwise returns false.
462*/
463static bool bottomLineStartsInCComment()
464{
465 QString slashAster( "/*" );
466 QString asterSlash( "*/" );
467
468 /*
469 We could use the linizer here, but that would slow us down
470 terribly. We are better to trim only the code lines we need.
471 */
472 QStringList::ConstIterator p = yyProgram->end();
473 --p; // skip bottom line
474
475 for ( int i = 0; i < BigRoof; i++ ) {
476 if ( p == yyProgram->begin() )
477 return false;
478 --p;
479
480 if ( (*p).indexOf(slashAster) != -1 || (*p).indexOf(asterSlash) != -1 ) {
481 QString trimmed = trimmedCodeLine( *p );
482
483 if ( trimmed.indexOf(slashAster) != -1 ) {
484 return true;
485 } else if ( trimmed.indexOf(asterSlash) != -1 ) {
486 return false;
487 }
488 }
489 }
490 return false;
491}
492
493/*
494 Returns the recommended indent for the bottom line of yyProgram
495 assuming that it starts in a C-style comment, a condition that is
496 tested elsewhere.
497
498 Essentially, we're trying to align against some text on the
499 previous line.
500*/
501static int indentWhenBottomLineStartsInCComment()
502{
503 int k = yyLine->lastIndexOf( "/*" );
504 if ( k == -1 ) {
505 /*
506 We found a normal text line in a comment. Align the
507 bottom line with the text on this line.
508 */
509 return indentOfLine( *yyLine );
510 } else {
511 /*
512 The C-style comment starts on this line. If there is
513 text on the same line, align with it. Otherwise, align
514 with the slash-aster plus a given offset.
515 */
516 int indent = columnForIndex( *yyLine, k );
517 k += 2;
518 while ( k < (int) yyLine->length() ) {
519 if ( !(*yyLine)[k].isSpace() )
520 return columnForIndex( *yyLine, k );
521 k++;
522 }
523 return indent + ppCommentOffset;
524 }
525}
526
527/*
528 A function called match...() modifies the linizer state. If it
529 returns true, yyLine is the top line of the matched construct;
530 otherwise, the linizer is left in an unknown state.
531
532 A function called is...() keeps the linizer state intact.
533*/
534
535/*
536 Returns true if the current line (and upwards) forms a braceless
537 control statement; otherwise returns false.
538
539 The first line of the following example is a "braceless control
540 statement":
541
542 if ( x )
543 y;
544*/
545static bool matchBracelessControlStatement()
546{
547 int delimDepth = 0;
548
549 if ( yyLine->endsWith("else") )
550 return true;
551
552 if ( !yyLine->endsWith(")") )
553 return false;
554
555 for ( int i = 0; i < SmallRoof; i++ ) {
556 int j = yyLine->length();
557 while ( j > 0 ) {
558 j--;
559 QChar ch = (*yyLine)[j];
560
561 switch ( ch.unicode() ) {
562 case ')':
563 delimDepth++;
564 break;
565 case '(':
566 delimDepth--;
567 if ( delimDepth == 0 ) {
568 if ( yyLine->indexOf(*iflikeKeyword) != -1 ) {
569 /*
570 We have
571
572 if ( x )
573 y
574
575 "if ( x )" is not part of the statement
576 "y".
577 */
578 return true;
579 }
580 }
581 if ( delimDepth == -1 ) {
582 /*
583 We have
584
585 if ( (1 +
586 2)
587
588 and not
589
590 if ( 1 +
591 2 )
592 */
593 return false;
594 }
595 break;
596 case '{':
597 case '}':
598 case ';':
599 /*
600 We met a statement separator, but not where we
601 expected it. What follows is probably a weird
602 continuation line. Be careful with ';' in for,
603 though.
604 */
605 if ( ch != QChar(';') || delimDepth == 0 )
606 return false;
607 }
608 }
609
610 if ( !readLine() )
611 break;
612 }
613 return false;
614}
615
616/*
617 Returns true if yyLine is an unfinished line; otherwise returns
618 false.
619
620 In many places we'll use the terms "standalone line", "unfinished
621 line" and "continuation line". The meaning of these should be
622 evident from this code example:
623
624 a = b; // standalone line
625 c = d + // unfinished line
626 e + // unfinished continuation line
627 f + // unfinished continuation line
628 g; // continuation line
629*/
630static bool isUnfinishedLine()
631{
632 bool unf = false;
633
634 YY_SAVE();
635
636 if ( yyLine->isEmpty() )
637 return false;
638
639 QChar lastCh = (*yyLine)[(int) yyLine->length() - 1];
640 if ( QString("{};").indexOf(lastCh) == -1 && !yyLine->endsWith("...") ) {
641 /*
642 It doesn't end with ';' or similar. If it's neither
643 "Q_OBJECT" nor "if ( x )", it must be an unfinished line.
644 */
645 unf = ( yyLine->indexOf("Q_OBJECT") == -1 &&
646 !matchBracelessControlStatement() );
647 } else if ( lastCh == QChar(';') ) {
648 if ( lastParen(*yyLine) == QChar('(') ) {
649 /*
650 Exception:
651
652 for ( int i = 1; i < 10;
653 */
654 unf = true;
655 } else if ( readLine() && yyLine->endsWith(";") &&
656 lastParen(*yyLine) == QChar('(') ) {
657 /*
658 Exception:
659
660 for ( int i = 1;
661 i < 10;
662 */
663 unf = true;
664 }
665 }
666
667 YY_RESTORE();
668 return unf;
669}
670
671/*
672 Returns true if yyLine is a continuation line; otherwise returns
673 false.
674*/
675static bool isContinuationLine()
676{
677 bool cont = false;
678
679 YY_SAVE();
680 if ( readLine() )
681 cont = isUnfinishedLine();
682 YY_RESTORE();
683 return cont;
684}
685
686/*
687 Returns the recommended indent for the bottom line of yyProgram,
688 assuming it's a continuation line.
689
690 We're trying to align the continuation line against some parenthesis
691 or other bracked left opened on a previous line, or some interesting
692 operator such as '='.
693*/
694static int indentForContinuationLine()
695{
696 int braceDepth = 0;
697 int delimDepth = 0;
698
699 bool leftBraceFollowed = *yyLeftBraceFollows;
700
701 for ( int i = 0; i < SmallRoof; i++ ) {
702 int hook = -1;
703
704 int j = yyLine->length();
705 while ( j > 0 && hook < 0 ) {
706 j--;
707 QChar ch = (*yyLine)[j];
708
709 switch ( ch.unicode() ) {
710 case ')':
711 case ']':
712 delimDepth++;
713 break;
714 case '}':
715 braceDepth++;
716 break;
717 case '(':
718 case '[':
719 delimDepth--;
720 /*
721 An unclosed delimiter is a good place to align at,
722 at least for some styles (including Qt's).
723 */
724 if ( delimDepth == -1 )
725 hook = j;
726 break;
727 case '{':
728 braceDepth--;
729 /*
730 A left brace followed by other stuff on the same
731 line is typically for an enum or an initializer.
732 Such a brace must be treated just like the other
733 delimiters.
734 */
735 if ( braceDepth == -1 ) {
736 if ( j < (int) yyLine->length() - 1 ) {
737 hook = j;
738 } else {
739 return 0; // shouldn't happen
740 }
741 }
742 break;
743 case '=':
744 /*
745 An equal sign is a very natural alignment hook
746 because it's usually the operator with the lowest
747 precedence in statements it appears in. Case in
748 point:
749
750 int x = 1 +
751 2;
752
753 However, we have to beware of constructs such as
754 default arguments and explicit enum constant
755 values:
756
757 void foo( int x = 0,
758 int y = 0 );
759
760 And not
761
762 void foo( int x = 0,
763 int y = 0 );
764
765 These constructs are caracterized by a ',' at the
766 end of the unfinished lines or by unbalanced
767 parentheses.
768 */
769 if ( QString("!=<>").indexOf((*yyLine)[j - 1]) == -1 &&
770 (*yyLine)[j + 1] != '=' ) {
771 if ( braceDepth == 0 && delimDepth == 0 &&
772 j < (int) yyLine->length() - 1 &&
773 !yyLine->endsWith(",") &&
774 (yyLine->contains('(') == yyLine->contains(')')) )
775 hook = j;
776 }
777 }
778 }
779
780 if ( hook >= 0 ) {
781 /*
782 Yes, we have a delimiter or an operator to align
783 against! We don't really align against it, but rather
784 against the following token, if any. In this example,
785 the following token is "11":
786
787 int x = ( 11 +
788 2 );
789
790 If there is no such token, we use a continuation indent:
791
792 static QRegExp foo( QString(
793 "foo foo foo foo foo foo foo foo foo") );
794 */
795 hook++;
796 while ( hook < (int) yyLine->length() ) {
797 if ( !(*yyLine)[hook].isSpace() )
798 return columnForIndex( *yyLine, hook );
799 hook++;
800 }
801 return indentOfLine( *yyLine ) + ppContinuationIndentSize;
802 }
803
804 if ( braceDepth != 0 )
805 break;
806
807 /*
808 The line's delimiters are balanced. It looks like a
809 continuation line or something.
810 */
811 if ( delimDepth == 0 ) {
812 if ( leftBraceFollowed ) {
813 /*
814 We have
815
816 int main()
817 {
818
819 or
820
821 Bar::Bar()
822 : Foo( x )
823 {
824
825 The "{" should be flush left.
826 */
827 if ( !isContinuationLine() )
828 return indentOfLine( *yyLine );
829 } else if ( isContinuationLine() || yyLine->endsWith(",") ) {
830 /*
831 We have
832
833 x = a +
834 b +
835 c;
836
837 or
838
839 int t[] = {
840 1, 2, 3,
841 4, 5, 6
842
843 The "c;" should fall right under the "b +", and the
844 "4, 5, 6" right under the "1, 2, 3,".
845 */
846 return indentOfLine( *yyLine );
847 } else {
848 /*
849 We have
850
851 stream << 1 +
852 2;
853
854 We could, but we don't, try to analyze which
855 operator has precedence over which and so on, to
856 obtain the excellent result
857
858 stream << 1 +
859 2;
860
861 We do have a special trick above for the assignment
862 operator above, though.
863 */
864 return indentOfLine( *yyLine ) + ppContinuationIndentSize;
865 }
866 }
867
868 if ( !readLine() )
869 break;
870 }
871 return 0;
872}
873
874/*
875 Returns the recommended indent for the bottom line of yyProgram if
876 that line is standalone (or should be indented likewise).
877
878 Indenting a standalone line is tricky, mostly because of braceless
879 control statements. Grossly, we are looking backwards for a special
880 line, a "hook line", that we can use as a starting point to indent,
881 and then modify the indentation level according to the braces met
882 along the way to that hook.
883
884 Let's consider a few examples. In all cases, we want to indent the
885 bottom line.
886
887 Example 1:
888
889 x = 1;
890 y = 2;
891
892 The hook line is "x = 1;". We met 0 opening braces and 0 closing
893 braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
894
895 Example 2:
896
897 if ( x ) {
898 y;
899
900 The hook line is "if ( x ) {". No matter what precedes it, "y;" has
901 to be indented one level deeper than the hook line, since we met one
902 opening brace along the way.
903
904 Example 3:
905
906 if ( a )
907 while ( b ) {
908 c;
909 }
910 d;
911
912 To indent "d;" correctly, we have to go as far as the "if ( a )".
913 Compare with
914
915 if ( a ) {
916 while ( b ) {
917 c;
918 }
919 d;
920
921 Still, we're striving to go back as little as possible to
922 accommodate people with irregular indentation schemes. A hook line
923 near at hand is much more reliable than a remote one.
924*/
925static int indentForStandaloneLine()
926{
927 for ( int i = 0; i < SmallRoof; i++ ) {
928 if ( !*yyLeftBraceFollows ) {
929 YY_SAVE();
930
931 if ( matchBracelessControlStatement() ) {
932 /*
933 The situation is this, and we want to indent "z;":
934
935 if ( x &&
936 y )
937 z;
938
939 yyLine is "if ( x &&".
940 */
941 return indentOfLine( *yyLine ) + ppIndentSize;
942 }
943 YY_RESTORE();
944 }
945
946 if ( yyLine->endsWith(";") || yyLine->contains('{') ) {
947 /*
948 The situation is possibly this, and we want to indent
949 "z;":
950
951 while ( x )
952 y;
953 z;
954
955 We return the indent of "while ( x )". In place of "y;",
956 any arbitrarily complex compound statement can appear.
957 */
958
959 if ( *yyBraceDepth > 0 ) {
960 do {
961 if ( !readLine() )
962 break;
963 } while ( *yyBraceDepth > 0 );
964 }
965
966 LinizerState hookState;
967
968 while ( isContinuationLine() )
969 readLine();
970 hookState = *yyLinizerState;
971
972 readLine();
973 if ( *yyBraceDepth <= 0 ) {
974 do {
975 if ( !matchBracelessControlStatement() )
976 break;
977 hookState = *yyLinizerState;
978 } while ( readLine() );
979 }
980
981 *yyLinizerState = hookState;
982
983 while ( isContinuationLine() )
984 readLine();
985
986 /*
987 Never trust lines containing only '{' or '}', as some
988 people (Richard M. Stallman) format them weirdly.
989 */
990 if ( yyLine->trimmed().length() > 1 )
991 return indentOfLine( *yyLine ) - *yyBraceDepth * ppIndentSize;
992 }
993
994 if ( !readLine() )
995 return -*yyBraceDepth * ppIndentSize;
996 }
997 return 0;
998}
999
1000/*
1001 Constructs global variables used by the indenter.
1002*/
1003static void initializeIndenter()
1004{
1005 literal = new QRegExp( "([\"'])(?:\\\\.|[^\\\\])*\\1" );
1006 literal->setMinimal( true );
1007 label = new QRegExp(
1008 "^\\s*((?:case\\b([^:]|::)+|[a-zA-Z_0-9]+)(?:\\s+slots)?:)(?!:)" );
1009 inlineCComment = new QRegExp( "/\\*.*\\*/" );
1010 inlineCComment->setMinimal( true );
1011 braceX = new QRegExp( "^\\s*\\}\\s*(?:else|catch)\\b" );
1012 iflikeKeyword = new QRegExp( "\\b(?:catch|do|for|if|while)\\b" );
1013
1014 yyLinizerState = new LinizerState;
1015}
1016
1017/*
1018 Destroys global variables used by the indenter.
1019*/
1020static void terminateIndenter()
1021{
1022 delete literal;
1023 delete label;
1024 delete inlineCComment;
1025 delete braceX;
1026 delete iflikeKeyword;
1027 delete yyLinizerState;
1028}
1029
1030/*
1031 Returns the recommended indent for the bottom line of program.
1032 Unless null, typedIn stores the character of yyProgram that
1033 triggered reindentation.
1034
1035 This function works better if typedIn is set properly; it is
1036 slightly more conservative if typedIn is completely wild, and
1037 slighly more liberal if typedIn is always null. The user might be
1038 annoyed by the liberal behavior.
1039*/
1040int indentForBottomLine( const QStringList& program, QChar typedIn )
1041{
1042 if ( program.isEmpty() )
1043 return 0;
1044
1045 initializeIndenter();
1046
1047 yyProgram = new QStringList( program );
1048 startLinizer();
1049
1050 const QString& bottomLine = program.last();
1051 QChar firstCh = firstNonWhiteSpace( bottomLine );
1052 int indent;
1053
1054 if ( bottomLineStartsInCComment() ) {
1055 /*
1056 The bottom line starts in a C-style comment. Indent it
1057 smartly, unless the user has already played around with it,
1058 in which case it's better to leave her stuff alone.
1059 */
1060 if ( isOnlyWhiteSpace(bottomLine) ) {
1061 indent = indentWhenBottomLineStartsInCComment();
1062 } else {
1063 indent = indentOfLine( bottomLine );
1064 }
1065 } else if ( okay(typedIn, '#') && firstCh == QChar('#') ) {
1066 /*
1067 Preprocessor directives go flush left.
1068 */
1069 indent = 0;
1070 } else {
1071 if ( isUnfinishedLine() ) {
1072 indent = indentForContinuationLine();
1073 } else {
1074 indent = indentForStandaloneLine();
1075 }
1076
1077 if ( okay(typedIn, '}') && firstCh == QChar('}') ) {
1078 /*
1079 A closing brace is one level more to the left than the
1080 code it follows.
1081 */
1082 indent -= ppIndentSize;
1083 } else if ( okay(typedIn, ':') ) {
1084 QRegExp caseLabel(
1085 "\\s*(?:case\\b(?:[^:]|::)+"
1086 "|(?:public|protected|private|signals|default)(?:\\s+slots)?\\s*"
1087 ")?:.*" );
1088
1089 if ( caseLabel.exactMatch(bottomLine) ) {
1090 /*
1091 Move a case label (or the ':' in front of a
1092 constructor initialization list) one level to the
1093 left, but only if the user did not play around with
1094 it yet. Some users have exotic tastes in the
1095 matter, and most users probably are not patient
1096 enough to wait for the final ':' to format their
1097 code properly.
1098
1099 We don't attempt the same for goto labels, as the
1100 user is probably the middle of "foo::bar". (Who
1101 uses goto, anyway?)
1102 */
1103 if ( indentOfLine(bottomLine) <= indent )
1104 indent -= ppIndentSize;
1105 else
1106 indent = indentOfLine( bottomLine );
1107 }
1108 }
1109 }
1110 delete yyProgram;
1111 terminateIndenter();
1112 return qMax( 0, indent );
1113}
1114
1115QT_END_NAMESPACE
1116
1117#ifdef Q_TEST_YYINDENT
1118/*
1119 Test driver.
1120*/
1121
1122#include <qfile.h>
1123#include <qtextstream.h>
1124
1125#include <errno.h>
1126
1127QT_BEGIN_NAMESPACE
1128
1129static QString fileContents( const QString& fileName )
1130{
1131 QFile f( fileName );
1132 if ( !f.open(QFile::ReadOnly) ) {
1133 qWarning( "yyindent error: Cannot open file '%s' for reading: %s",
1134 fileName.toLatin1().data(), strerror(errno) );
1135 return QString();
1136 }
1137
1138 QTextStream t( &f );
1139 QString contents = t.read();
1140 f.close();
1141 if ( contents.isEmpty() )
1142 qWarning( "yyindent error: File '%s' is empty", fileName.toLatin1().data() );
1143 return contents;
1144}
1145
1146QT_END_NAMESPACE
1147
1148int main( int argc, char **argv )
1149{
1150 QT_USE_NAMESPACE
1151
1152 if ( argc != 2 ) {
1153 qWarning( "usage: yyindent file.cpp" );
1154 return 1;
1155 }
1156
1157 QString code = fileContents( argv[1] );
1158 QStringList program = QStringList::split( '\n', code, true );
1159 QStringList p;
1160 QString out;
1161
1162 while ( !program.isEmpty() && program.last().trimmed().isEmpty() )
1163 program.remove( program.fromLast() );
1164
1165 QStringList::ConstIterator line = program.begin();
1166 while ( line != program.end() ) {
1167 p.push_back( *line );
1168 QChar typedIn = firstNonWhiteSpace( *line );
1169 if ( p.last().endsWith(":") )
1170 typedIn = ':';
1171
1172 int indent = indentForBottomLine( p, typedIn );
1173
1174 if ( !(*line).trimmed().isEmpty() ) {
1175 for ( int j = 0; j < indent; j++ )
1176 out += " ";
1177 out += (*line).trimmed();
1178 }
1179 out += "\n";
1180 ++line;
1181 }
1182
1183 while ( out.endsWith("\n") )
1184 out.truncate( out.length() - 1 );
1185
1186 printf( "%s\n", out.toLatin1().data() );
1187 return 0;
1188}
1189
1190#endif // Q_TEST_YYINDENT
1191

Warning: That file was not part of the compilation database. It may have many parsing errors.