QmlJS indenter: Fix labelled statements and break/continue with label.

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>
This commit is contained in:
Christian Kamm
2011-09-07 13:30:48 +02:00
parent bbc48690e8
commit 16b4a6fe73
4 changed files with 187 additions and 27 deletions

View File

@@ -252,6 +252,12 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
default: enter(expression); continue; // really? identifier and more tokens might already be gone default: enter(expression); continue; // really? identifier and more tokens might already be gone
} break; } break;
case expression_or_label:
switch (kind) {
case Colon: turnInto(labelled_statement); break;
default: enter(expression); continue;
} break;
case expression: case expression:
if (tryInsideExpression()) if (tryInsideExpression())
break; break;
@@ -346,6 +352,12 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case RightBrace: leave(true); break; case RightBrace: leave(true); break;
} break; } break;
case labelled_statement:
if (tryStatement())
break;
leave(true); // error recovery
break;
case substatement: case substatement:
// prefer substatement_open over block_open // prefer substatement_open over block_open
if (kind != LeftBrace) { if (kind != LeftBrace) {
@@ -426,7 +438,11 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case RightParenthesis: leave(); leave(true); break; case RightParenthesis: leave(); leave(true); break;
} break; } break;
break; case breakcontinue_statement:
switch (kind) {
case Identifier: leave(true); break;
default: leave(true); continue; // try again
} break;
case case_start: case case_start:
switch (kind) { switch (kind) {
@@ -466,11 +482,22 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
int topState = m_currentState.top().type; 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 if (topState == expression
|| topState == expression_or_objectdefinition || topState == expression_or_objectdefinition
|| topState == objectliteral_assignment) { || topState == objectliteral_assignment) {
enter(expression_maybe_continuation); enter(expression_maybe_continuation);
} }
// multi-line comment start?
if (topState != multiline_comment_start if (topState != multiline_comment_start
&& topState != multiline_comment_cont && topState != multiline_comment_cont
&& (lexerState & Scanner::MultiLineMask) == Scanner::MultiLineComment) { && (lexerState & Scanner::MultiLineMask) == Scanner::MultiLineComment) {
@@ -680,7 +707,6 @@ bool CodeFormatter::tryStatement()
case Break: case Break:
case Continue: case Continue:
enter(breakcontinue_statement); enter(breakcontinue_statement);
leave(true);
return true; return true;
case Throw: case Throw:
enter(throw_statement); enter(throw_statement);
@@ -717,7 +743,10 @@ bool CodeFormatter::tryStatement()
enter(jsblock_open); enter(jsblock_open);
return true; return true;
case Identifier: case Identifier:
enter(expression_or_label);
return true;
case Delimiter: case Delimiter:
case Var:
case PlusPlus: case PlusPlus:
case MinusMinus: case MinusMinus:
case Import: case Import:
@@ -755,7 +784,6 @@ bool CodeFormatter::isExpressionEndState(int type) const
type == objectdefinition_open || type == objectdefinition_open ||
type == if_statement || type == if_statement ||
type == else_clause || type == else_clause ||
type == do_statement ||
type == jsblock_open || type == jsblock_open ||
type == substatement_open || type == substatement_open ||
type == bracket_open || type == bracket_open ||

View File

@@ -133,6 +133,7 @@ public: // must be public to make Q_GADGET introspection work
expression_continuation, // at the end of the line, when the next line definitely is a continuation 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_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_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 paren_open, // opening ( in expression
bracket_open, // opening [ in expression bracket_open, // opening [ in expression
@@ -148,7 +149,7 @@ public: // must be public to make Q_GADGET introspection work
jsblock_open, jsblock_open,
empty_statement, // for a ';', will be popped directly 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' if_statement, // After 'if'
maybe_else, // after the first substatement in an if maybe_else, // after the first substatement in an if
@@ -160,6 +161,8 @@ public: // must be public to make Q_GADGET introspection work
substatement, // The first line after a conditional or loop construct. substatement, // The first line after a conditional or loop construct.
substatement_open, // The brace that opens a substatement block. substatement_open, // The brace that opens a substatement block.
labelled_statement, // after a label
return_statement, // After 'return' return_statement, // After 'return'
throw_statement, // After 'throw' throw_statement, // After 'throw'

View File

@@ -128,15 +128,29 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
*indentDepth = tokenPosition; *indentDepth = tokenPosition;
break; break;
case expression_or_label:
if (*indentDepth == tokenPosition)
*indentDepth += 2*m_indentSize;
else
*indentDepth = tokenPosition;
break;
case expression: case expression:
// expression_or_objectdefinition has already consumed the first token if (*indentDepth == tokenPosition) {
// ternary already adjusts indents nicely // expression_or_objectdefinition doesn't want the indent
if (parentState.type != expression_or_objectdefinition // expression_or_label already has it
&& parentState.type != binding_assignment // ternary already adjusts indents nicely
&& parentState.type != ternary_op) { if (parentState.type != expression_or_objectdefinition
*indentDepth += 2 * m_indentSize; && 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; *indentDepth = tokenPosition;
} }
break; break;
@@ -204,11 +218,18 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
*indentDepth = *savedIndentDepth + m_indentSize; *indentDepth = *savedIndentDepth + m_indentSize;
break; break;
case substatement:
*indentDepth += m_indentSize;
break;
case objectliteral_open: case objectliteral_open:
if (parentState.type == expression if (parentState.type == expression
|| parentState.type == objectliteral_assignment) { || parentState.type == objectliteral_assignment) {
// undo the continuation indent of the expression // 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; *savedIndentDepth = *indentDepth;
} }
*indentDepth += m_indentSize; *indentDepth += m_indentSize;
@@ -224,6 +245,14 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
*savedIndentDepth = tokenPosition; *savedIndentDepth = tokenPosition;
// ### continuation // ### continuation
*indentDepth = *savedIndentDepth; // + 2*m_indentSize; *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; break;
case maybe_else: { case maybe_else: {
@@ -270,11 +299,6 @@ void QtStyleCodeFormatter::adjustIndent(const QList<Token> &tokens, int lexerSta
State topState = state(); State topState = state();
State previousState = state(1); 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 // keep user-adjusted indent in multiline comments
if (topState.type == multiline_comment_start if (topState.type == multiline_comment_start
|| topState.type == multiline_comment_cont) { || topState.type == multiline_comment_cont) {

View File

@@ -62,6 +62,7 @@ private Q_SLOTS:
void ifBinding3(); void ifBinding3();
void ifStatementWithoutBraces1(); void ifStatementWithoutBraces1();
void ifStatementWithoutBraces2(); void ifStatementWithoutBraces2();
void ifStatementWithoutBraces3();
void ifStatementWithBraces1(); void ifStatementWithBraces1();
void ifStatementWithBraces2(); void ifStatementWithBraces2();
void ifStatementWithBraces3(); void ifStatementWithBraces3();
@@ -90,6 +91,9 @@ private Q_SLOTS:
void propertyWithStatement(); void propertyWithStatement();
void keywordStatement(); void keywordStatement();
void namespacedObjects(); void namespacedObjects();
void labelledStatements1();
void labelledStatements2();
void labelledStatements3();
}; };
struct Line { struct Line {
@@ -462,8 +466,8 @@ void tst_QMLCodeFormatter::ifBinding2()
<< Line(" + 5") << Line(" + 5")
<< Line(" x: if (a)") << Line(" x: if (a)")
<< Line(" b") << Line(" b")
<< Line(" + 5") << Line(" + 5")
<< Line(" + 5") << Line(" + 5")
<< Line("}") << Line("}")
; ;
checkIndent(data); checkIndent(data);
@@ -513,7 +517,7 @@ void tst_QMLCodeFormatter::ifStatementWithoutBraces1()
<< Line(" foo;") << Line(" foo;")
<< Line(" else") << Line(" else")
<< Line(" a + b + ") << Line(" a + b + ")
<< Line(" c") << Line(" c")
<< Line(" else") << Line(" else")
<< Line(" foo;") << Line(" foo;")
<< Line(" y: 2") << Line(" y: 2")
@@ -552,6 +556,38 @@ void tst_QMLCodeFormatter::ifStatementWithoutBraces2()
checkIndent(data); 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() void tst_QMLCodeFormatter::ifStatementWithBraces1()
{ {
QList<Line> data; QList<Line> data;
@@ -689,7 +725,7 @@ void tst_QMLCodeFormatter::strayElse()
QList<Line> data; QList<Line> data;
data << Line("Rectangle {") data << Line("Rectangle {")
<< Line("onClicked: {", 4) << Line("onClicked: {", 4)
<< Line(" while( true ) {}") << Line(" while ( true ) {}")
<< Line(" else", -1) << Line(" else", -1)
<< Line(" else {", -1) << Line(" else {", -1)
<< Line(" }", -1) << Line(" }", -1)
@@ -776,14 +812,14 @@ void tst_QMLCodeFormatter::doWhile()
{ {
QList<Line> data; QList<Line> data;
data << Line("function foo() {") data << Line("function foo() {")
<< Line(" do { if (c) foo; } while(a);") << Line(" do { if (c) foo; } while (a);")
<< Line(" do {") << Line(" do {")
<< Line(" if(a);") << Line(" if (a);")
<< Line(" } while(a);") << Line(" } while (a);")
<< Line(" do") << Line(" do")
<< Line(" foo;") << Line(" foo;")
<< Line(" while(a);") << Line(" while (a);")
<< Line(" do foo; while(a);") << Line(" do foo; while (a);")
<< Line("};") << Line("};")
; ;
checkIndent(data); checkIndent(data);
@@ -847,7 +883,7 @@ void tst_QMLCodeFormatter::ternary()
<< Line(" ? b") << Line(" ? b")
<< Line(" : c;") << Line(" : c;")
<< Line(" var i = a ?") << Line(" var i = a ?")
<< Line(" b : c;") << Line(" b : c;")
<< Line(" var i = aooo ? b") << Line(" var i = aooo ? b")
<< Line(" : c +") << Line(" : c +")
<< Line(" 2;") << Line(" 2;")
@@ -1093,6 +1129,75 @@ void tst_QMLCodeFormatter::namespacedObjects()
checkIndent(data); 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) QTEST_APPLESS_MAIN(tst_QMLCodeFormatter)
#include "tst_qmlcodeformatter.moc" #include "tst_qmlcodeformatter.moc"