1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "qmljscodeformatter.h"
35 #include <QtCore/QDebug>
36 #include <QtCore/QMetaEnum>
37 #include <QtGui/QTextDocument>
38 #include <QtGui/QTextCursor>
39 #include <QtGui/QTextBlock>
41 using namespace QmlJS;
43 CodeFormatter::BlockData::BlockData()
49 CodeFormatter::CodeFormatter()
55 CodeFormatter::~CodeFormatter()
59 void CodeFormatter::setTabSize(int tabSize)
64 void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
66 restoreCurrentState(block.previous());
68 const int lexerState = tokenizeBlock(block);
72 //qDebug() << "Starting to look at " << block.text() << block.blockNumber() + 1;
74 for (; m_tokenIndex < m_tokens.size(); ) {
75 m_currentToken = tokenAt(m_tokenIndex);
76 const int kind = extendedTokenKind(m_currentToken);
78 //qDebug() << "Token" << m_currentLine.mid(m_currentToken.begin(), m_currentToken.length) << m_tokenIndex << "in line" << block.blockNumber() + 1;
82 && state().type != multiline_comment_cont
83 && state().type != multiline_comment_start) {
88 switch (m_currentState.top().type) {
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;
99 case Import: enter(import_start); break;
100 case Identifier: enter(binding_or_objectdefinition); break;
107 case objectdefinition_or_js:
111 if (!m_currentLine.at(m_currentToken.begin()).isUpper()) {
116 case LeftBrace: turnInto(binding_or_objectdefinition); continue;
117 default: turnInto(top_js); continue;
121 enter(import_maybe_dot_or_version_or_as);
124 case import_maybe_dot_or_version_or_as:
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;
132 case import_maybe_as:
134 case As: turnInto(import_as); break;
135 default: leave(); leave(); continue;
140 case Identifier: turnInto(import_maybe_dot_or_version_or_as); break;
141 default: leave(); leave(); continue;
146 case Identifier: leave(); leave(); break;
149 case binding_or_objectdefinition:
151 case Colon: enter(binding_assignment); break;
152 case LeftBrace: enter(objectdefinition_open); break;
155 case binding_assignment:
157 case Semicolon: leave(true); break;
158 case If: enter(if_statement); break;
159 case LeftBrace: enter(jsblock_open); break;
166 case Identifier: enter(expression_or_objectdefinition); break;
167 default: enter(expression); continue;
170 case objectdefinition_open:
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;
181 case Identifier: enter(binding_or_objectdefinition); break;
184 case default_property_start:
185 if (kind != Property)
188 turnInto(property_start);
193 case Colon: enter(binding_assignment); break; // oops, was a binding
195 case Identifier: enter(property_type); break;
196 case List: enter(property_list_open); break;
197 default: leave(true); continue;
201 turnInto(property_maybe_initializer);
204 case property_list_open:
205 if (m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length) == QLatin1String(">"))
206 turnInto(property_maybe_initializer);
209 case property_maybe_initializer:
211 case Colon: turnInto(binding_assignment); break;
212 default: leave(true); continue;
217 case Colon: enter(binding_assignment); break; // oops, was a binding
218 default: enter(signal_maybe_arglist); break;
221 case signal_maybe_arglist:
223 case LeftParenthesis: turnInto(signal_arglist_open); break;
224 default: leave(true); continue;
227 case signal_arglist_open:
229 case RightParenthesis: leave(true); break;
234 case LeftParenthesis: enter(function_arglist_open); break;
237 case function_arglist_open:
239 case RightParenthesis: turnInto(function_arglist_closed); break;
242 case function_arglist_closed:
244 case LeftBrace: turnInto(jsblock_open); break;
245 default: leave(true); continue; // error recovery
248 case expression_or_objectdefinition:
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
256 case expression_or_label:
258 case Colon: turnInto(labelled_statement); break;
260 // propagate 'leave' from expression state
262 case RightParenthesis: leave(); continue;
264 default: enter(expression); continue;
269 enter(ternary_op_after_colon);
270 enter(expression_continuation);
274 case ternary_op_after_colon:
276 if (tryInsideExpression())
280 case Delimiter: enter(expression_continuation); break;
282 case RightParenthesis: leave(); continue;
283 case RightBrace: leave(true); continue;
284 case Semicolon: leave(true); break;
287 case expression_continuation:
291 case expression_maybe_continuation:
296 case LeftParenthesis: leave(); continue;
297 default: leave(true); continue;
301 if (tryInsideExpression())
304 case RightParenthesis: leave(); break;
308 if (tryInsideExpression())
311 case Comma: enter(bracket_element_start); break;
312 case RightBracket: leave(); break;
315 case objectliteral_open:
316 if (tryInsideExpression())
319 case Colon: enter(objectliteral_assignment); break;
321 case RightParenthesis: leave(); continue; // error recovery
322 case RightBrace: leave(true); break;
325 // pretty much like expression, but ends with , or }
326 case objectliteral_assignment:
327 if (tryInsideExpression())
330 case Delimiter: enter(expression_continuation); break;
332 case RightParenthesis: leave(); continue; // error recovery
333 case RightBrace: leave(); continue; // so we also leave objectliteral_open
334 case Comma: leave(); break;
337 case bracket_element_start:
339 case Identifier: turnInto(bracket_element_maybe_objectdefinition); break;
340 default: leave(); continue;
343 case bracket_element_maybe_objectdefinition:
345 case LeftBrace: turnInto(objectdefinition_open); break;
346 default: leave(); continue;
350 case substatement_open:
354 case RightBrace: leave(true); break;
357 case labelled_statement:
360 leave(true); // error recovery
364 // prefer substatement_open over block_open
365 if (kind != LeftBrace) {
370 case LeftBrace: turnInto(substatement_open); break;
375 case LeftParenthesis: enter(condition_open); break;
376 default: leave(true); break; // error recovery
381 turnInto(else_clause);
390 // ### shouldn't happen
398 case RightParenthesis: turnInto(substatement); break;
399 case LeftParenthesis: enter(condition_paren_open); break;
403 case condition_paren_open:
405 case RightParenthesis: leave(); break;
406 case LeftParenthesis: enter(condition_paren_open); break;
409 case switch_statement:
410 case statement_with_condition:
412 case LeftParenthesis: enter(statement_with_condition_paren_open); break;
413 default: leave(true);
416 case statement_with_condition_paren_open:
417 if (tryInsideExpression())
420 case RightParenthesis: turnInto(substatement); break;
423 case statement_with_block:
425 case LeftBrace: enter(jsblock_open); break;
426 default: leave(true); break;
432 case LeftParenthesis: enter(do_statement_while_paren_open); break;
433 default: leave(true); break;
436 case do_statement_while_paren_open:
437 if (tryInsideExpression())
440 case RightParenthesis: leave(); leave(true); break;
443 case breakcontinue_statement:
445 case Identifier: leave(true); break;
446 default: leave(true); continue; // try again
451 case Colon: turnInto(case_cont); break;
455 if (kind != Case && kind != Default && tryStatement())
458 case RightBrace: leave(); continue;
460 case Case: leave(); continue;
463 case multiline_comment_start:
464 case multiline_comment_cont:
465 if (kind != Comment) {
468 } else if (m_tokenIndex == m_tokens.size() - 1
469 && (lexerState & Scanner::MultiLineMask) == Scanner::Normal) {
471 } else if (m_tokenIndex == 0) {
472 // to allow enter/leave to update the indentDepth
473 turnInto(multiline_comment_cont);
478 qWarning() << "Unhandled state" << m_currentState.top().type;
480 } // end of state switch
485 int topState = m_currentState.top().type;
487 // if there's no colon on the same line, it's not a label
488 if (topState == expression_or_label)
490 // if not followed by an identifier on the same line, it's done
491 else if (topState == breakcontinue_statement)
494 topState = m_currentState.top().type;
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);
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);
510 saveCurrentState(block);
513 int CodeFormatter::indentFor(const QTextBlock &block)
515 // qDebug() << "indenting for" << block.blockNumber() + 1;
517 restoreCurrentState(block.previous());
518 correctIndentation(block);
519 return m_indentDepth;
522 int CodeFormatter::indentForNewLineAfter(const QTextBlock &block)
524 restoreCurrentState(block);
526 int lexerState = loadLexerState(block);
528 m_currentLine.clear();
529 adjustIndent(m_tokens, lexerState, &m_indentDepth);
531 return m_indentDepth;
534 void CodeFormatter::updateStateUntil(const QTextBlock &endBlock)
536 QStack<State> previousState = initialState();
537 QTextBlock it = endBlock.document()->firstBlock();
539 // find the first block that needs recalculation
540 for (; it.isValid() && it != endBlock; it = it.next()) {
542 if (!loadBlockData(it, &blockData))
544 if (blockData.m_blockRevision != it.revision())
546 if (previousState != blockData.m_beginState)
548 if (loadLexerState(it) == -1)
551 previousState = blockData.m_endState;
557 // update everthing until endBlock
558 for (; it.isValid() && it != endBlock; it = it.next()) {
559 recalculateStateAfter(it);
562 // invalidate everything below by marking the state in endBlock as invalid
564 BlockData invalidBlockData;
565 saveBlockData(&it, invalidBlockData);
569 void CodeFormatter::updateLineStateChange(const QTextBlock &block)
571 if (!block.isValid())
575 if (loadBlockData(block, &blockData) && blockData.m_blockRevision == block.revision())
578 recalculateStateAfter(block);
580 // invalidate everything below by marking the next block's state as invalid
581 QTextBlock next = block.next();
585 saveBlockData(&next, BlockData());
588 CodeFormatter::State CodeFormatter::state(int belowTop) const
590 if (belowTop < m_currentState.size())
591 return m_currentState.at(m_currentState.size() - 1 - belowTop);
596 const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const
601 int CodeFormatter::tokenIndex() const
606 int CodeFormatter::tokenCount() const
608 return m_tokens.size();
611 const Token &CodeFormatter::currentToken() const
613 return m_currentToken;
616 void CodeFormatter::invalidateCache(QTextDocument *document)
621 BlockData invalidBlockData;
622 QTextBlock it = document->firstBlock();
623 for (; it.isValid(); it = it.next()) {
624 saveBlockData(&it, invalidBlockData);
628 void CodeFormatter::enter(int newState)
630 int savedIndentDepth = m_indentDepth;
631 onEnter(newState, &m_indentDepth, &savedIndentDepth);
632 State s(newState, savedIndentDepth);
633 m_currentState.push(s);
636 if (newState == bracket_open)
637 enter(bracket_element_start);
640 void CodeFormatter::leave(bool statementDone)
642 Q_ASSERT(m_currentState.size() > 1);
643 if (m_currentState.top().type == topmost_intro)
646 if (m_newStates.size() > 0)
649 // restore indent depth
650 State poppedState = m_currentState.pop();
651 m_indentDepth = poppedState.savedIndentDepth;
653 int topState = m_currentState.top().type;
655 // if statement is done, may need to leave recursively
657 if (!isExpressionEndState(topState))
659 if (topState == if_statement) {
660 if (poppedState.type != maybe_else)
664 } else if (topState == else_clause) {
665 // leave the else *and* the surrounding if, to prevent another else
672 void CodeFormatter::correctIndentation(const QTextBlock &block)
674 const int lexerState = tokenizeBlock(block);
675 Q_ASSERT(m_currentState.size() >= 1);
677 adjustIndent(m_tokens, lexerState, &m_indentDepth);
680 bool CodeFormatter::tryInsideExpression(bool alsoExpression)
683 const int kind = extendedTokenKind(m_currentToken);
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;
692 if (newState != -1) {
702 bool CodeFormatter::tryStatement()
704 const int kind = extendedTokenKind(m_currentToken);
707 enter(empty_statement);
712 enter(breakcontinue_statement);
715 enter(throw_statement);
719 enter(return_statement);
725 enter(statement_with_condition);
728 enter(switch_statement);
743 enter(statement_with_block);
749 enter(expression_or_label);
764 case LeftParenthesis:
766 // look at the token again
773 bool CodeFormatter::isBracelessState(int type) const
776 type == if_statement ||
777 type == else_clause ||
778 type == substatement ||
779 type == binding_assignment ||
780 type == binding_or_objectdefinition;
783 bool CodeFormatter::isExpressionEndState(int type) const
786 type == topmost_intro ||
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 ||
796 type == objectliteral_open;
799 const Token &CodeFormatter::tokenAt(int idx) const
801 static const Token empty;
802 if (idx < 0 || idx >= m_tokens.size())
805 return m_tokens.at(idx);
808 int CodeFormatter::column(int index) const
811 if (index > m_currentLine.length())
812 index = m_currentLine.length();
814 const QChar tab = QLatin1Char('\t');
816 for (int i = 0; i < index; i++) {
817 if (m_currentLine[i] == tab) {
818 col = ((col / m_tabSize) + 1) * m_tabSize;
826 QStringRef CodeFormatter::currentTokenText() const
828 return m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length);
831 void CodeFormatter::turnInto(int newState)
837 void CodeFormatter::saveCurrentState(const QTextBlock &block)
839 if (!block.isValid())
843 blockData.m_blockRevision = block.revision();
844 blockData.m_beginState = m_beginState;
845 blockData.m_endState = m_currentState;
846 blockData.m_indentDepth = m_indentDepth;
848 QTextBlock saveableBlock(block);
849 saveBlockData(&saveableBlock, blockData);
852 void CodeFormatter::restoreCurrentState(const QTextBlock &block)
854 if (block.isValid()) {
856 if (loadBlockData(block, &blockData)) {
857 m_indentDepth = blockData.m_indentDepth;
858 m_currentState = blockData.m_endState;
859 m_beginState = m_currentState;
864 m_currentState = initialState();
865 m_beginState = m_currentState;
869 QStack<CodeFormatter::State> CodeFormatter::initialState()
871 static QStack<CodeFormatter::State> initialState;
872 if (initialState.isEmpty())
873 initialState.push(State(topmost_intro, 0));
877 int CodeFormatter::tokenizeBlock(const QTextBlock &block)
879 int startState = loadLexerState(block.previous());
880 if (block.blockNumber() == 0)
882 Q_ASSERT(startState != -1);
885 tokenize.setScanComments(true);
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);
893 const int lexerState = tokenize.state();
894 QTextBlock saveableBlock(block);
895 saveLexerState(&saveableBlock, lexerState);
899 CodeFormatter::TokenKind CodeFormatter::extendedTokenKind(const QmlJS::Token &token) const
901 const int kind = token.kind;
902 QStringRef text = m_currentLine.midRef(token.begin(), token.length);
904 if (kind == Identifier) {
907 if (text == "import")
909 if (text == "signal")
911 if (text == "property")
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()) {
927 else if (char3 == 's')
934 else if (char2 == 'u')
975 } else if (kind == Delimiter) {
978 else if (text == "++")
980 else if (text == "--")
984 return static_cast<TokenKind>(kind);
987 void CodeFormatter::dump() const
989 QMetaEnum metaEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("StateType"));
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;
996 qDebug() << "Current indent depth:" << m_indentDepth;
999 QtStyleCodeFormatter::QtStyleCodeFormatter()
1003 void QtStyleCodeFormatter::setIndentSize(int size)
1005 m_indentSize = size;
1008 void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth) const
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);
1017 case objectdefinition_open: {
1018 // special case for things like "gradient: Gradient {"
1019 if (parentState.type == binding_assignment)
1020 *savedIndentDepth = state(1).savedIndentDepth;
1023 *savedIndentDepth = tokenPosition;
1025 *indentDepth = *savedIndentDepth + m_indentSize;
1029 case binding_or_objectdefinition:
1031 *indentDepth = *savedIndentDepth = tokenPosition;
1034 case binding_assignment:
1035 case objectliteral_assignment:
1037 *indentDepth = *savedIndentDepth + 4;
1039 *indentDepth = column(tokenAt(tokenIndex() + 1).begin());
1042 case expression_or_objectdefinition:
1043 *indentDepth = tokenPosition;
1046 case expression_or_label:
1047 if (*indentDepth == tokenPosition)
1048 *indentDepth += 2*m_indentSize;
1050 *indentDepth = tokenPosition;
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;
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;
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;
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;
1091 *indentDepth = *savedIndentDepth + m_indentSize;
1095 case function_start:
1096 if (parentState.type == expression) {
1097 // undo the continuation indent of the expression
1098 *indentDepth = parentState.savedIndentDepth;
1099 *savedIndentDepth = *indentDepth;
1101 // always align to function keyword
1102 *indentDepth = tokenPosition;
1103 *savedIndentDepth = *indentDepth;
1107 case do_statement_while_paren_open:
1108 case statement_with_condition_paren_open:
1109 case signal_arglist_open:
1110 case function_arglist_open:
1112 case condition_paren_open:
1114 *indentDepth = tokenPosition + 1;
1116 *indentDepth += m_indentSize;
1121 *indentDepth = tokenPosition + tk.length + 1;
1123 *indentDepth += m_indentSize;
1127 // closing brace should be aligned to case
1128 if (parentState.type == case_cont) {
1129 *savedIndentDepth = parentState.savedIndentDepth;
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;
1141 *indentDepth += m_indentSize;
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;
1151 *indentDepth = parentState.savedIndentDepth;
1152 *savedIndentDepth = *indentDepth;
1154 *indentDepth += m_indentSize;
1158 case statement_with_condition:
1159 case statement_with_block:
1162 case switch_statement:
1163 if (firstToken || parentState.type == binding_assignment)
1164 *savedIndentDepth = tokenPosition;
1166 *indentDepth = *savedIndentDepth; // + 2*m_indentSize;
1167 // special case for 'else if'
1169 && newState == if_statement
1170 && parentState.type == substatement
1171 && state(1).type == else_clause) {
1172 *indentDepth = state(1).savedIndentDepth;
1173 *savedIndentDepth = *indentDepth;
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;
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;
1193 *indentDepth = tokenPosition + 1;
1197 *savedIndentDepth = tokenPosition;
1201 *indentDepth += m_indentSize;
1204 case multiline_comment_start:
1205 *indentDepth = tokenPosition + 2;
1208 case multiline_comment_cont:
1209 *indentDepth = tokenPosition;
1214 void QtStyleCodeFormatter::adjustIndent(const QList<Token> &tokens, int lexerState, int *indentDepth) const
1216 Q_UNUSED(lexerState)
1218 State topState = state();
1219 State previousState = state(1);
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());
1230 const int kind = extendedTokenKind(tokenAt(0));
1233 if (topState.type == substatement
1234 || topState.type == binding_assignment
1235 || topState.type == case_cont) {
1236 *indentDepth = topState.savedIndentDepth;
1240 if (topState.type == jsblock_open && previousState.type == case_cont) {
1241 *indentDepth = previousState.savedIndentDepth;
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;
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;
1267 case LeftParenthesis:
1269 if (topState.type == expression_maybe_continuation)
1270 *indentDepth = topState.savedIndentDepth;
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)
1282 if (type == if_statement) {
1286 *indentDepth = state(i).savedIndentDepth;
1295 if (topState.type == ternary_op) {
1301 if (topState.type == expression_maybe_continuation)
1302 *indentDepth = topState.savedIndentDepth;
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;
1312 } else if (type == topmost_intro) {