forked from qt-creator/qt-creator
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:
@@ -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 ||
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user