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
|
||||
} break;
|
||||
|
||||
case expression_or_label:
|
||||
switch (kind) {
|
||||
case Colon: turnInto(labelled_statement); break;
|
||||
default: enter(expression); continue;
|
||||
} break;
|
||||
|
||||
case expression:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
@@ -346,6 +352,12 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
|
||||
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) {
|
||||
@@ -426,7 +438,11 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
|
||||
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) {
|
||||
@@ -466,11 +482,22 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
|
||||
|
||||
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) {
|
||||
@@ -680,7 +707,6 @@ bool CodeFormatter::tryStatement()
|
||||
case Break:
|
||||
case Continue:
|
||||
enter(breakcontinue_statement);
|
||||
leave(true);
|
||||
return true;
|
||||
case Throw:
|
||||
enter(throw_statement);
|
||||
@@ -717,7 +743,10 @@ bool CodeFormatter::tryStatement()
|
||||
enter(jsblock_open);
|
||||
return true;
|
||||
case Identifier:
|
||||
enter(expression_or_label);
|
||||
return true;
|
||||
case Delimiter:
|
||||
case Var:
|
||||
case PlusPlus:
|
||||
case MinusMinus:
|
||||
case Import:
|
||||
@@ -755,7 +784,6 @@ bool CodeFormatter::isExpressionEndState(int type) const
|
||||
type == objectdefinition_open ||
|
||||
type == if_statement ||
|
||||
type == else_clause ||
|
||||
type == do_statement ||
|
||||
type == jsblock_open ||
|
||||
type == substatement_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_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
|
||||
@@ -148,7 +149,7 @@ public: // must be public to make Q_GADGET introspection work
|
||||
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
|
||||
@@ -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_open, // The brace that opens a substatement block.
|
||||
|
||||
labelled_statement, // after a label
|
||||
|
||||
return_statement, // After 'return'
|
||||
throw_statement, // After 'throw'
|
||||
|
||||
|
||||
@@ -128,15 +128,29 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
|
||||
*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
|
||||
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;
|
||||
@@ -204,10 +218,17 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
|
||||
*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
|
||||
if (state(1).type == expression_or_label)
|
||||
*indentDepth = state(1).savedIndentDepth;
|
||||
else
|
||||
*indentDepth = parentState.savedIndentDepth;
|
||||
*savedIndentDepth = *indentDepth;
|
||||
}
|
||||
@@ -224,6 +245,14 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
|
||||
*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: {
|
||||
@@ -270,11 +299,6 @@ void QtStyleCodeFormatter::adjustIndent(const QList<Token> &tokens, int lexerSta
|
||||
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) {
|
||||
|
||||
@@ -62,6 +62,7 @@ private Q_SLOTS:
|
||||
void ifBinding3();
|
||||
void ifStatementWithoutBraces1();
|
||||
void ifStatementWithoutBraces2();
|
||||
void ifStatementWithoutBraces3();
|
||||
void ifStatementWithBraces1();
|
||||
void ifStatementWithBraces2();
|
||||
void ifStatementWithBraces3();
|
||||
@@ -90,6 +91,9 @@ private Q_SLOTS:
|
||||
void propertyWithStatement();
|
||||
void keywordStatement();
|
||||
void namespacedObjects();
|
||||
void labelledStatements1();
|
||||
void labelledStatements2();
|
||||
void labelledStatements3();
|
||||
};
|
||||
|
||||
struct Line {
|
||||
@@ -552,6 +556,38 @@ void tst_QMLCodeFormatter::ifStatementWithoutBraces2()
|
||||
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;
|
||||
@@ -1093,6 +1129,75 @@ void tst_QMLCodeFormatter::namespacedObjects()
|
||||
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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user