OSDN Git Service

QmlJS indenter: Fix labelled statements and break/continue with label.
authorChristian Kamm <christian.d.kamm@nokia.com>
Wed, 7 Sep 2011 11:30:48 +0000 (13:30 +0200)
committerChristian Kamm <christian.d.kamm@nokia.com>
Wed, 14 Sep 2011 09:02:45 +0000 (11:02 +0200)
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
src/libs/qmljs/qmljscodeformatter.h
src/plugins/qmljstools/qmljsqtstylecodeformatter.cpp
tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp

index eb7e616..b02075e 100644 (file)
@@ -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 ||
index bd2e922..4a1f372 100644 (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_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'
 
index a85a256..4b809d8 100644 (file)
@@ -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) {
index b7ef68d..3dcfbdf 100644 (file)
@@ -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"