OSDN Git Service

QmlJS indenter: Fix ternary multiline indent.
[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             default:            enter(top_js); continue;
94             } break;
95
96         case top_qml:
97             switch (kind) {
98             case Import:        enter(import_start); break;
99             case Identifier:    enter(binding_or_objectdefinition); break;
100             } break;
101
102         case top_js:
103             tryStatement();
104             break;
105
106         case objectdefinition_or_js:
107             switch (kind) {
108             case Dot:           break;
109             case Identifier:
110                 if (!m_currentLine.at(m_currentToken.begin()).isUpper()) {
111                     turnInto(top_js);
112                     continue;
113                 }
114                 break;
115             case LeftBrace:     turnInto(binding_or_objectdefinition); continue;
116             default:            turnInto(top_js); continue;
117             } break;
118
119         case import_start:
120             enter(import_maybe_dot_or_version_or_as);
121             break;
122
123         case import_maybe_dot_or_version_or_as:
124             switch (kind) {
125             case Dot:           turnInto(import_dot); break;
126             case As:            turnInto(import_as); break;
127             case Number:        turnInto(import_maybe_as); break;
128             default:            leave(); leave(); continue;
129             } break;
130
131         case import_maybe_as:
132             switch (kind) {
133             case As:            turnInto(import_as); break;
134             default:            leave(); leave(); continue;
135             } break;
136
137         case import_dot:
138             switch (kind) {
139             case Identifier:    turnInto(import_maybe_dot_or_version_or_as); break;
140             default:            leave(); leave(); continue;
141             } break;
142
143         case import_as:
144             switch (kind) {
145             case Identifier:    leave(); leave(); break;
146             } break;
147
148         case binding_or_objectdefinition:
149             switch (kind) {
150             case Colon:         enter(binding_assignment); break;
151             case LeftBrace:     enter(objectdefinition_open); break;
152             } break;
153
154         case binding_assignment:
155             switch (kind) {
156             case Semicolon:     leave(true); break;
157             case If:            enter(if_statement); break;
158             case LeftBrace:     enter(jsblock_open); break;
159             case On:
160             case As:
161             case List:
162             case Import:
163             case Signal:
164             case Property:
165             case Identifier:    enter(expression_or_objectdefinition); break;
166             default:            enter(expression); continue;
167             } break;
168
169         case objectdefinition_open:
170             switch (kind) {
171             case RightBrace:    leave(true); break;
172             case Default:       enter(default_property_start); break;
173             case Property:      enter(property_start); break;
174             case Function:      enter(function_start); break;
175             case Signal:        enter(signal_start); break;
176             case On:
177             case As:
178             case List:
179             case Import:
180             case Identifier:    enter(binding_or_objectdefinition); break;
181             } break;
182
183         case default_property_start:
184             if (kind != Property)
185                 leave(true);
186             else
187                 turnInto(property_start);
188             break;
189
190         case property_start:
191             switch (kind) {
192             case Colon:         enter(binding_assignment); break; // oops, was a binding
193             case Var:
194             case Identifier:    enter(property_type); break;
195             case List:          enter(property_list_open); break;
196             default:            leave(true); continue;
197             } break;
198
199         case property_type:
200             turnInto(property_maybe_initializer);
201             break;
202
203         case property_list_open:
204             if (m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length) == QLatin1String(">"))
205                 turnInto(property_maybe_initializer);
206             break;
207
208         case property_maybe_initializer:
209             switch (kind) {
210             case Colon:         turnInto(binding_assignment); break;
211             default:            leave(true); continue;
212             } break;
213
214         case signal_start:
215             switch (kind) {
216             case Colon:         enter(binding_assignment); break; // oops, was a binding
217             default:            enter(signal_maybe_arglist); break;
218             } break;
219
220         case signal_maybe_arglist:
221             switch (kind) {
222             case LeftParenthesis:   turnInto(signal_arglist_open); break;
223             default:                leave(true); continue;
224             } break;
225
226         case signal_arglist_open:
227             switch (kind) {
228             case RightParenthesis:  leave(true); break;
229             } break;
230
231         case function_start:
232             switch (kind) {
233             case LeftParenthesis:   enter(function_arglist_open); break;
234             } break;
235
236         case function_arglist_open:
237             switch (kind) {
238             case RightParenthesis:  turnInto(function_arglist_closed); break;
239             } break;
240
241         case function_arglist_closed:
242             switch (kind) {
243             case LeftBrace:         turnInto(jsblock_open); break;
244             default:                leave(true); continue; // error recovery
245             } break;
246
247         case expression_or_objectdefinition:
248             switch (kind) {
249             case Dot:
250             case Identifier:    break; // need to become an objectdefinition_open in cases like "width: Qt.Foo {"
251             case LeftBrace:     turnInto(objectdefinition_open); break;
252             default:            enter(expression); continue; // really? identifier and more tokens might already be gone
253             } break;
254
255         case expression_or_label:
256             switch (kind) {
257             case Colon:         turnInto(labelled_statement); break;
258             default:            enter(expression); continue;
259             } break;
260
261         case ternary_op:
262             if (kind == Colon) {
263                 enter(ternary_op_after_colon);
264                 enter(expression_continuation);
265                 break;
266             }
267             // fallthrough
268         case ternary_op_after_colon:
269         case expression:
270             if (tryInsideExpression())
271                 break;
272             switch (kind) {
273             case Comma:
274             case Delimiter:         enter(expression_continuation); break;
275             case RightBracket:
276             case RightParenthesis:  leave(); continue;
277             case RightBrace:        leave(true); continue;
278             case Semicolon:         leave(true); break;
279             } break;
280
281         case expression_continuation:
282             leave();
283             continue;
284
285         case expression_maybe_continuation:
286             switch (kind) {
287             case Question:
288             case Delimiter:
289             case LeftBracket:
290             case LeftParenthesis:  leave(); continue;
291             default:               leave(true); continue;
292             } break;
293
294         case paren_open:
295             if (tryInsideExpression())
296                 break;
297             switch (kind) {
298             case RightParenthesis:  leave(); break;
299             } break;
300
301         case bracket_open:
302             if (tryInsideExpression())
303                 break;
304             switch (kind) {
305             case Comma:             enter(bracket_element_start); break;
306             case RightBracket:      leave(); break;
307             } break;
308
309         case objectliteral_open:
310             if (tryInsideExpression())
311                 break;
312             switch (kind) {
313             case Colon:             enter(objectliteral_assignment); break;
314             case RightBracket:
315             case RightParenthesis:  leave(); continue; // error recovery
316             case RightBrace:        leave(true); break;
317             } break;
318
319         // pretty much like expression, but ends with , or }
320         case objectliteral_assignment:
321             if (tryInsideExpression())
322                 break;
323             switch (kind) {
324             case Delimiter:         enter(expression_continuation); break;
325             case RightBracket:
326             case RightParenthesis:  leave(); continue; // error recovery
327             case RightBrace:        leave(); continue; // so we also leave objectliteral_open
328             case Comma:             leave(); break;
329             } break;
330
331         case bracket_element_start:
332             switch (kind) {
333             case Identifier:        turnInto(bracket_element_maybe_objectdefinition); break;
334             default:                leave(); continue;
335             } break;
336
337         case bracket_element_maybe_objectdefinition:
338             switch (kind) {
339             case LeftBrace:         turnInto(objectdefinition_open); break;
340             default:                leave(); continue;
341             } break;
342
343         case jsblock_open:
344         case substatement_open:
345             if (tryStatement())
346                 break;
347             switch (kind) {
348             case RightBrace:        leave(true); break;
349             } break;
350
351         case labelled_statement:
352             if (tryStatement())
353                 break;
354             leave(true); // error recovery
355             break;
356
357         case substatement:
358             // prefer substatement_open over block_open
359             if (kind != LeftBrace) {
360                 if (tryStatement())
361                     break;
362             }
363             switch (kind) {
364             case LeftBrace:         turnInto(substatement_open); break;
365             } break;
366
367         case if_statement:
368             switch (kind) {
369             case LeftParenthesis:   enter(condition_open); break;
370             default:                leave(true); break; // error recovery
371             } break;
372
373         case maybe_else:
374             if (kind == Else) {
375                 turnInto(else_clause);
376                 enter(substatement);
377                 break;
378             } else {
379                 leave(true);
380                 continue;
381             }
382
383         case else_clause:
384             // ### shouldn't happen
385             dump();
386             Q_ASSERT(false);
387             leave(true);
388             break;
389
390         case condition_open:
391             switch (kind) {
392             case RightParenthesis:  turnInto(substatement); break;
393             case LeftParenthesis:   enter(condition_paren_open); break;
394             } break;
395
396         // paren nesting
397         case condition_paren_open:
398             switch (kind) {
399             case RightParenthesis:  leave(); break;
400             case LeftParenthesis:   enter(condition_paren_open); break;
401             } break;
402
403         case switch_statement:
404         case statement_with_condition:
405             switch (kind) {
406             case LeftParenthesis:   enter(statement_with_condition_paren_open); break;
407             default:                leave(true);
408             } break;
409
410         case statement_with_condition_paren_open:
411             if (tryInsideExpression())
412                 break;
413             switch (kind) {
414             case RightParenthesis:  turnInto(substatement); break;
415             } break;
416
417         case statement_with_block:
418             switch (kind) {
419             case LeftBrace:         enter(jsblock_open); break;
420             default:                leave(true); break;
421             } break;
422
423         case do_statement:
424             switch (kind) {
425             case While:             break;
426             case LeftParenthesis:   enter(do_statement_while_paren_open); break;
427             default:                leave(true); break;
428             } break;
429
430         case do_statement_while_paren_open:
431             if (tryInsideExpression())
432                 break;
433             switch (kind) {
434             case RightParenthesis:  leave(); leave(true); break;
435             } break;
436
437         case breakcontinue_statement:
438             switch (kind) {
439             case Identifier:        leave(true); break;
440             default:                leave(true); continue; // try again
441             } break;
442
443         case case_start:
444             switch (kind) {
445             case Colon:             turnInto(case_cont); break;
446             } break;
447
448         case case_cont:
449             if (kind != Case && kind != Default && tryStatement())
450                 break;
451             switch (kind) {
452             case RightBrace:        leave(); continue;
453             case Default:
454             case Case:              leave(); continue;
455             } break;
456
457         case multiline_comment_start:
458         case multiline_comment_cont:
459             if (kind != Comment) {
460                 leave();
461                 continue;
462             } else if (m_tokenIndex == m_tokens.size() - 1
463                        && (lexerState & Scanner::MultiLineMask) == Scanner::Normal) {
464                 leave();
465             } else if (m_tokenIndex == 0) {
466                 // to allow enter/leave to update the indentDepth
467                 turnInto(multiline_comment_cont);
468             }
469             break;
470
471         default:
472             qWarning() << "Unhandled state" << m_currentState.top().type;
473             break;
474         } // end of state switch
475
476         ++m_tokenIndex;
477     }
478
479     int topState = m_currentState.top().type;
480
481     // if there's no colon on the same line, it's not a label
482     if (topState == expression_or_label)
483         enter(expression);
484     // if not followed by an identifier on the same line, it's done
485     else if (topState == breakcontinue_statement)
486         leave(true);
487
488     topState = m_currentState.top().type;
489
490     // some states might be continued on the next line
491     if (topState == expression
492             || topState == expression_or_objectdefinition
493             || topState == objectliteral_assignment
494             || topState == ternary_op_after_colon) {
495         enter(expression_maybe_continuation);
496     }
497     // multi-line comment start?
498     if (topState != multiline_comment_start
499             && topState != multiline_comment_cont
500             && (lexerState & Scanner::MultiLineMask) == Scanner::MultiLineComment) {
501         enter(multiline_comment_start);
502     }
503
504     saveCurrentState(block);
505 }
506
507 int CodeFormatter::indentFor(const QTextBlock &block)
508 {
509 //    qDebug() << "indenting for" << block.blockNumber() + 1;
510
511     restoreCurrentState(block.previous());
512     correctIndentation(block);
513     return m_indentDepth;
514 }
515
516 int CodeFormatter::indentForNewLineAfter(const QTextBlock &block)
517 {
518     restoreCurrentState(block);
519
520     int lexerState = loadLexerState(block);
521     m_tokens.clear();
522     m_currentLine.clear();
523     adjustIndent(m_tokens, lexerState, &m_indentDepth);
524
525     return m_indentDepth;
526 }
527
528 void CodeFormatter::updateStateUntil(const QTextBlock &endBlock)
529 {
530     QStack<State> previousState = initialState();
531     QTextBlock it = endBlock.document()->firstBlock();
532
533     // find the first block that needs recalculation
534     for (; it.isValid() && it != endBlock; it = it.next()) {
535         BlockData blockData;
536         if (!loadBlockData(it, &blockData))
537             break;
538         if (blockData.m_blockRevision != it.revision())
539             break;
540         if (previousState != blockData.m_beginState)
541             break;
542         if (loadLexerState(it) == -1)
543             break;
544
545         previousState = blockData.m_endState;
546     }
547
548     if (it == endBlock)
549         return;
550
551     // update everthing until endBlock
552     for (; it.isValid() && it != endBlock; it = it.next()) {
553         recalculateStateAfter(it);
554     }
555
556     // invalidate everything below by marking the state in endBlock as invalid
557     if (it.isValid()) {
558         BlockData invalidBlockData;
559         saveBlockData(&it, invalidBlockData);
560     }
561 }
562
563 void CodeFormatter::updateLineStateChange(const QTextBlock &block)
564 {
565     if (!block.isValid())
566         return;
567
568     BlockData blockData;
569     if (loadBlockData(block, &blockData) && blockData.m_blockRevision == block.revision())
570         return;
571
572     recalculateStateAfter(block);
573
574     // invalidate everything below by marking the next block's state as invalid
575     QTextBlock next = block.next();
576     if (!next.isValid())
577         return;
578
579     saveBlockData(&next, BlockData());
580 }
581
582 CodeFormatter::State CodeFormatter::state(int belowTop) const
583 {
584     if (belowTop < m_currentState.size())
585         return m_currentState.at(m_currentState.size() - 1 - belowTop);
586     else
587         return State();
588 }
589
590 const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const
591 {
592     return m_newStates;
593 }
594
595 int CodeFormatter::tokenIndex() const
596 {
597     return m_tokenIndex;
598 }
599
600 int CodeFormatter::tokenCount() const
601 {
602     return m_tokens.size();
603 }
604
605 const Token &CodeFormatter::currentToken() const
606 {
607     return m_currentToken;
608 }
609
610 void CodeFormatter::invalidateCache(QTextDocument *document)
611 {
612     if (!document)
613         return;
614
615     BlockData invalidBlockData;
616     QTextBlock it = document->firstBlock();
617     for (; it.isValid(); it = it.next()) {
618         saveBlockData(&it, invalidBlockData);
619     }
620 }
621
622 void CodeFormatter::enter(int newState)
623 {
624     int savedIndentDepth = m_indentDepth;
625     onEnter(newState, &m_indentDepth, &savedIndentDepth);
626     State s(newState, savedIndentDepth);
627     m_currentState.push(s);
628     m_newStates.push(s);
629
630     if (newState == bracket_open)
631         enter(bracket_element_start);
632 }
633
634 void CodeFormatter::leave(bool statementDone)
635 {
636     Q_ASSERT(m_currentState.size() > 1);
637     if (m_currentState.top().type == topmost_intro)
638         return;
639
640     if (m_newStates.size() > 0)
641         m_newStates.pop();
642
643     // restore indent depth
644     State poppedState = m_currentState.pop();
645     m_indentDepth = poppedState.savedIndentDepth;
646
647     int topState = m_currentState.top().type;
648
649     // if statement is done, may need to leave recursively
650     if (statementDone) {
651         if (!isExpressionEndState(topState))
652             leave(true);
653         if (topState == if_statement) {
654             if (poppedState.type != maybe_else)
655                 enter(maybe_else);
656             else
657                 leave(true);
658         } else if (topState == else_clause) {
659             // leave the else *and* the surrounding if, to prevent another else
660             leave();
661             leave(true);
662         }
663     }
664 }
665
666 void CodeFormatter::correctIndentation(const QTextBlock &block)
667 {
668     const int lexerState = tokenizeBlock(block);
669     Q_ASSERT(m_currentState.size() >= 1);
670
671     adjustIndent(m_tokens, lexerState, &m_indentDepth);
672 }
673
674 bool CodeFormatter::tryInsideExpression(bool alsoExpression)
675 {
676     int newState = -1;
677     const int kind = extendedTokenKind(m_currentToken);
678     switch (kind) {
679     case LeftParenthesis:   newState = paren_open; break;
680     case LeftBracket:       newState = bracket_open; break;
681     case LeftBrace:         newState = objectliteral_open; break;
682     case Function:          newState = function_start; break;
683     case Question:          newState = ternary_op; break;
684     }
685
686     if (newState != -1) {
687         if (alsoExpression)
688             enter(expression);
689         enter(newState);
690         return true;
691     }
692
693     return false;
694 }
695
696 bool CodeFormatter::tryStatement()
697 {
698     const int kind = extendedTokenKind(m_currentToken);
699     switch (kind) {
700     case Semicolon:
701         enter(empty_statement);
702         leave(true);
703         return true;
704     case Break:
705     case Continue:
706         enter(breakcontinue_statement);
707         return true;
708     case Throw:
709         enter(throw_statement);
710         enter(expression);
711         return true;
712     case Return:
713         enter(return_statement);
714         enter(expression);
715         return true;
716     case While:
717     case For:
718     case Catch:
719         enter(statement_with_condition);
720         return true;
721     case Switch:
722         enter(switch_statement);
723         return true;
724     case If:
725         enter(if_statement);
726         return true;
727     case Do:
728         enter(do_statement);
729         enter(substatement);
730         return true;
731     case Case:
732     case Default:
733         enter(case_start);
734         return true;
735     case Try:
736     case Finally:
737         enter(statement_with_block);
738         return true;
739     case LeftBrace:
740         enter(jsblock_open);
741         return true;
742     case Identifier:
743         enter(expression_or_label);
744         return true;
745     case Delimiter:
746     case Var:
747     case PlusPlus:
748     case MinusMinus:
749     case Import:
750     case Signal:
751     case On:
752     case As:
753     case List:
754     case Property:
755     case Function:
756     case Number:
757     case String:
758         enter(expression);
759         // look at the token again
760         m_tokenIndex -= 1;
761         return true;
762     }
763     return false;
764 }
765
766 bool CodeFormatter::isBracelessState(int type) const
767 {
768     return
769             type == if_statement ||
770             type == else_clause ||
771             type == substatement ||
772             type == binding_assignment ||
773             type == binding_or_objectdefinition;
774 }
775
776 bool CodeFormatter::isExpressionEndState(int type) const
777 {
778     return
779             type == topmost_intro ||
780             type == top_js ||
781             type == objectdefinition_open ||
782             type == if_statement ||
783             type == else_clause ||
784             type == jsblock_open ||
785             type == substatement_open ||
786             type == bracket_open ||
787             type == paren_open ||
788             type == case_cont ||
789             type == objectliteral_open;
790 }
791
792 const Token &CodeFormatter::tokenAt(int idx) const
793 {
794     static const Token empty;
795     if (idx < 0 || idx >= m_tokens.size())
796         return empty;
797     else
798         return m_tokens.at(idx);
799 }
800
801 int CodeFormatter::column(int index) const
802 {
803     int col = 0;
804     if (index > m_currentLine.length())
805         index = m_currentLine.length();
806
807     const QChar tab = QLatin1Char('\t');
808
809     for (int i = 0; i < index; i++) {
810         if (m_currentLine[i] == tab) {
811             col = ((col / m_tabSize) + 1) * m_tabSize;
812         } else {
813             col++;
814         }
815     }
816     return col;
817 }
818
819 QStringRef CodeFormatter::currentTokenText() const
820 {
821     return m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length);
822 }
823
824 void CodeFormatter::turnInto(int newState)
825 {
826     leave(false);
827     enter(newState);
828 }
829
830 void CodeFormatter::saveCurrentState(const QTextBlock &block)
831 {
832     if (!block.isValid())
833         return;
834
835     BlockData blockData;
836     blockData.m_blockRevision = block.revision();
837     blockData.m_beginState = m_beginState;
838     blockData.m_endState = m_currentState;
839     blockData.m_indentDepth = m_indentDepth;
840
841     QTextBlock saveableBlock(block);
842     saveBlockData(&saveableBlock, blockData);
843 }
844
845 void CodeFormatter::restoreCurrentState(const QTextBlock &block)
846 {
847     if (block.isValid()) {
848         BlockData blockData;
849         if (loadBlockData(block, &blockData)) {
850             m_indentDepth = blockData.m_indentDepth;
851             m_currentState = blockData.m_endState;
852             m_beginState = m_currentState;
853             return;
854         }
855     }
856
857     m_currentState = initialState();
858     m_beginState = m_currentState;
859     m_indentDepth = 0;
860 }
861
862 QStack<CodeFormatter::State> CodeFormatter::initialState()
863 {
864     static QStack<CodeFormatter::State> initialState;
865     if (initialState.isEmpty())
866         initialState.push(State(topmost_intro, 0));
867     return initialState;
868 }
869
870 int CodeFormatter::tokenizeBlock(const QTextBlock &block)
871 {
872     int startState = loadLexerState(block.previous());
873     if (block.blockNumber() == 0)
874         startState = 0;
875     Q_ASSERT(startState != -1);
876
877     Scanner tokenize;
878     tokenize.setScanComments(true);
879
880     m_currentLine = block.text();
881     // to determine whether a line was joined, Tokenizer needs a
882     // newline character at the end
883     m_currentLine.append(QLatin1Char('\n'));
884     m_tokens = tokenize(m_currentLine, startState);
885
886     const int lexerState = tokenize.state();
887     QTextBlock saveableBlock(block);
888     saveLexerState(&saveableBlock, lexerState);
889     return lexerState;
890 }
891
892 CodeFormatter::TokenKind CodeFormatter::extendedTokenKind(const QmlJS::Token &token) const
893 {
894     const int kind = token.kind;
895     QStringRef text = m_currentLine.midRef(token.begin(), token.length);
896
897     if (kind == Identifier) {
898         if (text == "as")
899             return As;
900         if (text == "import")
901             return Import;
902         if (text == "signal")
903             return Signal;
904         if (text == "property")
905             return Property;
906         if (text == "on")
907             return On;
908         if (text == "list")
909             return On;
910     } else if (kind == Keyword) {
911         const QChar char1 = text.at(0);
912         const QChar char2 = text.at(1);
913         const QChar char3 = (text.size() > 2 ? text.at(2) : QChar());
914         switch (char1.toLatin1()) {
915         case 'v':
916             return Var;
917         case 'i':
918             if (char2 == 'f')
919                 return If;
920             else if (char3 == 's')
921                 return Instanceof;
922             else
923                 return In;
924         case 'f':
925             if (char2 == 'o')
926                 return For;
927             else if (char2 == 'u')
928                 return Function;
929             else
930                 return Finally;
931         case 'e':
932             return Else;
933         case 'n':
934             return New;
935         case 'r':
936             return Return;
937         case 's':
938             return Switch;
939         case 'w':
940             if (char2 == 'h')
941                 return While;
942             return With;
943         case 'c':
944             if (char3 == 's')
945                 return Case;
946             if (char3 == 't')
947                 return Catch;
948             return Continue;
949         case 'd':
950             if (char3 == 'l')
951                 return Delete;
952             if (char3 == 'f')
953                 return Default;
954             if (char3 == 'b')
955                 return Debugger;
956             return Do;
957         case 't':
958             if (char3 == 'i')
959                 return This;
960             if (char3 == 'y')
961                 return Try;
962             if (char3 == 'r')
963                 return Throw;
964             return Typeof;
965         case 'b':
966             return Break;
967         }
968     } else if (kind == Delimiter) {
969         if (text == "?")
970             return Question;
971         else if (text == "++")
972             return PlusPlus;
973         else if (text == "--")
974             return MinusMinus;
975     }
976
977     return static_cast<TokenKind>(kind);
978 }
979
980 void CodeFormatter::dump() const
981 {
982     QMetaEnum metaEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("StateType"));
983
984     qDebug() << "Current token index" << m_tokenIndex;
985     qDebug() << "Current state:";
986     foreach (const State &s, m_currentState) {
987         qDebug() << metaEnum.valueToKey(s.type) << s.savedIndentDepth;
988     }
989     qDebug() << "Current indent depth:" << m_indentDepth;
990 }