diff options
Diffstat (limited to 'tools/qmlformat')
-rw-r--r-- | tools/qmlformat/.prev_CMakeLists.txt | 19 | ||||
-rw-r--r-- | tools/qmlformat/CMakeLists.txt | 20 | ||||
-rw-r--r-- | tools/qmlformat/commentastvisitor.cpp | 6 | ||||
-rw-r--r-- | tools/qmlformat/commentastvisitor.h | 2 | ||||
-rw-r--r-- | tools/qmlformat/dumpastvisitor.cpp | 413 | ||||
-rw-r--r-- | tools/qmlformat/dumpastvisitor.h | 37 | ||||
-rw-r--r-- | tools/qmlformat/main.cpp | 28 | ||||
-rw-r--r-- | tools/qmlformat/restructureastvisitor.cpp | 55 |
8 files changed, 475 insertions, 105 deletions
diff --git a/tools/qmlformat/.prev_CMakeLists.txt b/tools/qmlformat/.prev_CMakeLists.txt new file mode 100644 index 0000000000..4f1f038f9b --- /dev/null +++ b/tools/qmlformat/.prev_CMakeLists.txt @@ -0,0 +1,19 @@ +# Generated from qmlformat.pro. + +##################################################################### +## qmlformat Tool: +##################################################################### + +qt_add_tool(qmlformat + SOURCES + commentastvisitor.cpp commentastvisitor.h + dumpastvisitor.cpp dumpastvisitor.h + main.cpp + restructureastvisitor.cpp restructureastvisitor.h + PUBLIC_LIBRARIES + Qt::QmlDevToolsPrivate +) + +#### Keys ignored in scope 1:.:.:qmlformat.pro:<TRUE>: +# QMAKE_TARGET_DESCRIPTION = "QML" "Formatter" +# _OPTION = "host_build" diff --git a/tools/qmlformat/CMakeLists.txt b/tools/qmlformat/CMakeLists.txt new file mode 100644 index 0000000000..b83b8745d2 --- /dev/null +++ b/tools/qmlformat/CMakeLists.txt @@ -0,0 +1,20 @@ +# Generated from qmlformat.pro. + +##################################################################### +## qmlformat Tool: +##################################################################### + +qt_add_tool(qmlformat + TOOLS_TARGET Qml # special case + SOURCES + commentastvisitor.cpp commentastvisitor.h + dumpastvisitor.cpp dumpastvisitor.h + main.cpp + restructureastvisitor.cpp restructureastvisitor.h + PUBLIC_LIBRARIES + Qt::QmlDevToolsPrivate +) + +#### Keys ignored in scope 1:.:.:qmlformat.pro:<TRUE>: +# QMAKE_TARGET_DESCRIPTION = "QML" "Formatter" +# _OPTION = "host_build" diff --git a/tools/qmlformat/commentastvisitor.cpp b/tools/qmlformat/commentastvisitor.cpp index 8264a6099e..4dd241ff93 100644 --- a/tools/qmlformat/commentastvisitor.cpp +++ b/tools/qmlformat/commentastvisitor.cpp @@ -268,3 +268,9 @@ bool CommentAstVisitor::visit(UiImport *node) attachComment(node); return true; } + +bool CommentAstVisitor::visit(UiPragma *node) +{ + attachComment(node); + return true; +} diff --git a/tools/qmlformat/commentastvisitor.h b/tools/qmlformat/commentastvisitor.h index 369784a5ba..d3de0a9b9d 100644 --- a/tools/qmlformat/commentastvisitor.h +++ b/tools/qmlformat/commentastvisitor.h @@ -38,6 +38,7 @@ #include <QVector> using namespace QQmlJS::AST; +using namespace QQmlJS; struct Comment { @@ -108,6 +109,7 @@ public: void endVisit(StatementList *node) override; bool visit(UiImport *node) override; + bool visit(UiPragma *node) override; bool visit(UiPublicMember *node) override; bool visit(FunctionDeclaration *node) override; private: diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp index 20ebef8927..75712975cc 100644 --- a/tools/qmlformat/dumpastvisitor.cpp +++ b/tools/qmlformat/dumpastvisitor.cpp @@ -35,10 +35,29 @@ DumpAstVisitor::DumpAstVisitor(Node *rootNode, CommentAstVisitor *comment): m_co // Add all completely orphaned comments m_result += getOrphanedComments(nullptr); + m_scope_properties.push(ScopeProperties {}); + rootNode->accept(this); // We need to get rid of one new-line so our output doesn't append an empty line m_result.chop(1); + + // Remove trailing whitespace + QStringList lines = m_result.split("\n"); + for (QString& line : lines) { + while (line.endsWith(" ")) + line.chop(1); + } + + m_result = lines.join("\n"); +} + +bool DumpAstVisitor::preVisit(Node *el) +{ + UiObjectMember *m = el->uiObjectMemberCast(); + if (m != 0) + Node::accept(m->annotations, this); + return true; } static QString parseUiQualifiedId(UiQualifiedId *id) @@ -185,7 +204,7 @@ QString DumpAstVisitor::parseUiParameterList(UiParameterList *list) { QString result = ""; for (auto *item = list; item != nullptr; item = item->next) - result += item->type->name + " " + item->name + (item->next != nullptr ? ", " : ""); + result += parseUiQualifiedId(item->type) + " " + item->name + (item->next != nullptr ? ", " : ""); return result; } @@ -218,20 +237,28 @@ QString DumpAstVisitor::parsePatternElement(PatternElement *element, bool scope) result += element->bindingIdentifier.toString(); + if (element->typeAnnotation != nullptr) + result += ": " + parseType(element->typeAnnotation->type); + if (!expr.isEmpty()) result += " = "+expr; return result; } default: + m_error = true; return "pe_unknown"; } } QString escapeString(QString string) { - // Escape \r, \n and \t - string = string.replace("\r", "\\r").replace("\n", "\\n").replace("\t", "\\t"); + // Handle escape sequences + string = string.replace("\r", "\\r").replace("\n", "\\n").replace("\t", "\\t") + .replace("\b","\\b").replace("\v", "\\v").replace("\f", "\\f"); + + // Escape backslash + string = string.replace("\\", "\\\\"); // Escape " string = string.replace("\"", "\\\""); @@ -261,7 +288,14 @@ QString DumpAstVisitor::parseFormalParameterList(FormalParameterList *list) QString DumpAstVisitor::parsePatternProperty(PatternProperty *property) { - return escapeString(property->name->asString())+": "+parsePatternElement(property, false); + switch (property->type) { + case PatternElement::Getter: + return "get "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true); + case PatternElement::Setter: + return "set "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true); + default: + return escapeString(property->name->asString())+": "+parsePatternElement(property, false); + } } QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list) @@ -275,6 +309,61 @@ QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list) return result; } +QString DumpAstVisitor::parseFunctionExpression(FunctionExpression *functExpr, bool omitFunction) +{ + m_indentLevel++; + QString result; + + if (!functExpr->isArrowFunction) { + result += omitFunction ? "" : "function"; + + if (functExpr->isGenerator) + result += "*"; + + if (!functExpr->name.isEmpty()) + result += (omitFunction ? "" : " ") + functExpr->name; + + result += "("+parseFormalParameterList(functExpr->formals)+")"; + + if (functExpr->typeAnnotation != nullptr) + result += " : " + parseType(functExpr->typeAnnotation->type); + + result += " {\n" + parseStatementList(functExpr->body); + } else { + result += "("+parseFormalParameterList(functExpr->formals)+")"; + + if (functExpr->typeAnnotation != nullptr) + result += " : " + parseType(functExpr->typeAnnotation->type); + + result += " => {\n" + parseStatementList(functExpr->body); + } + + m_indentLevel--; + + result += formatLine("}", false); + + return result; + +} + +QString DumpAstVisitor::parseType(Type *type) { + QString result = parseUiQualifiedId(type->typeId); + + if (type->typeArguments != nullptr) { + TypeArgumentList *list = cast<TypeArgumentList *>(type->typeArguments); + + result += "<"; + + for (auto *item = list; item != nullptr; item = item->next) { + result += parseType(item->typeId) + (item->next != nullptr ? ", " : ""); + } + + result += ">"; + } + + return result; +} + QString DumpAstVisitor::parseExpression(ExpressionNode *expression) { if (expression == nullptr) @@ -288,7 +377,15 @@ QString DumpAstVisitor::parseExpression(ExpressionNode *expression) return cast<IdentifierExpression*>(expression)->name.toString(); case Node::Kind_FieldMemberExpression: { auto *fieldMemberExpr = cast<FieldMemberExpression *>(expression); - return parseExpression(fieldMemberExpr->base) + "." + fieldMemberExpr->name.toString(); + QString result = parseExpression(fieldMemberExpr->base); + + // If we're operating on a numeric literal, always put it in braces + if (fieldMemberExpr->base->kind == Node::Kind_NumericLiteral) + result = "(" + result + ")"; + + result += "." + fieldMemberExpr->name.toString(); + + return result; } case Node::Kind_ArrayMemberExpression: { auto *arrayMemberExpr = cast<ArrayMemberExpression *>(expression); @@ -304,31 +401,7 @@ QString DumpAstVisitor::parseExpression(ExpressionNode *expression) case Node::Kind_FunctionExpression: { auto *functExpr = cast<FunctionExpression *>(expression); - - m_indentLevel++; - QString result; - - if (!functExpr->isArrowFunction) { - result += "function"; - - if (functExpr->isGenerator) - result += "*"; - - if (!functExpr->name.isEmpty()) - result += " " + functExpr->name; - - result += "("+parseFormalParameterList(functExpr->formals)+") {\n" - + parseStatementList(functExpr->body); - } else { - result += "("+parseFormalParameterList(functExpr->formals)+") => {\n"; - result += parseStatementList(functExpr->body); - } - - m_indentLevel--; - - result += formatLine("}", false); - - return result; + return parseFunctionExpression(functExpr); } case Node::Kind_NullExpression: return "null"; @@ -419,8 +492,7 @@ QString DumpAstVisitor::parseExpression(ExpressionNode *expression) } case Node::Kind_Type: { auto* type = reinterpret_cast<Type*>(expression); - - return parseUiQualifiedId(type->typeId); + return parseType(type); } case Node::Kind_RegExpLiteral: { auto* regexpLiteral = cast<RegExpLiteral*>(expression); @@ -610,10 +682,14 @@ QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, result += "; "; result += parseExpression(forStatement->condition) + "; "; - result += parseExpression(forStatement->expression)+") "; + result += parseExpression(forStatement->expression)+")"; - result += parseStatement(forStatement->statement); + const QString statement = parseStatement(forStatement->statement); + if (!statement.isEmpty()) + result += " "+statement; + else + result += ";"; return result; } @@ -639,9 +715,16 @@ QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, break; } - result += parseExpression(forEachStatement->expression) + ") "; + result += parseExpression(forEachStatement->expression) + ")"; + + const QString statement = parseStatement(forEachStatement->statement); + + if (!statement.isEmpty()) + result += " "+statement; + else + result += ";"; + - result += parseStatement(forEachStatement->statement); return result; } @@ -652,9 +735,14 @@ QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, auto statement = parseStatement(whileStatement->statement, false, true); - return "while ("+parseExpression(whileStatement->expression) + ")" - + (m_blockNeededBraces ? " " : "") - + statement; + QString result = "while ("+parseExpression(whileStatement->expression) + ")"; + + if (!statement.isEmpty()) + result += (m_blockNeededBraces ? " " : "") + statement; + else + result += ";"; + + return result; } case Node::Kind_DoWhileStatement: { auto *doWhileStatement = cast<DoWhileStatement *>(statement); @@ -762,31 +850,32 @@ bool DumpAstVisitor::visit(UiPublicMember *node) { switch (node->type) { case UiPublicMember::Signal: - if (m_firstSignal) { - if (m_firstOfAll) - m_firstOfAll = false; + if (scope().m_firstSignal) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; else addNewLine(); - m_firstSignal = false; + scope().m_firstSignal = false; } addLine("signal "+node->name.toString()+"("+parseUiParameterList(node->parameters) + ")" + commentBackInline); break; case UiPublicMember::Property: { - if (m_firstProperty) { - if (m_firstOfAll) - m_firstOfAll = false; + if (scope().m_firstProperty) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; else addNewLine(); - m_firstProperty = false; + scope().m_firstProperty = false; } const bool is_required = node->requiredToken.isValid(); const bool is_default = node->defaultToken.isValid(); const bool is_readonly = node->readonlyToken.isValid(); + const bool has_type_modifier = node->typeModifierToken.isValid(); QString prefix = ""; QString statement = parseStatement(node->statement); @@ -803,8 +892,20 @@ bool DumpAstVisitor::visit(UiPublicMember *node) { if (is_readonly) prefix += "readonly "; - addLine(prefix + "property " + node->memberType->name + " " - + node->name+statement + commentBackInline); + QString member_type = parseUiQualifiedId(node->memberType); + + if (has_type_modifier) + member_type = node->typeModifier + "<" + member_type + ">"; + + if (is_readonly && statement.isEmpty() + && scope().m_bindings.contains(node->name.toString())) { + m_result += formatLine(prefix + "property " + member_type + " ", false); + + scope().m_pendingBinding = true; + } else { + addLine(prefix + "property " + member_type + " " + + node->name+statement + commentBackInline); + } break; } } @@ -845,26 +946,70 @@ void DumpAstVisitor::addLine(QString line) { m_result += formatLine(line); } +QHash<QString, UiObjectMember*> findBindings(UiObjectMemberList *list) { + QHash<QString, UiObjectMember*> bindings; + + // This relies on RestructureASTVisitor having run beforehand + + for (auto *item = list; item != nullptr; item = item->next) { + switch (item->member->kind) { + case Node::Kind_UiPublicMember: { + UiPublicMember *member = cast<UiPublicMember *>(item->member); + + if (member->type != UiPublicMember::Property) + continue; + + bindings[member->name.toString()] = nullptr; + + break; + } + case Node::Kind_UiObjectBinding: { + UiObjectBinding *binding = cast<UiObjectBinding *>(item->member); + + const QString name = parseUiQualifiedId(binding->qualifiedId); + + if (bindings.contains(name)) + bindings[name] = binding; + + break; + } + case Node::Kind_UiArrayBinding: { + UiArrayBinding *binding = cast<UiArrayBinding *>(item->member); + + const QString name = parseUiQualifiedId(binding->qualifiedId); + + if (bindings.contains(name)) + bindings[name] = binding; + + break; + } + case Node::Kind_UiScriptBinding: + // We can ignore UiScriptBindings since those are actually properly attached to the property + break; + } + } + + return bindings; +} + bool DumpAstVisitor::visit(UiObjectDefinition *node) { - if (m_firstObject) { - if (m_firstOfAll) - m_firstOfAll = false; + if (scope().m_firstObject) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; else addNewLine(); - m_firstObject = false; + scope().m_firstObject = false; } addLine(getComment(node, Comment::Location::Front)); - addLine(node->qualifiedTypeNameId->name+" {"); + addLine(parseUiQualifiedId(node->qualifiedTypeNameId) + " {"); m_indentLevel++; - m_firstProperty = true; - m_firstSignal = true; - m_firstBinding = true; - m_firstObject = true; - m_firstOfAll = true; + ScopeProperties props; + props.m_bindings = findBindings(node->initializer->members); + m_scope_properties.push(props); m_result += getOrphanedComments(node); @@ -873,9 +1018,14 @@ bool DumpAstVisitor::visit(UiObjectDefinition *node) { void DumpAstVisitor::endVisit(UiObjectDefinition *node) { m_indentLevel--; - addLine(m_inArrayBinding && m_lastInArrayBinding != node ? "}," : "}"); + + m_scope_properties.pop(); + + bool need_comma = scope().m_inArrayBinding && scope().m_lastInArrayBinding != node; + + addLine(need_comma ? "}," : "}"); addLine(getComment(node, Comment::Location::Back)); - if (!m_inArrayBinding) + if (!scope().m_inArrayBinding) addNewLine(); } @@ -920,49 +1070,64 @@ bool DumpAstVisitor::visit(UiEnumMemberList *node) { } bool DumpAstVisitor::visit(UiScriptBinding *node) { - if (m_firstBinding) { - if (m_firstOfAll) - m_firstOfAll = false; + if (scope().m_firstBinding) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; else addNewLine(); if (parseUiQualifiedId(node->qualifiedId) != "id") - m_firstBinding = false; + scope().m_firstBinding = false; } addLine(getComment(node, Comment::Location::Front)); - addLine(parseUiQualifiedId(node->qualifiedId)+ ": " + parseStatement(node->statement) - + getComment(node, Comment::Location::Back_Inline)); + + QString statement = parseStatement(node->statement); + + QString result = parseUiQualifiedId(node->qualifiedId) + ":"; + + if (!statement.isEmpty()) + result += " "+statement; + else + result += ";"; + + result += getComment(node, Comment::Location::Back_Inline); + + addLine(result); + return true; } bool DumpAstVisitor::visit(UiArrayBinding *node) { - if (m_firstBinding) { - if (m_firstOfAll) - m_firstOfAll = false; + if (!scope().m_pendingBinding && scope().m_firstBinding) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; else addNewLine(); - m_firstBinding = false; + scope().m_firstBinding = false; } - addLine(getComment(node, Comment::Location::Front)); - addLine(parseUiQualifiedId(node->qualifiedId)+ ": ["); + if (scope().m_pendingBinding) { + m_result += parseUiQualifiedId(node->qualifiedId)+ ": [\n"; + scope().m_pendingBinding = false; + } else { + addLine(getComment(node, Comment::Location::Front)); + addLine(parseUiQualifiedId(node->qualifiedId)+ ": ["); + } m_indentLevel++; - m_inArrayBinding = true; - m_firstOfAll = true; - m_firstObject = true; - m_firstSignal = true; - m_firstBinding = true; - m_firstProperty = true; + ScopeProperties props; + props.m_inArrayBinding = true; for (auto *item = node->members; item != nullptr; item = item->next) { if (item->next == nullptr) - m_lastInArrayBinding = item->member; + props.m_lastInArrayBinding = item->member; } + m_scope_properties.push(props); + m_result += getOrphanedComments(node); return true; @@ -970,8 +1135,7 @@ bool DumpAstVisitor::visit(UiArrayBinding *node) { void DumpAstVisitor::endVisit(UiArrayBinding *) { m_indentLevel--; - m_inArrayBinding = false; - m_lastInArrayBinding = nullptr; + m_scope_properties.pop(); addLine("]"); } @@ -986,7 +1150,12 @@ bool DumpAstVisitor::visit(FunctionDeclaration *node) { if (node->isGenerator) head += "*"; - head += " "+node->name+"("+parseFormalParameterList(node->formals)+") {"; + head += " "+node->name+"("+parseFormalParameterList(node->formals)+")"; + + if (node->typeAnnotation != nullptr) + head += " : " + parseType(node->typeAnnotation->type); + + head += " {"; addLine(head); m_indentLevel++; @@ -1000,27 +1169,37 @@ bool DumpAstVisitor::visit(FunctionDeclaration *node) { } bool DumpAstVisitor::visit(UiObjectBinding *node) { - if (m_firstObject) { - if (m_firstOfAll) - m_firstOfAll = false; + if (!scope().m_pendingBinding && scope().m_firstObject) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; else addNewLine(); - m_firstObject = false; + scope().m_firstObject = false; } QString name = parseUiQualifiedId(node->qualifiedTypeNameId); QString result = name; + ScopeProperties props; + props.m_bindings = findBindings(node->initializer->members); + m_scope_properties.push(props); + if (node->hasOnToken) result += " on "+parseUiQualifiedId(node->qualifiedId); else result.prepend(parseUiQualifiedId(node->qualifiedId) + ": "); - addNewLine(); - addLine(getComment(node, Comment::Location::Front)); - addLine(result+" {"); + if (scope().m_pendingBinding) { + m_result += result + " {\n"; + + scope().m_pendingBinding = false; + } else { + addNewLine(); + addLine(getComment(node, Comment::Location::Front)); + addLine(result + " {"); + } m_indentLevel++; @@ -1029,6 +1208,8 @@ bool DumpAstVisitor::visit(UiObjectBinding *node) { void DumpAstVisitor::endVisit(UiObjectBinding *node) { m_indentLevel--; + m_scope_properties.pop(); + addLine("}"); addLine(getComment(node, Comment::Location::Back)); @@ -1036,6 +1217,8 @@ void DumpAstVisitor::endVisit(UiObjectBinding *node) { } bool DumpAstVisitor::visit(UiImport *node) { + scope().m_firstOfAll = false; + addLine(getComment(node, Comment::Location::Front)); QString result = "import "; @@ -1046,8 +1229,8 @@ bool DumpAstVisitor::visit(UiImport *node) { result += parseUiQualifiedId(node->importUri); if (node->version) { - result += " " + QString::number(node->version->majorVersion) + "." - + QString::number(node->version->minorVersion); + result += " " + QString::number(node->version->version.majorVersion()) + "." + + QString::number(node->version->version.minorVersion()); } if (node->asToken.isValid()) { @@ -1060,3 +1243,49 @@ bool DumpAstVisitor::visit(UiImport *node) { return true; } + +bool DumpAstVisitor::visit(UiPragma *node) { + scope().m_firstOfAll = false; + + addLine(getComment(node, Comment::Location::Front)); + QString result = "pragma "+ node->name; + result += getComment(node, Comment::Location::Back_Inline); + + addLine(result); + + return true; +} + +bool DumpAstVisitor::visit(UiAnnotation *node) +{ + if (scope().m_firstObject) { + if (scope().m_firstOfAll) + scope().m_firstOfAll = false; + else + addNewLine(); + + scope().m_firstObject = false; + } + + addLine(getComment(node, Comment::Location::Front)); + addLine(QLatin1String("@") + parseUiQualifiedId(node->qualifiedTypeNameId) + " {"); + + m_indentLevel++; + + ScopeProperties props; + props.m_bindings = findBindings(node->initializer->members); + m_scope_properties.push(props); + + m_result += getOrphanedComments(node); + + return true; +} + +void DumpAstVisitor::endVisit(UiAnnotation *node) { + m_indentLevel--; + + m_scope_properties.pop(); + + addLine("}"); + addLine(getComment(node, Comment::Location::Back)); +} diff --git a/tools/qmlformat/dumpastvisitor.h b/tools/qmlformat/dumpastvisitor.h index 2001f4366e..faf067d400 100644 --- a/tools/qmlformat/dumpastvisitor.h +++ b/tools/qmlformat/dumpastvisitor.h @@ -32,9 +32,13 @@ #include <QtQml/private/qqmljsastvisitor_p.h> #include <QtQml/private/qqmljsast_p.h> +#include <QHash> +#include <QStack> + #include "commentastvisitor.h" using namespace QQmlJS::AST; +using namespace QQmlJS; class DumpAstVisitor : protected Visitor { @@ -43,6 +47,8 @@ public: QString toString() const { return m_result; } + bool preVisit(Node *) override; + bool visit(UiScriptBinding *node) override; bool visit(UiArrayBinding *node) override; @@ -62,11 +68,28 @@ public: bool visit(UiEnumMemberList *node) override; bool visit(UiPublicMember *node) override; bool visit(UiImport *node) override; + bool visit(UiPragma *node) override; + + bool visit(UiAnnotation *node) override; + void endVisit(UiAnnotation *node) override; void throwRecursionDepthError() override {} bool error() const { return m_error; } private: + struct ScopeProperties { + bool m_firstOfAll = true; + bool m_firstSignal = true; + bool m_firstProperty = true; + bool m_firstBinding = true; + bool m_firstObject = true; + bool m_inArrayBinding = false; + bool m_pendingBinding = false; + + UiObjectMember* m_lastInArrayBinding = nullptr; + QHash<QString, UiObjectMember*> m_bindings; + }; + QString generateIndent() const; QString formatLine(QString line, bool newline = true) const; @@ -106,19 +129,19 @@ private: QString parseFormalParameterList(FormalParameterList *list); + QString parseType(Type *type); + + QString parseFunctionExpression(FunctionExpression *expression, bool omitFunction = false); + + ScopeProperties& scope() { return m_scope_properties.top(); } + int m_indentLevel = 0; bool m_error = false; bool m_blockNeededBraces = false; - bool m_inArrayBinding = false; - bool m_firstOfAll = false; - bool m_firstSignal = false; - bool m_firstProperty = false; - bool m_firstBinding = false; - bool m_firstObject = true; + QStack<ScopeProperties> m_scope_properties; - UiObjectMember* m_lastInArrayBinding = nullptr; QString m_result = ""; CommentAstVisitor *m_comment; }; diff --git a/tools/qmlformat/main.cpp b/tools/qmlformat/main.cpp index 036fbe9748..da58ffd5d0 100644 --- a/tools/qmlformat/main.cpp +++ b/tools/qmlformat/main.cpp @@ -67,7 +67,7 @@ bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImp const auto diagnosticMessages = parser.diagnosticMessages(); for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { qWarning().noquote() << QString::fromLatin1("%1:%2 : %3") - .arg(filename).arg(m.line).arg(m.message); + .arg(filename).arg(m.loc.startLine).arg(m.message); } qWarning().noquote() << "Failed to parse" << filename; @@ -101,11 +101,29 @@ bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImp DumpAstVisitor dump(parser.rootNode(), &comment); - if (dump.error()) { + QString dumpCode = dump.toString(); + + lexer.setCode(dumpCode, 1, true); + + bool dumpSuccess = parser.parse(); + + if (!dumpSuccess) { + if (verbose) { + const auto diagnosticMessages = parser.diagnosticMessages(); + for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { + qWarning().noquote() << QString::fromLatin1("<formatted>:%2 : %3") + .arg(m.loc.startLine).arg(m.message); + } + } + + qWarning().noquote() << "Failed to parse formatted code."; + } + + if (dump.error() || !dumpSuccess) { if (force) { qWarning().noquote() << "An error has occurred. The output may not be reliable."; } else { - qWarning().noquote() << "Am error has occurred. Aborting."; + qWarning().noquote() << "An error has occurred. Aborting."; return false; } } @@ -120,10 +138,10 @@ bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImp return false; } - file.write(dump.toString().toUtf8()); + file.write(dumpCode.toUtf8()); file.close(); } else { - QTextStream(stdout) << dump.toString(); + QTextStream(stdout) << dumpCode; } return true; diff --git a/tools/qmlformat/restructureastvisitor.cpp b/tools/qmlformat/restructureastvisitor.cpp index f9ac2a20c2..7cce0e8034 100644 --- a/tools/qmlformat/restructureastvisitor.cpp +++ b/tools/qmlformat/restructureastvisitor.cpp @@ -110,6 +110,11 @@ void RestructureAstVisitor::endVisit(UiObjectMemberList *node) { QList<UiObjectMember*> correctOrder; + QList<UiScriptBinding*> largeScriptBinding; + + UiObjectMember *states = nullptr; + UiObjectMember *transitions = nullptr; + auto enumDeclarations = findKind<UiEnumDeclaration>(node); auto scriptBindings = findKind<UiScriptBinding>(node); auto arrayBindings = findKind<UiArrayBinding>(node); @@ -117,11 +122,47 @@ void RestructureAstVisitor::endVisit(UiObjectMemberList *node) auto sourceElements = findKind<UiSourceElement>(node); auto objectDefinitions = findKind<UiObjectDefinition>(node); + // Look for transitions and states + for (auto *binding : findKind<UiObjectBinding>(node)) { + const QString name = parseUiQualifiedId(binding->qualifiedId); + + if (name == "transitions") + transitions = binding; + else if (name == "states") + states = binding; + } + + for (auto it = arrayBindings.begin(); it != arrayBindings.end();) { + const QString name = parseUiQualifiedId((*it)->qualifiedId); + + if (name == "transitions") { + transitions = *it; + it = arrayBindings.erase(it); + } else if (name == "states") { + states = *it; + it = arrayBindings.erase(it); + } else { + it++; + } + } + + // Find large script bindings + for (auto it = scriptBindings.begin(); it != scriptBindings.end();) { + // A binding is considered large if it uses a block + if ((*it)->statement->kind != Node::Kind_Block) { + it++; + continue; + } + + largeScriptBinding.push_back(*it); + it = scriptBindings.erase(it); + } + // This structure is based on https://doc.qt.io/qt-5/qml-codingconventions.html // 1st id for (auto *binding : scriptBindings) { - if (binding->qualifiedId->name == "id") { + if (parseUiQualifiedId(binding->qualifiedId) == "id") { correctOrder.append(binding); scriptBindings.removeOne(binding); @@ -154,9 +195,14 @@ void RestructureAstVisitor::endVisit(UiObjectMemberList *node) correctOrder.append(source); // 6th properties + // small script bindings... for (auto *binding : scriptBindings) correctOrder.append(binding); + // ...then large ones + for (auto *binding : largeScriptBinding) + correctOrder.append(binding); + for (auto *binding : arrayBindings) correctOrder.append(binding); @@ -170,6 +216,13 @@ void RestructureAstVisitor::endVisit(UiObjectMemberList *node) correctOrder.append(item->member); } + // 9th states and transitions + if (states != nullptr) + correctOrder.append(states); + + if (transitions != nullptr) + correctOrder.append(transitions); + // Rebuild member list from correctOrder for (auto *item = node; item != nullptr; item = item->next) { item->member = correctOrder.front(); |