OSDN Git Service

QmlJS indenter: Fix hang when using 'else (a==a) {}'.
[qt-creator-jp/qt-creator-jp.git] / src / libs / qmljs / qmljscodeformatter.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 (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "qmljscodeformatter.h"
34
35 #include <QtCore/QDebug>
36 #include <QtCore/QMetaEnum>
37 #include <QtGui/QTextDocument>
38 #include <QtGui/QTextCursor>
39 #include <QtGui/QTextBlock>
40
41 using namespace QmlJS;
42
43 CodeFormatter::BlockData::BlockData()
44     : m_indentDepth(0)
45     , m_blockRevision(-1)
46 {
47 }
48
49 CodeFormatter::CodeFormatter()
50     : m_indentDepth(0)
51     , m_tabSize(4)
52 {
53 }
54
55 CodeFormatter::~CodeFormatter()
56 {
57 }
58
59 void CodeFormatter::setTabSize(int tabSize)
60 {
61     m_tabSize = tabSize;
62 }
63
64 void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
65 {
66     restoreCurrentState(block.previous());
67
68     const int lexerState = tokenizeBlock(block);
69     m_tokenIndex = 0;
70     m_newStates.clear();
71
72     //qDebug() << "Starting to look at " << block.text() << block.blockNumber() + 1;
73
74     for (; m_tokenIndex < m_tokens.size(); ) {
75         m_currentToken = tokenAt(m_tokenIndex);
76         const int kind = extendedTokenKind(m_currentToken);
77
78         //qDebug() << "Token" << m_currentLine.mid(m_currentToken.begin(), m_currentToken.length) << m_tokenIndex << "in line" << block.blockNumber() + 1;
79         //dump();
80
81         if (kind == Comment
82                 && state().type != multiline_comment_cont
83                 && state().type != multiline_comment_start) {
84             m_tokenIndex += 1;
85             continue;
86         }
87
88         switch (m_currentState.top().type) {
89         case topmost_intro:
90             switch (kind) {
91             case Identifier:    enter(objectdefinition_or_js); continue;
92             case Import:        enter(top_qml); continue;
93             case LeftBrace:     enter(top_js); enter(expression); continue; // if a file starts with {, it's likely json
94             default:            enter(top_js); continue;
95             } break;
96
97         case top_qml:
98             switch (kind) {
99             case Import:        enter(import_start); break;
100             case Identifier:    enter(binding_or_objectdefinition); break;
101             } break;
102
103         case top_js:
104             tryStatement();
105             break;
106
107         case objectdefinition_or_js:
108             switch (kind) {
109             case Dot:           break;
110             case Identifier:
111                 if (!m_currentLine.at(m_currentToken.begin()).isUpper()) {
112                     turnInto(top_js);
113                     continue;
114                 }
115                 break;
116             case LeftBrace:     turnInto(binding_or_objectdefinition); continue;
117             default:            turnInto(top_js); continue;
118             } break;
119
120         case import_start:
121             enter(import_maybe_dot_or_version_or_as);
122             break;
123
124         case import_maybe_dot_or_version_or_as:
125             switch (kind) {
126             case Dot:           turnInto(import_dot); break;
127             case As:            turnInto(import_as); break;
128             case Number:        turnInto(import_maybe_as); break;
129             default:            leave(); leave(); continue;
130             } break;
131
132         case import_maybe_as:
133             switch (kind) {
134             case As:            turnInto(import_as); break;
135             default:            leave(); leave(); continue;
136             } break;
137
138         case import_dot:
139             switch (kind) {
140             case Identifier:    turnInto(import_maybe_dot_or_version_or_as); break;
141             default:            leave(); leave(); continue;
142             } break;
143
144         case import_as:
145             switch (kind) {
146             case Identifier:    leave(); leave(); break;
147             } break;
148
149         case binding_or_objectdefinition:
150             switch (kind) {
151             case Colon:         enter(binding_assignment); break;
152             case LeftBrace:     enter(objectdefinition_open); break;
153             } break;
154
155         case binding_assignment:
156             switch (kind) {
157             case Semicolon:     leave(true); break;
158             case If:            enter(if_statement); break;
159             case LeftBrace:     enter(jsblock_open); break;
160             case On:
161             case As:
162             case List:
163             case Import:
164             case Signal:
165             case Property:
166             case Identifier:    enter(expression_or_objectdefinition); break;
167             default:            enter(expression); continue;
168             } break;
169
170         case objectdefinition_open:
171             switch (kind) {
172             case RightBrace:    leave(true); break;
173             case Default:       enter(default_property_start); break;
174             case Property:      enter(property_start); break;
175             case Function:      enter(function_start); break;
176             case Signal:        enter(signal_start); break;
177             case On:
178             case As:
179             case List:
180             case Import:
181             case Identifier:    enter(binding_or_objectdefinition); break;
182             } break;
183
184         case default_property_start:
185             if (kind != Property)
186                 leave(true);
187             else
188                 turnInto(property_start);
189             break;
190
191         case property_start:
192             switch (kind) {
193             case Colon:         enter(binding_assignment); break; // oops, was a binding
194             case Var:
195             case Identifier:    enter(property_type); break;
196             case List:          enter(property_list_open); break;
197             default:            leave(true); continue;
198             } break;
199
200         case property_type:
201             turnInto(property_maybe_initializer);
202             break;
203
204         case property_list_open:
205             if (m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length) == QLatin1String(">"))
206                 turnInto(property_maybe_initializer);
207             break;
208
209         case property_maybe_initializer:
210             switch (kind) {
211             case Colon:         turnInto(binding_assignment); break;
212             default:            leave(true); continue;
213             } break;
214
215         case signal_start:
216             switch (kind) {
217             case Colon:         enter(binding_assignment); break; // oops, was a binding
218             default:            enter(signal_maybe_arglist); break;
219             } break;
220
221         case signal_maybe_arglist:
222             switch (kind) {
223             case LeftParenthesis:   turnInto(signal_arglist_open); break;
224             default:                leave(true); continue;
225             } break;
226
227         case signal_arglist_open:
228             switch (kind) {
229             case RightParenthesis:  leave(true); break;
230             } break;
231
232         case function_start:
233             switch (kind) {
234             case LeftParenthesis:   enter(function_arglist_open); break;
235             } break;
236
237         case function_arglist_open:
238             switch (kind) {
239             case RightParenthesis:  turnInto(function_arglist_closed); break;
240             } break;
241
242         case function_arglist_closed:
243             switch (kind) {
244             case LeftBrace:         turnInto(jsblock_open); break;
245             default:                leave(true); continue; // error recovery
246             } break;
247
248         case expression_or_objectdefinition:
249             switch (kind) {
250             case Dot:
251             case Identifier:    break; // need to become an objectdefinition_open in cases like "width: Qt.Foo {"
252             case LeftBrace:     turnInto(objectdefinition_open); break;
253             default:            enter(expression); continue; // really? identifier and more tokens might already be gone
254             } break;
255
256         case expression_or_label:
257             switch (kind) {
258             case Colon:             turnInto(labelled_statement); break;
259
260             // propagate 'leave' from expression state
261             case RightBracket:
262             case RightParenthesis:  leave(); continue;
263
264             default:                enter(expression); continue;
265             } break;
266
267         case ternary_op:
268             if (kind == Colon) {
269                 enter(ternary_op_after_colon);
270                 enter(expression_continuation);
271                 break;
272             }
273             // fallthrough
274         case ternary_op_after_colon:
275         case expression:
276             if (tryInsideExpression())
277                 break;
278             switch (kind) {
279             case Comma:
280             case Delimiter:         enter(expression_continuation); break;
281             case RightBracket:
282             case RightParenthesis:  leave(); continue;
283             case RightBrace:        leave(true); continue;
284             case Semicolon:         leave(true); break;
285             } break;
286
287         case expression_continuation:
288             leave();
289             continue;
290
291         case expression_maybe_continuation:
292             switch (kind) {
293             case Question:
294             case Delimiter:
295             case LeftBracket:
296             case LeftParenthesis:  leave(); continue;
297             default:               leave(true); continue;
298             } break;
299
300         case paren_open:
301             if (tryInsideExpression())
302                 break;
303             switch (kind) {
304             case RightParenthesis:  leave(); break;
305             } break;
306
307         case bracket_open:
308             if (tryInsideExpression())
309                 break;
310             switch (kind) {
311             case Comma:             enter(bracket_element_start); break;
312             case RightBracket:      leave(); break;
313             } break;
314
315         case objectliteral_open:
316             if (tryInsideExpression())
317                 break;
318             switch (kind) {
319             case Colon:             enter(objectliteral_assignment); break;
320             case RightBracket:
321             case RightParenthesis:  leave(); continue; // error recovery
322             case RightBrace:        leave(true); break;
323             } break;
324
325         // pretty much like expression, but ends with , or }
326         case objectliteral_assignment:
327             if (tryInsideExpression())
328                 break;
329             switch (kind) {
330             case Delimiter:         enter(expression_continuation); break;
331             case RightBracket:
332             case RightParenthesis:  leave(); continue; // error recovery
333             case RightBrace:        leave(); continue; // so we also leave objectliteral_open
334             case Comma:             leave(); break;
335             } break;
336
337         case bracket_element_start:
338             switch (kind) {
339             case Identifier:        turnInto(bracket_element_maybe_objectdefinition); break;
340             default:                leave(); continue;
341             } break;
342
343         case bracket_element_maybe_objectdefinition:
344             switch (kind) {
345             case LeftBrace:         turnInto(objectdefinition_open); break;
346             default:                leave(); continue;
347             } break;
348
349         case jsblock_open:
350         case substatement_open:
351             if (tryStatement())
352                 break;
353             switch (kind) {
354             case RightBrace:        leave(true); break;
355             } break;
356
357         case labelled_statement:
358             if (tryStatement())
359                 break;
360             leave(true); // error recovery
361             break;
362
363         case substatement:
364             // prefer substatement_open over block_open
365             if (kind != LeftBrace) {
366                 if (tryStatement())
367                     break;
368             }
369             switch (kind) {
370             case LeftBrace:         turnInto(substatement_open); break;
371             } break;
372
373         case if_statement:
374             switch (kind) {
375             case LeftParenthesis:   enter(condition_open); break;
376             default:                leave(true); break; // error recovery
377             } break;
378
379         case maybe_else:
380             if (kind == Else) {
381                 turnInto(else_clause);
382                 enter(substatement);
383                 break;
384             } else {
385                 leave(true);
386                 continue;
387             }
388
389         case else_clause:
390             // ### shouldn't happen
391             dump();
392             Q_ASSERT(false);
393             leave(true);
394             break;
395
396         case condition_open:
397             switch (kind) {
398             case RightParenthesis:  turnInto(substatement); break;
399             case LeftParenthesis:   enter(condition_paren_open); break;
400             } break;
401
402         // paren nesting
403         case condition_paren_open:
404             switch (kind) {
405             case RightParenthesis:  leave(); break;
406             case LeftParenthesis:   enter(condition_paren_open); break;
407             } break;
408
409         case switch_statement:
410         case statement_with_condition:
411             switch (kind) {
412             case LeftParenthesis:   enter(statement_with_condition_paren_open); break;
413             default:                leave(true);
414             } break;
415
416         case statement_with_condition_paren_open:
417             if (tryInsideExpression())
418                 break;
419             switch (kind) {
420             case RightParenthesis:  turnInto(substatement); break;
421             } break;
422
423         case statement_with_block:
424             switch (kind) {
425             case LeftBrace:         enter(jsblock_open); break;
426             default:                leave(true); break;
427             } break;
428
429         case do_statement:
430             switch (kind) {
431             case While:             break;
432             case LeftParenthesis:   enter(do_statement_while_paren_open); break;
433             default:                leave(true); break;
434             } break;
435
436         case do_statement_while_paren_open:
437             if (tryInsideExpression())
438                 break;
439             switch (kind) {
440             case RightParenthesis:  leave(); leave(true); break;
441             } break;
442
443         case breakcontinue_statement:
444             switch (kind) {
445             case Identifier:        leave(true); break;
446             default:                leave(true); continue; // try again
447             } break;
448
449         case case_start:
450             switch (kind) {
451             case Colon:             turnInto(case_cont); break;
452             } break;
453
454         case case_cont:
455             if (kind != Case && kind != Default && tryStatement())
456                 break;
457             switch (kind) {
458             case RightBrace:        leave(); continue;
459             case Default:
460             case Case:              leave(); continue;
461             } break;
462
463         case multiline_comment_start:
464         case multiline_comment_cont:
465             if (kind != Comment) {
466                 leave();
467                 continue;
468             } else if (m_tokenIndex == m_tokens.size() - 1
469                        && (lexerState & Scanner::MultiLineMask) == Scanner::Normal) {
470                 leave();
471             } else if (m_tokenIndex == 0) {
472                 // to allow enter/leave to update the indentDepth
473                 turnInto(multiline_comment_cont);
474             }
475             break;
476
477         default:
478             qWarning() << "Unhandled state" << m_currentState.top().type;
479             break;
480         } // end of state switch
481
482         ++m_tokenIndex;
483     }
484
485     int topState = m_currentState.top().type;
486
487     // if there's no colon on the same line, it's not a label
488     if (topState == expression_or_label)
489         enter(expression);
490     // if not followed by an identifier on the same line, it's done
491     else if (topState == breakcontinue_statement)
492         leave(true);
493
494     topState = m_currentState.top().type;
495
496     // some states might be continued on the next line
497     if (topState == expression
498             || topState == expression_or_objectdefinition
499             || topState == objectliteral_assignment
500             || topState == ternary_op_after_colon) {
501         enter(expression_maybe_continuation);
502     }
503     // multi-line comment start?
504     if (topState != multiline_comment_start
505             && topState != multiline_comment_cont
506             && (lexerState & Scanner::MultiLineMask) == Scanner::MultiLineComment) {
507         enter(multiline_comment_start);
508     }
509
510     saveCurrentState(block);
511 }
512
513 int CodeFormatter::indentFor(const QTextBlock &block)
514 {
515 //    qDebug() << "indenting for" << block.blockNumber() + 1;
516
517     restoreCurrentState(block.previous());
518     correctIndentation(block);
519     return m_indentDepth;
520 }
521
522 int CodeFormatter::indentForNewLineAfter(const QTextBlock &block)
523 {
524     restoreCurrentState(block);
525
526     int lexerState = loadLexerState(block);
527     m_tokens.clear();
528     m_currentLine.clear();
529     adjustIndent(m_tokens, lexerState, &m_indentDepth);
530
531     return m_indentDepth;
532 }
533
534 void CodeFormatter::updateStateUntil(const QTextBlock &endBlock)
535 {
536     QStack<State> previousState = initialState();
537     QTextBlock it = endBlock.document()->firstBlock();
538
539     // find the first block that needs recalculation
540     for (; it.isValid() && it != endBlock; it = it.next()) {
541         BlockData blockData;
542         if (!loadBlockData(it, &blockData))
543             break;
544         if (blockData.m_blockRevision != it.revision())
545             break;
546         if (previousState != blockData.m_beginState)
547             break;
548         if (loadLexerState(it) == -1)
549             break;
550
551         previousState = blockData.m_endState;
552     }
553
554     if (it == endBlock)
555         return;
556
557     // update everthing until endBlock
558     for (; it.isValid() && it != endBlock; it = it.next()) {
559         recalculateStateAfter(it);
560     }
561
562     // invalidate everything below by marking the state in endBlock as invalid
563     if (it.isValid()) {
564         BlockData invalidBlockData;
565         saveBlockData(&it, invalidBlockData);
566     }
567 }
568
569 void CodeFormatter::updateLineStateChange(const QTextBlock &block)
570 {
571     if (!block.isValid())
572         return;
573
574     BlockData blockData;
575     if (loadBlockData(block, &blockData) && blockData.m_blockRevision == block.revision())
576         return;
577
578     recalculateStateAfter(block);
579
580     // invalidate everything below by marking the next block's state as invalid
581     QTextBlock next = block.next();
582     if (!next.isValid())
583         return;
584
585     saveBlockData(&next, BlockData());
586 }
587
588 CodeFormatter::State CodeFormatter::state(int belowTop) const
589 {
590     if (belowTop < m_currentState.size())
591         return m_currentState.at(m_currentState.size() - 1 - belowTop);
592     else
593         return State();
594 }
595
596 const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const
597 {
598     return m_newStates;
599 }
600
601 int CodeFormatter::tokenIndex() const
602 {
603     return m_tokenIndex;
604 }
605
606 int CodeFormatter::tokenCount() const
607 {
608     return m_tokens.size();
609 }
610
611 const Token &CodeFormatter::currentToken() const
612 {
613     return m_currentToken;
614 }
615
616 void CodeFormatter::invalidateCache(QTextDocument *document)
617 {
618     if (!document)
619         return;
620
621     BlockData invalidBlockData;
622     QTextBlock it = document->firstBlock();
623     for (; it.isValid(); it = it.next()) {
624         saveBlockData(&it, invalidBlockData);
625     }
626 }
627
628 void CodeFormatter::enter(int newState)
629 {
630     int savedIndentDepth = m_indentDepth;
631     onEnter(newState, &m_indentDepth, &savedIndentDepth);
632     State s(newState, savedIndentDepth);
633     m_currentState.push(s);
634     m_newStates.push(s);
635
636     if (newState == bracket_open)
637         enter(bracket_element_start);
638 }
639
640 void CodeFormatter::leave(bool statementDone)
641 {
642     Q_ASSERT(m_currentState.size() > 1);
643     if (m_currentState.top().type == topmost_intro)
644         return;
645
646     if (m_newStates.size() > 0)
647         m_newStates.pop();
648
649     // restore indent depth
650     State poppedState = m_currentState.pop();
651     m_indentDepth = poppedState.savedIndentDepth;
652
653     int topState = m_currentState.top().type;
654
655     // if statement is done, may need to leave recursively
656     if (statementDone) {
657         if (!isExpressionEndState(topState))
658             leave(true);
659         if (topState == if_statement) {
660             if (poppedState.type != maybe_else)
661                 enter(maybe_else);
662             else
663                 leave(true);
664         } else if (topState == else_clause) {
665             // leave the else *and* the surrounding if, to prevent another else
666             leave();
667             leave(true);
668         }
669     }
670 }
671
672 void CodeFormatter::correctIndentation(const QTextBlock &block)
673 {
674     const int lexerState = tokenizeBlock(block);
675     Q_ASSERT(m_currentState.size() >= 1);
676
677     adjustIndent(m_tokens, lexerState, &m_indentDepth);
678 }
679
680 bool CodeFormatter::tryInsideExpression(bool alsoExpression)
681 {
682     int newState = -1;
683     const int kind = extendedTokenKind(m_currentToken);
684     switch (kind) {
685     case LeftParenthesis:   newState = paren_open; break;
686     case LeftBracket:       newState = bracket_open; break;
687     case LeftBrace:         newState = objectliteral_open; break;
688     case Function:          newState = function_start; break;
689     case Question:          newState = ternary_op; break;
690     }
691
692     if (newState != -1) {
693         if (alsoExpression)
694             enter(expression);
695         enter(newState);
696         return true;
697     }
698
699     return false;
700 }
701
702 bool CodeFormatter::tryStatement()
703 {
704     const int kind = extendedTokenKind(m_currentToken);
705     switch (kind) {
706     case Semicolon:
707         enter(empty_statement);
708         leave(true);
709         return true;
710     case Break:
711     case Continue:
712         enter(breakcontinue_statement);
713         return true;
714     case Throw:
715         enter(throw_statement);
716         enter(expression);
717         return true;
718     case Return:
719         enter(return_statement);
720         enter(expression);
721         return true;
722     case While:
723     case For:
724     case Catch:
725         enter(statement_with_condition);
726         return true;
727     case Switch:
728         enter(switch_statement);
729         return true;
730     case If:
731         enter(if_statement);
732         return true;
733     case Do:
734         enter(do_statement);
735         enter(substatement);
736         return true;
737     case Case:
738     case Default:
739         enter(case_start);
740         return true;
741     case Try:
742     case Finally:
743         enter(statement_with_block);
744         return true;
745     case LeftBrace:
746         enter(jsblock_open);
747         return true;
748     case Identifier:
749         enter(expression_or_label);
750         return true;
751     case Delimiter:
752     case Var:
753     case PlusPlus:
754     case MinusMinus:
755     case Import:
756     case Signal:
757     case On:
758     case As:
759     case List:
760     case Property:
761     case Function:
762     case Number:
763     case String:
764     case LeftParenthesis:
765         enter(expression);
766         // look at the token again
767         m_tokenIndex -= 1;
768         return true;
769     }
770     return false;
771 }
772
773 bool CodeFormatter::isBracelessState(int type) const
774 {
775     return
776             type == if_statement ||
777             type == else_clause ||
778             type == substatement ||
779             type == binding_assignment ||
780             type == binding_or_objectdefinition;
781 }
782
783 bool CodeFormatter::isExpressionEndState(int type) const
784 {
785     return
786             type == topmost_intro ||
787             type == top_js ||
788             type == objectdefinition_open ||
789             type == if_statement ||
790             type == else_clause ||
791             type == jsblock_open ||
792             type == substatement_open ||
793             type == bracket_open ||
794             type == paren_open ||
795             type == case_cont ||
796             type == objectliteral_open;
797 }
798
799 const Token &CodeFormatter::tokenAt(int idx) const
800 {
801     static const Token empty;
802     if (idx < 0 || idx >= m_tokens.size())
803         return empty;
804     else
805         return m_tokens.at(idx);
806 }
807
808 int CodeFormatter::column(int index) const
809 {
810     int col = 0;
811     if (index > m_currentLine.length())
812         index = m_currentLine.length();
813
814     const QChar tab = QLatin1Char('\t');
815
816     for (int i = 0; i < index; i++) {
817         if (m_currentLine[i] == tab) {
818             col = ((col / m_tabSize) + 1) * m_tabSize;
819         } else {
820             col++;
821         }
822     }
823     return col;
824 }
825
826 QStringRef CodeFormatter::currentTokenText() const
827 {
828     return m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length);
829 }
830
831 void CodeFormatter::turnInto(int newState)
832 {
833     leave(false);
834     enter(newState);
835 }
836
837 void CodeFormatter::saveCurrentState(const QTextBlock &block)
838 {
839     if (!block.isValid())
840         return;
841
842     BlockData blockData;
843     blockData.m_blockRevision = block.revision();
844     blockData.m_beginState = m_beginState;
845     blockData.m_endState = m_currentState;
846     blockData.m_indentDepth = m_indentDepth;
847
848     QTextBlock saveableBlock(block);
849     saveBlockData(&saveableBlock, blockData);
850 }
851
852 void CodeFormatter::restoreCurrentState(const QTextBlock &block)
853 {
854     if (block.isValid()) {
855         BlockData blockData;
856         if (loadBlockData(block, &blockData)) {
857             m_indentDepth = blockData.m_indentDepth;
858             m_currentState = blockData.m_endState;
859             m_beginState = m_currentState;
860             return;
861         }
862     }
863
864     m_currentState = initialState();
865     m_beginState = m_currentState;
866     m_indentDepth = 0;
867 }
868
869 QStack<CodeFormatter::State> CodeFormatter::initialState()
870 {
871     static QStack<CodeFormatter::State> initialState;
872     if (initialState.isEmpty())
873         initialState.push(State(topmost_intro, 0));
874     return initialState;
875 }
876
877 int CodeFormatter::tokenizeBlock(const QTextBlock &block)
878 {
879     int startState = loadLexerState(block.previous());
880     if (block.blockNumber() == 0)
881         startState = 0;
882     Q_ASSERT(startState != -1);
883
884     Scanner tokenize;
885     tokenize.setScanComments(true);
886
887     m_currentLine = block.text();
888     // to determine whether a line was joined, Tokenizer needs a
889     // newline character at the end
890     m_currentLine.append(QLatin1Char('\n'));
891     m_tokens = tokenize(m_currentLine, startState);
892
893     const int lexerState = tokenize.state();
894     QTextBlock saveableBlock(block);
895     saveLexerState(&saveableBlock, lexerState);
896     return lexerState;
897 }
898
899 CodeFormatter::TokenKind CodeFormatter::extendedTokenKind(const QmlJS::Token &token) const
900 {
901     const int kind = token.kind;
902     QStringRef text = m_currentLine.midRef(token.begin(), token.length);
903
904     if (kind == Identifier) {
905         if (text == "as")
906             return As;
907         if (text == "import")
908             return Import;
909         if (text == "signal")
910             return Signal;
911         if (text == "property")
912             return Property;
913         if (text == "on")
914             return On;
915         if (text == "list")
916             return On;
917     } else if (kind == Keyword) {
918         const QChar char1 = text.at(0);
919         const QChar char2 = text.at(1);
920         const QChar char3 = (text.size() > 2 ? text.at(2) : QChar());
921         switch (char1.toLatin1()) {
922         case 'v':
923             return Var;
924         case 'i':
925             if (char2 == 'f')
926                 return If;
927             else if (char3 == 's')
928                 return Instanceof;
929             else
930                 return In;
931         case 'f':
932             if (char2 == 'o')
933                 return For;
934             else if (char2 == 'u')
935                 return Function;
936             else
937                 return Finally;
938         case 'e':
939             return Else;
940         case 'n':
941             return New;
942         case 'r':
943             return Return;
944         case 's':
945             return Switch;
946         case 'w':
947             if (char2 == 'h')
948                 return While;
949             return With;
950         case 'c':
951             if (char3 == 's')
952                 return Case;
953             if (char3 == 't')
954                 return Catch;
955             return Continue;
956         case 'd':
957             if (char3 == 'l')
958                 return Delete;
959             if (char3 == 'f')
960                 return Default;
961             if (char3 == 'b')
962                 return Debugger;
963             return Do;
964         case 't':
965             if (char3 == 'i')
966                 return This;
967             if (char3 == 'y')
968                 return Try;
969             if (char3 == 'r')
970                 return Throw;
971             return Typeof;
972         case 'b':
973             return Break;
974         }
975     } else if (kind == Delimiter) {
976         if (text == "?")
977             return Question;
978         else if (text == "++")
979             return PlusPlus;
980         else if (text == "--")
981             return MinusMinus;
982     }
983
984     return static_cast<TokenKind>(kind);
985 }
986
987 void CodeFormatter::dump() const
988 {
989     QMetaEnum metaEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("StateType"));
990
991     qDebug() << "Current token index" << m_tokenIndex;
992     qDebug() << "Current state:";
993     foreach (const State &s, m_currentState) {
994         qDebug() << metaEnum.valueToKey(s.type) << s.savedIndentDepth;
995     }
996     qDebug() << "Current indent depth:" << m_indentDepth;
997 }
998
999 QtStyleCodeFormatter::QtStyleCodeFormatter()
1000     : m_indentSize(4)
1001 {}
1002
1003 void QtStyleCodeFormatter::setIndentSize(int size)
1004 {
1005     m_indentSize = size;
1006 }
1007
1008 void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth) const
1009 {
1010     const State &parentState = state();
1011     const Token &tk = currentToken();
1012     const int tokenPosition = column(tk.begin());
1013     const bool firstToken = (tokenIndex() == 0);
1014     const bool lastToken = (tokenIndex() == tokenCount() - 1);
1015
1016     switch (newState) {
1017     case objectdefinition_open: {
1018         // special case for things like "gradient: Gradient {"
1019         if (parentState.type == binding_assignment)
1020             *savedIndentDepth = state(1).savedIndentDepth;
1021
1022         if (firstToken)
1023             *savedIndentDepth = tokenPosition;
1024
1025         *indentDepth = *savedIndentDepth + m_indentSize;
1026         break;
1027     }
1028
1029     case binding_or_objectdefinition:
1030         if (firstToken)
1031             *indentDepth = *savedIndentDepth = tokenPosition;
1032         break;
1033
1034     case binding_assignment:
1035     case objectliteral_assignment:
1036         if (lastToken)
1037             *indentDepth = *savedIndentDepth + 4;
1038         else
1039             *indentDepth = column(tokenAt(tokenIndex() + 1).begin());
1040         break;
1041
1042     case expression_or_objectdefinition:
1043         *indentDepth = tokenPosition;
1044         break;
1045
1046     case expression_or_label:
1047         if (*indentDepth == tokenPosition)
1048             *indentDepth += 2*m_indentSize;
1049         else
1050             *indentDepth = tokenPosition;
1051         break;
1052
1053     case expression:
1054         if (*indentDepth == tokenPosition) {
1055             // expression_or_objectdefinition doesn't want the indent
1056             // expression_or_label already has it
1057             if (parentState.type != expression_or_objectdefinition
1058                     && parentState.type != expression_or_label
1059                     && parentState.type != binding_assignment) {
1060                 *indentDepth += 2*m_indentSize;
1061             }
1062         }
1063         // expression_or_objectdefinition and expression_or_label have already consumed the first token
1064         else if (parentState.type != expression_or_objectdefinition
1065                  && parentState.type != expression_or_label) {
1066             *indentDepth = tokenPosition;
1067         }
1068         break;
1069
1070     case expression_maybe_continuation:
1071         // set indent depth to indent we'd get if the expression ended here
1072         for (int i = 1; state(i).type != topmost_intro; ++i) {
1073             const int type = state(i).type;
1074             if (isExpressionEndState(type) && !isBracelessState(type)) {
1075                 *indentDepth = state(i - 1).savedIndentDepth;
1076                 break;
1077             }
1078         }
1079         break;
1080
1081     case bracket_open:
1082         if (parentState.type == expression && state(1).type == binding_assignment) {
1083             *savedIndentDepth = state(2).savedIndentDepth;
1084             *indentDepth = *savedIndentDepth + m_indentSize;
1085         } else if (parentState.type == objectliteral_assignment) {
1086             *savedIndentDepth = parentState.savedIndentDepth;
1087             *indentDepth = *savedIndentDepth + m_indentSize;
1088         } else if (!lastToken) {
1089             *indentDepth = tokenPosition + 1;
1090         } else {
1091             *indentDepth = *savedIndentDepth + m_indentSize;
1092         }
1093         break;
1094
1095     case function_start:
1096         if (parentState.type == expression) {
1097             // undo the continuation indent of the expression
1098             *indentDepth = parentState.savedIndentDepth;
1099             *savedIndentDepth = *indentDepth;
1100         } else {
1101             // always align to function keyword
1102             *indentDepth = tokenPosition;
1103             *savedIndentDepth = *indentDepth;
1104         }
1105         break;
1106
1107     case do_statement_while_paren_open:
1108     case statement_with_condition_paren_open:
1109     case signal_arglist_open:
1110     case function_arglist_open:
1111     case paren_open:
1112     case condition_paren_open:
1113         if (!lastToken)
1114             *indentDepth = tokenPosition + 1;
1115         else
1116             *indentDepth += m_indentSize;
1117         break;
1118
1119     case ternary_op:
1120         if (!lastToken)
1121             *indentDepth = tokenPosition + tk.length + 1;
1122         else
1123             *indentDepth += m_indentSize;
1124         break;
1125
1126     case jsblock_open:
1127         // closing brace should be aligned to case
1128         if (parentState.type == case_cont) {
1129             *savedIndentDepth = parentState.savedIndentDepth;
1130             break;
1131         }
1132         // fallthrough
1133     case substatement_open:
1134         // special case for "foo: {" and "property int foo: {"
1135         if (parentState.type == binding_assignment)
1136             *savedIndentDepth = state(1).savedIndentDepth;
1137         *indentDepth = *savedIndentDepth + m_indentSize;
1138         break;
1139
1140     case substatement:
1141         *indentDepth += m_indentSize;
1142         break;
1143
1144     case objectliteral_open:
1145         if (parentState.type == expression
1146                 || parentState.type == objectliteral_assignment) {
1147             // undo the continuation indent of the expression
1148             if (state(1).type == expression_or_label)
1149                 *indentDepth = state(1).savedIndentDepth;
1150             else
1151                 *indentDepth = parentState.savedIndentDepth;
1152             *savedIndentDepth = *indentDepth;
1153         }
1154         *indentDepth += m_indentSize;
1155         break;
1156
1157
1158     case statement_with_condition:
1159     case statement_with_block:
1160     case if_statement:
1161     case do_statement:
1162     case switch_statement:
1163         if (firstToken || parentState.type == binding_assignment)
1164             *savedIndentDepth = tokenPosition;
1165         // ### continuation
1166         *indentDepth = *savedIndentDepth; // + 2*m_indentSize;
1167         // special case for 'else if'
1168         if (!firstToken
1169                 && newState == if_statement
1170                 && parentState.type == substatement
1171                 && state(1).type == else_clause) {
1172             *indentDepth = state(1).savedIndentDepth;
1173             *savedIndentDepth = *indentDepth;
1174         }
1175         break;
1176
1177     case maybe_else: {
1178         // set indent to outermost braceless savedIndent
1179         int outermostBraceless = 0;
1180         while (isBracelessState(state(outermostBraceless + 1).type))
1181             ++outermostBraceless;
1182         *indentDepth = state(outermostBraceless).savedIndentDepth;
1183         // this is where the else should go, if one appears - aligned to if_statement
1184         *savedIndentDepth = state().savedIndentDepth;
1185         break;
1186     }
1187
1188     case condition_open:
1189         // fixed extra indent when continuing 'if (', but not for 'else if ('
1190         if (tokenPosition <= *indentDepth + m_indentSize)
1191             *indentDepth += 2*m_indentSize;
1192         else
1193             *indentDepth = tokenPosition + 1;
1194         break;
1195
1196     case case_start:
1197         *savedIndentDepth = tokenPosition;
1198         break;
1199
1200     case case_cont:
1201         *indentDepth += m_indentSize;
1202         break;
1203
1204     case multiline_comment_start:
1205         *indentDepth = tokenPosition + 2;
1206         break;
1207
1208     case multiline_comment_cont:
1209         *indentDepth = tokenPosition;
1210         break;
1211     }
1212 }
1213
1214 void QtStyleCodeFormatter::adjustIndent(const QList<Token> &tokens, int lexerState, int *indentDepth) const
1215 {
1216     Q_UNUSED(lexerState)
1217
1218     State topState = state();
1219     State previousState = state(1);
1220
1221     // keep user-adjusted indent in multiline comments
1222     if (topState.type == multiline_comment_start
1223             || topState.type == multiline_comment_cont) {
1224         if (!tokens.isEmpty()) {
1225             *indentDepth = column(tokens.at(0).begin());
1226             return;
1227         }
1228     }
1229
1230     const int kind = extendedTokenKind(tokenAt(0));
1231     switch (kind) {
1232     case LeftBrace:
1233         if (topState.type == substatement
1234                 || topState.type == binding_assignment
1235                 || topState.type == case_cont) {
1236             *indentDepth = topState.savedIndentDepth;
1237         }
1238         break;
1239     case RightBrace: {
1240         if (topState.type == jsblock_open && previousState.type == case_cont) {
1241             *indentDepth = previousState.savedIndentDepth;
1242             break;
1243         }
1244         for (int i = 0; state(i).type != topmost_intro; ++i) {
1245             const int type = state(i).type;
1246             if (type == objectdefinition_open
1247                     || type == jsblock_open
1248                     || type == substatement_open
1249                     || type == objectliteral_open) {
1250                 *indentDepth = state(i).savedIndentDepth;
1251                 break;
1252             }
1253         }
1254         break;
1255     }
1256     case RightBracket:
1257         for (int i = 0; state(i).type != topmost_intro; ++i) {
1258             const int type = state(i).type;
1259             if (type == bracket_open) {
1260                 *indentDepth = state(i).savedIndentDepth;
1261                 break;
1262             }
1263         }
1264         break;
1265
1266     case LeftBracket:
1267     case LeftParenthesis:
1268     case Delimiter:
1269         if (topState.type == expression_maybe_continuation)
1270             *indentDepth = topState.savedIndentDepth;
1271         break;
1272
1273     case Else:
1274         if (topState.type == maybe_else) {
1275             *indentDepth = topState.savedIndentDepth;
1276         } else if (topState.type == expression_maybe_continuation) {
1277             bool hasElse = false;
1278             for (int i = 1; state(i).type != topmost_intro; ++i) {
1279                 const int type = state(i).type;
1280                 if (type == else_clause)
1281                     hasElse = true;
1282                 if (type == if_statement) {
1283                     if (hasElse) {
1284                         hasElse = false;
1285                     } else {
1286                         *indentDepth = state(i).savedIndentDepth;
1287                         break;
1288                     }
1289                 }
1290             }
1291         }
1292         break;
1293
1294     case Colon:
1295         if (topState.type == ternary_op) {
1296             *indentDepth -= 2;
1297         }
1298         break;
1299
1300     case Question:
1301         if (topState.type == expression_maybe_continuation)
1302             *indentDepth = topState.savedIndentDepth;
1303         break;
1304
1305     case Default:
1306     case Case:
1307         for (int i = 0; state(i).type != topmost_intro; ++i) {
1308             const int type = state(i).type;
1309             if (type == switch_statement || type == case_cont) {
1310                 *indentDepth = state(i).savedIndentDepth;
1311                 break;
1312             } else if (type == topmost_intro) {
1313                 break;
1314             }
1315         }
1316         break;
1317     }
1318 }