OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / libs / qmljs / qmljsindenter.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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 **
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, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 /*
35     This file is a self-contained interactive indenter for Qt Script.
36
37     The general problem of indenting a program is ill posed. On
38     the one hand, an indenter has to analyze programs written in a
39     free-form formal language that is best described in terms of
40     tokens, not characters, not lines. On the other hand, indentation
41     applies to lines and white space characters matter, and otherwise
42     the programs to indent are formally invalid in general, as they
43     are begin edited.
44
45     The approach taken here works line by line. We receive a program
46     consisting of N lines or more, and we want to compute the
47     indentation appropriate for the Nth line. Lines beyond the Nth
48     lines are of no concern to us, so for simplicity we pretend the
49     program has exactly N lines and we call the Nth line the "bottom
50     line". Typically, we have to indent the bottom line when it's
51     still empty, so we concentrate our analysis on the N - 1 lines
52     that precede.
53
54     By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
55     backwards, we determine the kind of the bottom line and indent it
56     accordingly.
57
58       * The bottom line is a comment line. See
59         bottomLineStartsInCComment() and
60         indentWhenBottomLineStartsInCComment().
61       * The bottom line is a continuation line. See isContinuationLine()
62         and indentForContinuationLine().
63       * The bottom line is a standalone line. See
64         indentForStandaloneLine().
65
66     Certain tokens that influence the indentation, notably braces,
67     are looked for in the lines. This is done by simple string
68     comparison, without a real tokenizer. Confusing constructs such
69     as comments and string literals are removed beforehand.
70 */
71
72 #include <qmljs/qmljsindenter.h>
73 #include <qmljs/qmljsscanner.h>
74
75 #include <QtDebug>
76
77 using namespace QmlJS;
78
79 /*
80     Saves and restores the state of the global linizer. This enables
81     backtracking.
82
83     Identical to the defines in qmljslineinfo.cpp
84 */
85 #define YY_SAVE() LinizerState savedState = yyLinizerState
86 #define YY_RESTORE() yyLinizerState = savedState
87
88
89 QmlJSIndenter::QmlJSIndenter()
90     : caseOrDefault(QRegExp(QLatin1String(
91             "\\s*(?:"
92             "case\\b[^:]+|"
93             "default)"
94             "\\s*:.*")))
95
96 {
97
98     /*
99         The indenter supports a few parameters:
100
101           * ppHardwareTabSize is the size of a '\t' in your favorite editor.
102           * ppIndentSize is the size of an indentation, or software tab
103             size.
104           * ppContinuationIndentSize is the extra indent for a continuation
105             line, when there is nothing to align against on the previous
106             line.
107           * ppCommentOffset is the indentation within a C-style comment,
108             when it cannot be picked up.
109     */
110
111     ppHardwareTabSize = 8;
112     ppIndentSize = 4;
113     ppContinuationIndentSize = 8;
114     ppCommentOffset = 2;
115 }
116
117 QmlJSIndenter::~QmlJSIndenter()
118 {
119 }
120
121 void QmlJSIndenter::setTabSize(int size)
122 {
123     ppHardwareTabSize = size;
124 }
125
126 void QmlJSIndenter::setIndentSize(int size)
127 {
128     ppIndentSize = size;
129     ppContinuationIndentSize = 2 * size;
130 }
131
132 /*
133     Returns true if string t is made only of white space; otherwise
134     returns false.
135 */
136 bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const
137 {
138     return firstNonWhiteSpace(t).isNull();
139 }
140
141 /*
142     Assuming string t is a line, returns the column number of a given
143     index. Column numbers and index are identical for strings that don't
144     contain '\t's.
145 */
146 int QmlJSIndenter::columnForIndex(const QString &t, int index) const
147 {
148     int col = 0;
149     if (index > t.length())
150         index = t.length();
151
152     for (int i = 0; i < index; i++) {
153         if (t.at(i) == QLatin1Char('\t')) {
154             col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize;
155         } else {
156             col++;
157         }
158     }
159     return col;
160 }
161
162 /*
163     Returns the indentation size of string t.
164 */
165 int QmlJSIndenter::indentOfLine(const QString &t) const
166 {
167     return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t)));
168 }
169
170 /*
171     Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
172     left alone since they break the "index equals column" rule. No
173     provisions are taken against '\n' or '\r', which shouldn't occur in
174     t anyway.
175 */
176 void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const
177 {
178     if (t.at(k) != QLatin1Char('\t'))
179         t[k] = ch;
180 }
181
182 /*
183     Returns '(' if the last parenthesis is opening, ')' if it is
184     closing, and QChar() if there are no parentheses in t.
185 */
186 QChar QmlJSIndenter::lastParen() const
187 {
188     for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
189         const Token &token = yyLinizerState.tokens.at(index);
190
191         if (token.is(Token::LeftParenthesis))
192             return QChar('(');
193
194         else if (token.is(Token::RightParenthesis))
195             return QChar(')');
196     }
197
198     return QChar();
199 }
200
201 /*
202     Returns true if typedIn the same as okayCh or is null; otherwise
203     returns false.
204 */
205 bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const
206 {
207     return typedIn == QChar() || typedIn == okayCh;
208 }
209
210 /*
211     Returns the recommended indent for the bottom line of yyProgram
212     assuming that it starts in a C-style comment, a condition that is
213     tested elsewhere.
214
215     Essentially, we're trying to align against some text on the
216     previous line.
217 */
218 int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment()
219 {
220     QTextBlock block = yyProgram.lastBlock().previous();
221     QString blockText;
222
223     for (; block.isValid(); block = block.previous()) {
224         blockText = block.text();
225
226         if (! isOnlyWhiteSpace(blockText))
227             break;
228     }
229
230     return indentOfLine(blockText);
231 }
232
233 /*
234     Returns the recommended indent for the bottom line of yyProgram,
235     assuming it's a continuation line.
236
237     We're trying to align the continuation line against some parenthesis
238     or other bracked left opened on a previous line, or some interesting
239     operator such as '='.
240 */
241 int QmlJSIndenter::indentForContinuationLine()
242 {
243     int braceDepth = 0;
244     int delimDepth = 0;
245
246     bool leftBraceFollowed = *yyLeftBraceFollows;
247
248     for (int i = 0; i < SmallRoof; i++) {
249         int hook = -1;
250
251         int j = yyLine->length();
252         while (j > 0 && hook < 0) {
253             j--;
254             QChar ch = yyLine->at(j);
255
256             switch (ch.unicode()) {
257             case ')':
258                 delimDepth++;
259                 break;
260             case ']':
261                 braceDepth++;
262                 break;
263             case '}':
264                 braceDepth++;
265                 break;
266             case '(':
267                 delimDepth--;
268                 /*
269                     An unclosed delimiter is a good place to align at,
270                     at least for some styles (including Qt's).
271                 */
272                 if (delimDepth == -1)
273                     hook = j;
274                 break;
275
276             case '[':
277                 braceDepth--;
278                 /*
279                     An unclosed delimiter is a good place to align at,
280                     at least for some styles (including Qt's).
281                 */
282                 if (braceDepth == -1)
283                     hook = j;
284                 break;
285             case '{':
286                 braceDepth--;
287                 /*
288                     A left brace followed by other stuff on the same
289                     line is typically for an enum or an initializer.
290                     Such a brace must be treated just like the other
291                     delimiters.
292                 */
293                 if (braceDepth == -1) {
294                     if (j < yyLine->length() - 1) {
295                         hook = j;
296                     } else {
297                         return 0; // shouldn't happen
298                     }
299                 }
300                 break;
301             case '=':
302                 /*
303                     An equal sign is a very natural alignment hook
304                     because it's usually the operator with the lowest
305                     precedence in statements it appears in. Case in
306                     point:
307
308                         int x = 1 +
309                                 2;
310
311                     However, we have to beware of constructs such as
312                     default arguments and explicit enum constant
313                     values:
314
315                         void foo(int x = 0,
316                                   int y = 0);
317
318                     And not
319
320                         void foo(int x = 0,
321                                         int y = 0);
322
323                     These constructs are caracterized by a ',' at the
324                     end of the unfinished lines or by unbalanced
325                     parentheses.
326                 */
327                 Q_ASSERT(j - 1 >= 0);
328
329                 if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 &&
330                      j + 1 < yyLine->length() && yyLine->at(j + 1) != '=') {
331                     if (braceDepth == 0 && delimDepth == 0 &&
332                          j < yyLine->length() - 1 &&
333                          !yyLine->endsWith(QLatin1Char(',')) &&
334                          (yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')'))))
335                         hook = j;
336                 }
337             }
338         }
339
340         if (hook >= 0) {
341             /*
342                 Yes, we have a delimiter or an operator to align
343                 against! We don't really align against it, but rather
344                 against the following token, if any. In this example,
345                 the following token is "11":
346
347                     int x = (11 +
348                               2);
349
350                 If there is no such token, we use a continuation indent:
351
352                     static QRegExp foo(QString(
353                             "foo foo foo foo foo foo foo foo foo"));
354             */
355             hook++;
356             while (hook < yyLine->length()) {
357                 if (!yyLine->at(hook).isSpace())
358                     return columnForIndex(*yyLine, hook);
359                 hook++;
360             }
361             return indentOfLine(*yyLine) + ppContinuationIndentSize;
362         }
363
364         if (braceDepth != 0)
365             break;
366
367         /*
368             The line's delimiters are balanced. It looks like a
369             continuation line or something.
370         */
371         if (delimDepth == 0) {
372             if (leftBraceFollowed) {
373                 /*
374                     We have
375
376                         int main()
377                         {
378
379                     or
380
381                         Bar::Bar()
382                             : Foo(x)
383                         {
384
385                     The "{" should be flush left.
386                 */
387                 if (!isContinuationLine())
388                     return indentOfLine(*yyLine);
389             } else if (isContinuationLine() || yyLine->endsWith(QLatin1String(","))) {
390                 /*
391                     We have
392
393                         x = a +
394                             b +
395                             c;
396
397                     or
398
399                         int t[] = {
400                             1, 2, 3,
401                             4, 5, 6
402
403                     The "c;" should fall right under the "b +", and the
404                     "4, 5, 6" right under the "1, 2, 3,".
405                 */
406                 return indentOfLine(*yyLine);
407             } else {
408                 /*
409                     We have
410
411                         stream << 1 +
412                                 2;
413
414                     We could, but we don't, try to analyze which
415                     operator has precedence over which and so on, to
416                     obtain the excellent result
417
418                         stream << 1 +
419                                   2;
420
421                     We do have a special trick above for the assignment
422                     operator above, though.
423                 */
424                 return indentOfLine(*yyLine) + ppContinuationIndentSize;
425             }
426         }
427
428         if (!readLine())
429             break;
430     }
431     return 0;
432 }
433
434 /*
435     Returns the recommended indent for the bottom line of yyProgram if
436     that line is standalone (or should be indented likewise).
437
438     Indenting a standalone line is tricky, mostly because of braceless
439     control statements. Grossly, we are looking backwards for a special
440     line, a "hook line", that we can use as a starting point to indent,
441     and then modify the indentation level according to the braces met
442     along the way to that hook.
443
444     Let's consider a few examples. In all cases, we want to indent the
445     bottom line.
446
447     Example 1:
448
449         x = 1;
450         y = 2;
451
452     The hook line is "x = 1;". We met 0 opening braces and 0 closing
453     braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
454
455     Example 2:
456
457         if (x) {
458             y;
459
460     The hook line is "if (x) {". No matter what precedes it, "y;" has
461     to be indented one level deeper than the hook line, since we met one
462     opening brace along the way.
463
464     Example 3:
465
466         if (a)
467             while (b) {
468                 c;
469             }
470         d;
471
472     To indent "d;" correctly, we have to go as far as the "if (a)".
473     Compare with
474
475         if (a) {
476             while (b) {
477                 c;
478             }
479             d;
480
481     Still, we're striving to go back as little as possible to
482     accommodate people with irregular indentation schemes. A hook line
483     near at hand is much more reliable than a remote one.
484 */
485 int QmlJSIndenter::indentForStandaloneLine()
486 {
487     for (int i = 0; i < SmallRoof; i++) {
488         if (!*yyLeftBraceFollows) {
489             YY_SAVE();
490
491             if (matchBracelessControlStatement()) {
492                 /*
493                     The situation is this, and we want to indent "z;":
494
495                         if (x &&
496                              y)
497                             z;
498
499                     yyLine is "if (x &&".
500                 */
501                 return indentOfLine(*yyLine) + ppIndentSize;
502             }
503             YY_RESTORE();
504         }
505
506         if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) {
507             /*
508                 The situation is possibly this, and we want to indent
509                 "z;":
510
511                     while (x)
512                         y;
513                     z;
514
515                 We return the indent of "while (x)". In place of "y;",
516                 any arbitrarily complex compound statement can appear.
517             */
518
519             if (*yyBraceDepth > 0) {
520                 do {
521                     if (!readLine())
522                         break;
523                 } while (*yyBraceDepth > 0);
524             }
525
526             LinizerState hookState;
527
528             while (isContinuationLine())
529                 readLine();
530             hookState = yyLinizerState;
531
532             readLine();
533             if (*yyBraceDepth <= 0) {
534                 do {
535                     if (!matchBracelessControlStatement())
536                         break;
537                     hookState = yyLinizerState;
538                 } while (readLine());
539             }
540
541             yyLinizerState = hookState;
542
543             while (isContinuationLine())
544                 readLine();
545
546             int indentChange = - *yyBraceDepth;
547             if (caseOrDefault.exactMatch(*yyLine))
548                 ++indentChange;
549
550             /*
551               Never trust lines containing only '{' or '}', as some
552               people (Richard M. Stallman) format them weirdly.
553             */
554             if (yyLine->trimmed().length() > 1)
555                 return indentOfLine(*yyLine) + indentChange * ppIndentSize;
556         }
557
558         if (!readLine())
559             return -*yyBraceDepth * ppIndentSize;
560     }
561     return 0;
562 }
563
564 /*
565     Returns the recommended indent for the bottom line of program.
566     Unless null, typedIn stores the character of yyProgram that
567     triggered reindentation.
568
569     This function works better if typedIn is set properly; it is
570     slightly more conservative if typedIn is completely wild, and
571     slighly more liberal if typedIn is always null. The user might be
572     annoyed by the liberal behavior.
573 */
574 int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn)
575 {
576     if (begin == end)
577         return 0;
578
579     const QTextBlock last = end.previous();
580
581     initialize(begin, last);
582
583     QString bottomLine = last.text();
584     QChar firstCh = firstNonWhiteSpace(bottomLine);
585     int indent = 0;
586
587     if (bottomLineStartsInMultilineComment()) {
588         /*
589             The bottom line starts in a C-style comment. Indent it
590             smartly, unless the user has already played around with it,
591             in which case it's better to leave her stuff alone.
592         */
593         if (isOnlyWhiteSpace(bottomLine)) {
594             indent = indentWhenBottomLineStartsInMultiLineComment();
595         } else {
596             indent = indentOfLine(bottomLine);
597         }
598     } else {
599         if (isUnfinishedLine()) {
600             indent = indentForContinuationLine();
601         } else {
602             indent = indentForStandaloneLine();
603         }
604
605         if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}'))
606             || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) {
607             /*
608                 A closing brace is one level more to the left than the
609                 code it follows.
610             */
611             indent -= ppIndentSize;
612         } else if (okay(typedIn, QLatin1Char(':'))) {
613             if (caseOrDefault.exactMatch(bottomLine)) {
614                 /*
615                     Move a case label (or the ':' in front of a
616                     constructor initialization list) one level to the
617                     left, but only if the user did not play around with
618                     it yet. Some users have exotic tastes in the
619                     matter, and most users probably are not patient
620                     enough to wait for the final ':' to format their
621                     code properly.
622
623                     We don't attempt the same for goto labels, as the
624                     user is probably the middle of "foo::bar". (Who
625                     uses goto, anyway?)
626                 */
627                 if (indentOfLine(bottomLine) <= indent)
628                     indent -= ppIndentSize;
629                 else
630                     indent = indentOfLine(bottomLine);
631             }
632         }
633     }
634
635     return qMax(0, indent);
636 }
637