Also do some cleanup to make handling of substatements nicer.
Change-Id: I78773fc81d9b0058fa97c5cef393cca34b7fd885
Reviewed-on: http://codereview.qt-project.org/4413
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@nokia.com>
default: enter(expression); continue; // really? identifier and more tokens might already be gone
} break;
+ case expression_or_label:
+ switch (kind) {
+ case Colon: turnInto(labelled_statement); break;
+ default: enter(expression); continue;
+ } break;
+
case expression:
if (tryInsideExpression())
break;
case RightBrace: leave(true); break;
} break;
+ case labelled_statement:
+ if (tryStatement())
+ break;
+ leave(true); // error recovery
+ break;
+
case substatement:
// prefer substatement_open over block_open
if (kind != LeftBrace) {
case RightParenthesis: leave(); leave(true); break;
} break;
- break;
+ case breakcontinue_statement:
+ switch (kind) {
+ case Identifier: leave(true); break;
+ default: leave(true); continue; // try again
+ } break;
case case_start:
switch (kind) {
int topState = m_currentState.top().type;
+ // if there's no colon on the same line, it's not a label
+ if (topState == expression_or_label)
+ enter(expression);
+ // if not followed by an identifier on the same line, it's done
+ else if (topState == breakcontinue_statement)
+ leave(true);
+
+ topState = m_currentState.top().type;
+
+ // some states might be continued on the next line
if (topState == expression
|| topState == expression_or_objectdefinition
|| topState == objectliteral_assignment) {
enter(expression_maybe_continuation);
}
+ // multi-line comment start?
if (topState != multiline_comment_start
&& topState != multiline_comment_cont
&& (lexerState & Scanner::MultiLineMask) == Scanner::MultiLineComment) {
case Break:
case Continue:
enter(breakcontinue_statement);
- leave(true);
return true;
case Throw:
enter(throw_statement);
enter(jsblock_open);
return true;
case Identifier:
+ enter(expression_or_label);
+ return true;
case Delimiter:
+ case Var:
case PlusPlus:
case MinusMinus:
case Import:
type == objectdefinition_open ||
type == if_statement ||
type == else_clause ||
- type == do_statement ||
type == jsblock_open ||
type == substatement_open ||
type == bracket_open ||
expression_continuation, // at the end of the line, when the next line definitely is a continuation
expression_maybe_continuation, // at the end of the line, when the next line may be an expression
expression_or_objectdefinition, // after a binding starting with an identifier ("x: foo")
+ expression_or_label, // when expecting a statement and getting an identifier
paren_open, // opening ( in expression
bracket_open, // opening [ in expression
jsblock_open,
empty_statement, // for a ';', will be popped directly
- breakcontinue_statement, // for continue/break, will be popped directly
+ breakcontinue_statement, // for continue/break, may be followed by identifier
if_statement, // After 'if'
maybe_else, // after the first substatement in an if
substatement, // The first line after a conditional or loop construct.
substatement_open, // The brace that opens a substatement block.
+ labelled_statement, // after a label
+
return_statement, // After 'return'
throw_statement, // After 'throw'
*indentDepth = tokenPosition;
break;
+ case expression_or_label:
+ if (*indentDepth == tokenPosition)
+ *indentDepth += 2*m_indentSize;
+ else
+ *indentDepth = tokenPosition;
+ break;
+
case expression:
- // expression_or_objectdefinition has already consumed the first token
- // ternary already adjusts indents nicely
- if (parentState.type != expression_or_objectdefinition
- && parentState.type != binding_assignment
- && parentState.type != ternary_op) {
- *indentDepth += 2 * m_indentSize;
+ if (*indentDepth == tokenPosition) {
+ // expression_or_objectdefinition doesn't want the indent
+ // expression_or_label already has it
+ // ternary already adjusts indents nicely
+ if (parentState.type != expression_or_objectdefinition
+ && parentState.type != expression_or_label
+ && parentState.type != binding_assignment
+ && parentState.type != ternary_op) {
+ *indentDepth += 2*m_indentSize;
+ }
}
- if (!firstToken && parentState.type != expression_or_objectdefinition) {
+ // expression_or_objectdefinition and expression_or_label have already consumed the first token
+ else if (parentState.type != expression_or_objectdefinition
+ && parentState.type != expression_or_label
+ && parentState.type != ternary_op) {
*indentDepth = tokenPosition;
}
break;
*indentDepth = *savedIndentDepth + m_indentSize;
break;
+ case substatement:
+ *indentDepth += m_indentSize;
+ break;
+
case objectliteral_open:
if (parentState.type == expression
|| parentState.type == objectliteral_assignment) {
// undo the continuation indent of the expression
- *indentDepth = parentState.savedIndentDepth;
+ if (state(1).type == expression_or_label)
+ *indentDepth = state(1).savedIndentDepth;
+ else
+ *indentDepth = parentState.savedIndentDepth;
*savedIndentDepth = *indentDepth;
}
*indentDepth += m_indentSize;
*savedIndentDepth = tokenPosition;
// ### continuation
*indentDepth = *savedIndentDepth; // + 2*m_indentSize;
+ // special case for 'else if'
+ if (!firstToken
+ && newState == if_statement
+ && parentState.type == substatement
+ && state(1).type == else_clause) {
+ *indentDepth = state(1).savedIndentDepth;
+ *savedIndentDepth = *indentDepth;
+ }
break;
case maybe_else: {
State topState = state();
State previousState = state(1);
- // adjusting the indentDepth here instead of in enter() gives 'else if' the correct indentation
- // ### could be moved?
- if (topState.type == substatement)
- *indentDepth += m_indentSize;
-
// keep user-adjusted indent in multiline comments
if (topState.type == multiline_comment_start
|| topState.type == multiline_comment_cont) {
void ifBinding3();
void ifStatementWithoutBraces1();
void ifStatementWithoutBraces2();
+ void ifStatementWithoutBraces3();
void ifStatementWithBraces1();
void ifStatementWithBraces2();
void ifStatementWithBraces3();
void propertyWithStatement();
void keywordStatement();
void namespacedObjects();
+ void labelledStatements1();
+ void labelledStatements2();
+ void labelledStatements3();
};
struct Line {
<< Line(" + 5")
<< Line(" x: if (a)")
<< Line(" b")
- << Line(" + 5")
- << Line(" + 5")
+ << Line(" + 5")
+ << Line(" + 5")
<< Line("}")
;
checkIndent(data);
<< Line(" foo;")
<< Line(" else")
<< Line(" a + b + ")
- << Line(" c")
+ << Line(" c")
<< Line(" else")
<< Line(" foo;")
<< Line(" y: 2")
checkIndent(data);
}
+void tst_QMLCodeFormatter::ifStatementWithoutBraces3()
+{
+ QList<Line> data;
+ data << Line("Rectangle {")
+ << Line(" x: {")
+ << Line(" if (a)")
+ << Line(" while (b)")
+ << Line(" foo;")
+ << Line(" while (a) if (a) b();")
+ << Line(" if (a) while (a) b; else")
+ << Line(" while (c)")
+ << Line(" while (d) break")
+ << Line(" while (a)")
+ << Line(" if (b)")
+ << Line(" for (;;) {}")
+ << Line(" else if (c)")
+ << Line(" for (;;) e")
+ << Line(" else")
+ << Line(" if (d)")
+ << Line(" foo;")
+ << Line(" else")
+ << Line(" e")
+ << Line(" if (a) ; else")
+ << Line(" while (true)")
+ << Line(" f")
+ << Line(" }")
+ << Line(" foo: bar")
+ << Line("}")
+ ;
+ checkIndent(data);
+}
+
void tst_QMLCodeFormatter::ifStatementWithBraces1()
{
QList<Line> data;
QList<Line> data;
data << Line("Rectangle {")
<< Line("onClicked: {", 4)
- << Line(" while( true ) {}")
+ << Line(" while ( true ) {}")
<< Line(" else", -1)
<< Line(" else {", -1)
<< Line(" }", -1)
{
QList<Line> data;
data << Line("function foo() {")
- << Line(" do { if (c) foo; } while(a);")
+ << Line(" do { if (c) foo; } while (a);")
<< Line(" do {")
- << Line(" if(a);")
- << Line(" } while(a);")
+ << Line(" if (a);")
+ << Line(" } while (a);")
<< Line(" do")
<< Line(" foo;")
- << Line(" while(a);")
- << Line(" do foo; while(a);")
+ << Line(" while (a);")
+ << Line(" do foo; while (a);")
<< Line("};")
;
checkIndent(data);
<< Line(" ? b")
<< Line(" : c;")
<< Line(" var i = a ?")
- << Line(" b : c;")
+ << Line(" b : c;")
<< Line(" var i = aooo ? b")
<< Line(" : c +")
<< Line(" 2;")
checkIndent(data);
}
+void tst_QMLCodeFormatter::labelledStatements1()
+{
+ QList<Line> data;
+ data << Line("lab: while (1) {")
+ << Line(" while (1)")
+ << Line(" break lab")
+ << Line("}")
+ << Line("for (;;) {")
+ << Line(" lab: do {")
+ << Line(" while (1) {")
+ << Line(" break lab")
+ << Line(" }")
+ << Line(" }")
+ << Line("}")
+ << Line("var x = function() {")
+ << Line(" x + 1;")
+ << Line("}")
+ ;
+ checkIndent(data);
+}
+
+void tst_QMLCodeFormatter::labelledStatements2()
+{
+ QList<Line> data;
+ data << Line("function a() {")
+ << Line(" lab: while (1)")
+ << Line(" break lab")
+ << Line(" if (a)")
+ << Line(" lab: while (1)")
+ << Line(" break lab")
+ << Line(" var a;")
+ << Line(" if (a)")
+ << Line(" lab: while (1)")
+ << Line(" break lab")
+ << Line(" else")
+ << Line(" lab: switch (a) {")
+ << Line(" case 1:")
+ << Line(" }")
+ << Line("}")
+ << Line("var x")
+ ;
+ checkIndent(data);
+}
+
+void tst_QMLCodeFormatter::labelledStatements3()
+{
+ QList<Line> data;
+ data << Line("function a() {")
+ << Line(" lab: while (1)")
+ << Line(" break lab")
+ << Line(" if (a) {")
+ << Line(" lab: while (1)")
+ << Line(" break lab")
+ << Line(" }")
+ << Line(" var a;")
+ << Line(" if (a) {")
+ << Line(" lab: while (1)")
+ << Line(" break lab")
+ << Line(" } else {")
+ << Line(" lab: switch (a) {")
+ << Line(" case 1:")
+ << Line(" }")
+ << Line(" }")
+ << Line("}")
+ << Line("var x")
+ ;
+ checkIndent(data);
+}
+
QTEST_APPLESS_MAIN(tst_QMLCodeFormatter)
#include "tst_qmlcodeformatter.moc"