tools/qdoc3/yyindent.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    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 
       
    84 QT_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 */
       
    96 static const int SmallRoof = 40;
       
    97 static 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 
       
   112 static int ppHardwareTabSize = 8;
       
   113 static int ppIndentSize = 4;
       
   114 static int ppContinuationIndentSize = 8;
       
   115 
       
   116 static const int ppCommentOffset = 2;
       
   117 
       
   118 void setTabSize( int size )
       
   119 {
       
   120     ppHardwareTabSize = size;
       
   121 }
       
   122 
       
   123 void setIndentSize( int size )
       
   124 {
       
   125     ppIndentSize = size;
       
   126     ppContinuationIndentSize = 2 * size;
       
   127 }
       
   128 
       
   129 static QRegExp *literal = 0;
       
   130 static QRegExp *label = 0;
       
   131 static QRegExp *inlineCComment = 0;
       
   132 static QRegExp *braceX = 0;
       
   133 static 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 */
       
   139 static 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 */
       
   154 static 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 */
       
   164 int 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 */
       
   183 int 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 */
       
   194 static 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 */
       
   204 static 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 */
       
   274 static 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 */
       
   289 static 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 
       
   302 struct LinizerState
       
   303 {
       
   304     QString line;
       
   305     int braceDepth;
       
   306     bool leftBraceFollows;
       
   307 
       
   308     QStringList::ConstIterator iter;
       
   309     bool inCComment;
       
   310     bool pendingRightBrace;
       
   311 };
       
   312 
       
   313 static QStringList *yyProgram = 0;
       
   314 static LinizerState *yyLinizerState = 0;
       
   315 
       
   316 // shorthands
       
   317 static const QString *yyLine = 0;
       
   318 static const int *yyBraceDepth = 0;
       
   319 static 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 */
       
   335 static 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 */
       
   442 static 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 */
       
   463 static 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 */
       
   501 static 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 */
       
   545 static 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 */
       
   630 static 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 */
       
   675 static 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 */
       
   694 static 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 */
       
   925 static 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 */
       
  1003 static 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 */
       
  1020 static 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 */
       
  1040 int 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 
       
  1115 QT_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 
       
  1127 QT_BEGIN_NAMESPACE
       
  1128 
       
  1129 static 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 
       
  1146 QT_END_NAMESPACE
       
  1147 
       
  1148 int 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