From 16b4a6fe733b9973311ae19c6ecc90b1a6c53f36 Mon Sep 17 00:00:00 2001 From: Christian Kamm <christian.d.kamm@nokia.com> Date: Wed, 7 Sep 2011 13:30:48 +0200 Subject: [PATCH] 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> --- src/libs/qmljs/qmljscodeformatter.cpp | 34 ++++- src/libs/qmljs/qmljscodeformatter.h | 5 +- .../qmljstools/qmljsqtstylecodeformatter.cpp | 50 +++++-- .../qmlcodeformatter/tst_qmlcodeformatter.cpp | 125 ++++++++++++++++-- 4 files changed, 187 insertions(+), 27 deletions(-) diff --git a/src/libs/qmljs/qmljscodeformatter.cpp b/src/libs/qmljs/qmljscodeformatter.cpp index eb7e61642d2..b02075e4f89 100644 --- a/src/libs/qmljs/qmljscodeformatter.cpp +++ b/src/libs/qmljs/qmljscodeformatter.cpp @@ -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 || diff --git a/src/libs/qmljs/qmljscodeformatter.h b/src/libs/qmljs/qmljscodeformatter.h index bd2e9228d52..4a1f372a6d0 100644 --- a/src/libs/qmljs/qmljscodeformatter.h +++ b/src/libs/qmljs/qmljscodeformatter.h @@ -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' diff --git a/src/plugins/qmljstools/qmljsqtstylecodeformatter.cpp b/src/plugins/qmljstools/qmljsqtstylecodeformatter.cpp index a85a25685e3..4b809d89ee8 100644 --- a/src/plugins/qmljstools/qmljsqtstylecodeformatter.cpp +++ b/src/plugins/qmljstools/qmljsqtstylecodeformatter.cpp @@ -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 - // 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; @@ -204,11 +218,18 @@ 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 - *indentDepth = parentState.savedIndentDepth; + if (state(1).type == expression_or_label) + *indentDepth = state(1).savedIndentDepth; + else + *indentDepth = parentState.savedIndentDepth; *savedIndentDepth = *indentDepth; } *indentDepth += m_indentSize; @@ -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) { diff --git a/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp b/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp index b7ef68d88ef..3dcfbdf91b8 100644 --- a/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp +++ b/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp @@ -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 { @@ -462,8 +466,8 @@ void tst_QMLCodeFormatter::ifBinding2() << Line(" + 5") << Line(" x: if (a)") << Line(" b") - << Line(" + 5") - << Line(" + 5") + << Line(" + 5") + << Line(" + 5") << Line("}") ; checkIndent(data); @@ -513,7 +517,7 @@ void tst_QMLCodeFormatter::ifStatementWithoutBraces1() << Line(" foo;") << Line(" else") << Line(" a + b + ") - << Line(" c") + << Line(" c") << Line(" else") << Line(" foo;") << Line(" y: 2") @@ -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; @@ -689,7 +725,7 @@ void tst_QMLCodeFormatter::strayElse() QList<Line> data; data << Line("Rectangle {") << Line("onClicked: {", 4) - << Line(" while( true ) {}") + << Line(" while ( true ) {}") << Line(" else", -1) << Line(" else {", -1) << Line(" }", -1) @@ -776,14 +812,14 @@ void tst_QMLCodeFormatter::doWhile() { 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); @@ -847,7 +883,7 @@ void tst_QMLCodeFormatter::ternary() << Line(" ? b") << Line(" : c;") << Line(" var i = a ?") - << Line(" b : c;") + << Line(" b : c;") << Line(" var i = aooo ? b") << Line(" : c +") << Line(" 2;") @@ -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" -- GitLab