From dc5d14c3963e25a3664ed116856e7f604e43b7af Mon Sep 17 00:00:00 2001 From: Fawzi Mohamed Date: Mon, 19 Apr 2021 16:43:21 +0200 Subject: qmlformat: use QmlDom Replace qmlformat with the formatter using qml dom Change-Id: Ie90814260f2d3b9e589ce04381d5ad1880c5b519 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- tools/qmlformat/CMakeLists.txt | 7 +- tools/qmlformat/commentastvisitor.cpp | 288 ------ tools/qmlformat/commentastvisitor.h | 141 --- tools/qmlformat/dumpastvisitor.cpp | 1434 ----------------------------- tools/qmlformat/dumpastvisitor.h | 158 ---- tools/qmlformat/main.cpp | 295 ------ tools/qmlformat/qmlformat.cpp | 279 ++++++ tools/qmlformat/restructureastvisitor.cpp | 196 ---- tools/qmlformat/restructureastvisitor.h | 45 - 9 files changed, 282 insertions(+), 2561 deletions(-) delete mode 100644 tools/qmlformat/commentastvisitor.cpp delete mode 100644 tools/qmlformat/commentastvisitor.h delete mode 100644 tools/qmlformat/dumpastvisitor.cpp delete mode 100644 tools/qmlformat/dumpastvisitor.h delete mode 100644 tools/qmlformat/main.cpp create mode 100644 tools/qmlformat/qmlformat.cpp delete mode 100644 tools/qmlformat/restructureastvisitor.cpp delete mode 100644 tools/qmlformat/restructureastvisitor.h (limited to 'tools') diff --git a/tools/qmlformat/CMakeLists.txt b/tools/qmlformat/CMakeLists.txt index e492a3ec56..eeabe1c368 100644 --- a/tools/qmlformat/CMakeLists.txt +++ b/tools/qmlformat/CMakeLists.txt @@ -9,12 +9,11 @@ qt_internal_add_tool(${target_name} TARGET_DESCRIPTION "QML Formatter" TOOLS_TARGET Qml # special case SOURCES - commentastvisitor.cpp commentastvisitor.h - dumpastvisitor.cpp dumpastvisitor.h - main.cpp - restructureastvisitor.cpp restructureastvisitor.h + qmlformat.cpp PUBLIC_LIBRARIES + Qt::Core Qt::QmlDevToolsPrivate + Qt::QmlDomPrivate ) #### Keys ignored in scope 1:.:.:qmlformat.pro:: diff --git a/tools/qmlformat/commentastvisitor.cpp b/tools/qmlformat/commentastvisitor.cpp deleted file mode 100644 index f07f770b33..0000000000 --- a/tools/qmlformat/commentastvisitor.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "commentastvisitor.h" - -using namespace QQmlJS; -using namespace QQmlJS::AST; - -CommentAstVisitor::CommentAstVisitor(QQmlJS::Engine *engine, Node *rootNode) : m_engine(engine) -{ - rootNode->accept(this); - - // Look for complete orphans that have not been attached to *any* node - QVector completeOrphans; - - for (const auto &comment : m_engine->comments()) { - if (isCommentAttached(comment)) - continue; - - bool found_orphan = false; - for (const auto &orphanList : orphanComments().values()) { - for (const auto &orphan : orphanList) { - if (orphan.contains(comment)) { - found_orphan = true; - break; - } - } - - if (found_orphan) - break; - } - - if (found_orphan) - continue; - - completeOrphans.append(Comment(m_engine, Comment::Location::Front, {comment})); - } - - m_orphanComments[nullptr] = completeOrphans; -} - -QList CommentAstVisitor::findCommentsInLine(quint32 line, bool includePrevious) const -{ - QList results; - if (line == 0) - return results; - - for (const auto &location : m_engine->comments()) { - Comment comment(m_engine, Comment::Location::Front, { location }); - if (line < location.startLine || line > comment.endLine()) - continue; - - if (isCommentAttached(location)) - continue; - - results.append(location); - - if (includePrevious) { - // See if we can find any more comments above this one - auto previous = findCommentsInLine(location.startLine - 1, true); - - // Iterate it in reverse to restore the correct order - for (auto it = previous.rbegin(); it != previous.rend(); it++) { - results.prepend(*it); - } - } - - break; - } - - return results; -} - -bool CommentAstVisitor::isCommentAttached(const SourceLocation &location) const -{ - for (const auto &value : m_attachedComments.values()) { - if (value.contains(location)) - return true; - } - - for (const auto &value : m_listItemComments.values()) { - if (value.contains(location)) - return true; - } - - // If a comment is already marked as an orphan of a Node that counts as attached too. - for (const auto &orphanList : m_orphanComments.values()) { - for (const auto &value : orphanList) { - if (value.contains(location)) - return true; - } - } - - return false; -} - -Comment CommentAstVisitor::findComment(SourceLocation first, SourceLocation last, - int locations) const -{ - if (locations & Comment::Location::Front) { - quint32 searchAt = first.startLine - 1; - - const auto comments = findCommentsInLine(searchAt, true); - if (!comments.isEmpty()) - return Comment(m_engine, Comment::Location::Front, comments); - } - - if (locations & Comment::Location::Front_Inline) { - quint32 searchAt = first.startLine; - - const auto comments = findCommentsInLine(searchAt); - if (!comments.isEmpty()) - return Comment(m_engine, Comment::Location::Front_Inline, comments); - } - - if (locations & Comment::Location::Back_Inline) { - quint32 searchAt = last.startLine; - - const auto comments = findCommentsInLine(searchAt); - if (!comments.isEmpty()) - return Comment(m_engine, Comment::Location::Back_Inline, comments); - } - - if (locations & Comment::Location::Back) { - quint32 searchAt = last.startLine + 1; - - const auto comments = findCommentsInLine(searchAt); - if (!comments.isEmpty()) - return Comment(m_engine, Comment::Location::Back, comments); - } - - return Comment(); - -} - -Comment CommentAstVisitor::findComment(Node *node, int locations) const -{ - return findComment(node->firstSourceLocation(), node->lastSourceLocation(), locations); -} - -QVector CommentAstVisitor::findOrphanComments(Node *node) const -{ - QVector comments; - - for (auto &comment : m_engine->comments()) { - if (isCommentAttached(comment)) - continue; - - if (comment.begin() <= node->firstSourceLocation().begin() - || comment.end() > node->lastSourceLocation().end()) { - continue; - } - - comments.append(Comment(m_engine, Comment::Location::Front, {comment})); - } - - return comments; -} - -void CommentAstVisitor::attachComment(Node *node, int locations) -{ - auto comment = findComment(node, locations); - - if (comment.isValid()) - m_attachedComments[node] = comment; -} - -bool CommentAstVisitor::visit(UiScriptBinding *node) -{ - attachComment(node); - return true; -} - -bool CommentAstVisitor::visit(StatementList *node) -{ - for (auto *item = node; item != nullptr; item = item->next) - attachComment(item->statement, Comment::Front | Comment::Back_Inline); - return true; -} - -void CommentAstVisitor::endVisit(StatementList *node) -{ - m_orphanComments[node] = findOrphanComments(node); -} - -bool CommentAstVisitor::visit(UiObjectBinding *node) -{ - attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back); - return true; -} - -bool CommentAstVisitor::visit(UiObjectDefinition *node) -{ - attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back); - return true; -} - -void CommentAstVisitor::endVisit(UiObjectDefinition *node) -{ - m_orphanComments[node] = findOrphanComments(node); -} - -bool CommentAstVisitor::visit(UiArrayBinding *node) -{ - attachComment(node); - return true; -} - -void CommentAstVisitor::endVisit(UiArrayBinding *node) -{ - m_orphanComments[node] = findOrphanComments(node); -} - -bool CommentAstVisitor::visit(UiEnumDeclaration *node) -{ - attachComment(node); - return true; -} - -void CommentAstVisitor::endVisit(UiEnumDeclaration *node) -{ - m_orphanComments[node] = findOrphanComments(node); -} - -bool CommentAstVisitor::visit(UiEnumMemberList *node) -{ - for (auto *item = node; item != nullptr; item = item->next) { - auto comment = findComment(item->memberToken, - item->valueToken.isValid() ? item->valueToken : item->memberToken, - Comment::Front | Comment::Back_Inline); - - if (comment.isValid()) - m_listItemComments[item->memberToken.begin()] = comment; - } - - m_orphanComments[node] = findOrphanComments(node); - - return true; -} - -bool CommentAstVisitor::visit(UiPublicMember *node) -{ - attachComment(node); - return true; -} - -bool CommentAstVisitor::visit(FunctionDeclaration *node) -{ - attachComment(node); - return true; -} - -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 deleted file mode 100644 index 73a6015db7..0000000000 --- a/tools/qmlformat/commentastvisitor.h +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef COMMENTASTVISITOR_H -#define COMMENTASTVISITOR_H - -#include -#include -#include - -#include -#include -#include - -struct Comment -{ - enum Location : int - { - Front = 1, - Front_Inline = Front << 1, - Back = Front_Inline << 1, - Back_Inline = Back << 1, - DefaultLocations = Front | Back_Inline, - AllLocations = Front | Back | Front_Inline | Back_Inline - } m_location = Front; - - Comment() = default; - Comment(const QQmlJS::Engine *engine, Location location, QList srcLocations) - : m_location(location), m_srcLocations(srcLocations) { - for (const auto& srcLoc : srcLocations) { - m_text += engine->code().mid(static_cast(srcLoc.begin()), - static_cast(srcLoc.end() - srcLoc.begin())) + "\n"; - } - - m_text.chop(1); - } - - QList m_srcLocations; - - bool hasSheBang() const { return !m_srcLocations.isEmpty() && m_srcLocations.first().begin() == 0; } - bool isValid() const { return !m_srcLocations.isEmpty(); } - bool isMultiline() const { return m_text.contains("\n"); } - bool isSyntheticMultiline() const { return m_srcLocations.size() > 1; } - - bool contains(const QQmlJS::SourceLocation& location) const { - for (const QQmlJS::SourceLocation& srcLoc : m_srcLocations) { - if (srcLoc.begin() == location.begin() && srcLoc.end() == location.end()) - return true; - } - - return false; - } - - quint32 endLine() const - { - if (isSyntheticMultiline() || !isValid()) - return 0; - - return m_srcLocations[0].startLine + m_text.count(QLatin1Char('\n')); - } - - QString m_text; -}; - -namespace AST = QQmlJS::AST; -class CommentAstVisitor : protected QQmlJS::AST::Visitor -{ -public: - CommentAstVisitor(QQmlJS::Engine *engine, AST::Node *rootNode); - - void throwRecursionDepthError() override {} - - const QHash attachedComments() const { return m_attachedComments; } - const QHash listComments() const { return m_listItemComments; } - const QHash> orphanComments() const { return m_orphanComments; } - - bool visit(AST::UiScriptBinding *node) override; - bool visit(AST::UiObjectBinding *node) override; - - bool visit(AST::UiArrayBinding *node) override; - void endVisit(AST::UiArrayBinding *node) override; - - bool visit(AST::UiObjectDefinition *node) override; - void endVisit(AST::UiObjectDefinition *) override; - - bool visit(AST::UiEnumDeclaration *node) override; - void endVisit(AST::UiEnumDeclaration *node) override; - - bool visit(AST::UiEnumMemberList *node) override; - - bool visit(AST::StatementList *node) override; - void endVisit(AST::StatementList *node) override; - - bool visit(AST::UiImport *node) override; - bool visit(AST::UiPragma *node) override; - bool visit(AST::UiPublicMember *node) override; - bool visit(AST::FunctionDeclaration *node) override; -private: - bool isCommentAttached(const QQmlJS::SourceLocation& location) const; - - QList findCommentsInLine(quint32 line, bool includePrevious = false) const; - - Comment findComment(QQmlJS::SourceLocation first, QQmlJS::SourceLocation last, - int locations = Comment::DefaultLocations) const; - - Comment findComment(AST::Node *node, int locations = Comment::DefaultLocations) const; - QVector findOrphanComments(AST::Node *node) const; - void attachComment(AST::Node *node, int locations = Comment::DefaultLocations); - - QQmlJS::Engine *m_engine; - QHash m_attachedComments; - QHash m_listItemComments; - QHash> m_orphanComments; -}; - -#endif // COMMENTASTVISITOR_H diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp deleted file mode 100644 index baf6651b20..0000000000 --- a/tools/qmlformat/dumpastvisitor.cpp +++ /dev/null @@ -1,1434 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "dumpastvisitor.h" -#include - -using namespace QQmlJS::AST; - -DumpAstVisitor::DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment, - int indentWidth, DumpAstVisitor::Indentation indentation) - : m_engine(engine), m_comment(comment), m_indentWidth(indentWidth), m_indentation(indentation) -{ - // 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) -{ - QString name = id->name.toString(); - for (auto *item = id->next; item != nullptr; item = item->next) { - name += "." + item->name; - } - - return name; -} - -static QString operatorToString(int op) -{ - switch (op) - { - case QSOperator::Add: return "+"; - case QSOperator::And: return "&&"; - case QSOperator::InplaceAnd: return "&="; - case QSOperator::Assign: return "="; - case QSOperator::BitAnd: return "&"; - case QSOperator::BitOr: return "|"; - case QSOperator::BitXor: return "^"; - case QSOperator::InplaceSub: return "-="; - case QSOperator::Div: return "/"; - case QSOperator::InplaceDiv: return "/="; - case QSOperator::Equal: return "=="; - case QSOperator::Exp: return "**"; - case QSOperator::InplaceExp: return "**="; - case QSOperator::Ge: return ">="; - case QSOperator::Gt: return ">"; - case QSOperator::In: return "in"; - case QSOperator::InplaceAdd: return "+="; - case QSOperator::InstanceOf: return "instanceof"; - case QSOperator::Le: return "<="; - case QSOperator::LShift: return "<<"; - case QSOperator::InplaceLeftShift: return "<<="; - case QSOperator::Lt: return "<"; - case QSOperator::Mod: return "%"; - case QSOperator::InplaceMod: return "%="; - case QSOperator::Mul: return "*"; - case QSOperator::InplaceMul: return "*="; - case QSOperator::NotEqual: return "!="; - case QSOperator::Or: return "||"; - case QSOperator::InplaceOr: return "|="; - case QSOperator::RShift: return ">>"; - case QSOperator::InplaceRightShift: return ">>="; - case QSOperator::StrictEqual: return "==="; - case QSOperator::StrictNotEqual: return "!=="; - case QSOperator::Sub: return "-"; - case QSOperator::URShift: return ">>>"; - case QSOperator::InplaceURightShift: return ">>>="; - case QSOperator::InplaceXor: return "^="; - case QSOperator::As: return "as"; - case QSOperator::Coalesce: return "??"; - case QSOperator::Invalid: - default: - return "INVALID"; - } -} - -QString DumpAstVisitor::formatComment(const Comment &comment) const -{ - QString result; - - bool useMultilineComment = comment.isMultiline() && !comment.isSyntheticMultiline(); - - if (useMultilineComment) - result += "/*"; - else if (!comment.hasSheBang()) - result += "//"; - - result += comment.m_text; - - if (comment.isSyntheticMultiline()) - result = result.replace("\n","\n" + formatLine("//", false)); - - if (comment.m_location == Comment::Location::Back_Inline) - result.prepend(" "); - - if (useMultilineComment) - result += "*/"; - - return result; -} - -QString DumpAstVisitor::getComment(Node *node, Comment::Location location) const -{ - const auto& comments = m_comment->attachedComments(); - if (!comments.contains(node)) - return ""; - - auto comment = comments[node]; - - if (comment.m_location != location) - return ""; - - return formatComment(comment); -} - -QString DumpAstVisitor::getListItemComment(QQmlJS::SourceLocation srcLocation, - Comment::Location location) const { - const auto& comments = m_comment->listComments(); - - if (!comments.contains(srcLocation.begin())) - return ""; - - auto comment = comments[srcLocation.begin()]; - - if (comment.m_location != location) - return ""; - - return formatComment(comment); -} - -QString DumpAstVisitor::getOrphanedComments(Node *node) const { - const auto& orphans = m_comment->orphanComments()[node]; - - if (orphans.size() == 0) - return ""; - - QString result = ""; - - for (const Comment& orphan : orphans) { - result += formatLine(formatComment(orphan)); - } - - result += "\n"; - - return result; -} - -QString DumpAstVisitor::parseArgumentList(ArgumentList *list) -{ - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) - result += parseExpression(item->expression) + (item->next != nullptr ? ", " : ""); - - return result; -} - -QString DumpAstVisitor::parseUiParameterList(UiParameterList *list) { - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) - result += parseUiQualifiedId(item->type) + " " + item->name + (item->next != nullptr ? ", " : ""); - - return result; -} - -QString DumpAstVisitor::parsePatternElement(PatternElement *element, bool scope) -{ - switch (element->type) - { - case PatternElement::Literal: - return parseExpression(element->initializer); - case PatternElement::Binding: { - QString result = ""; - QString expr = parseExpression(element->initializer); - - if (scope) { - switch (element->scope) { - case VariableScope::NoScope: - break; - case VariableScope::Let: - result = "let "; - break; - case VariableScope::Const: - result = "const "; - break; - case VariableScope::Var: - result = "var "; - break; - } - } - - if (element->bindingIdentifier.isEmpty()) - result += parseExpression(element->bindingTarget); - else - 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"; - } -} - -static QString escapeString(QString string) -{ - // 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("\"", "\\\""); - - return "\"" + string + "\""; -} - -QString DumpAstVisitor::parsePatternElementList(PatternElementList *list) -{ - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) - result += parsePatternElement(item->element) + (item->next != nullptr ? ", " : ""); - - return result; -} - -QString DumpAstVisitor::parseFormalParameterList(FormalParameterList *list) -{ - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) - result += parsePatternElement(item->element) + (item->next != nullptr ? ", " : ""); - - return result; -} - -QString DumpAstVisitor::parsePatternProperty(PatternProperty *property) -{ - switch (property->type) { - case PatternElement::Getter: - return "get "+parseFunctionExpression(cast(property->initializer), true); - case PatternElement::Setter: - return "set "+parseFunctionExpression(cast(property->initializer), true); - default: - if (property->name->kind == Node::Kind_ComputedPropertyName) { - return "["+parseExpression(cast(property->name)->expression)+"]: "+parsePatternElement(property, false); - } else { - return escapeString(property->name->asString())+": "+parsePatternElement(property, false); - } - } -} - -QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list) -{ - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) { - result += formatLine(parsePatternProperty(item->property) + (item->next != nullptr ? "," : "")); - } - - return result; -} - -QString DumpAstVisitor::parseFunctionExpression(FunctionExpression *functExpr, bool omitFunction) -{ - m_indentLevel++; - QString result; - bool hasBraces = true; - - 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 += " => "; - - if (functExpr->body == nullptr) { - result += "{}"; - } else if (functExpr->body->next == nullptr && functExpr->body->statement->kind == Node::Kind_ReturnStatement) { - m_indentLevel--; - result += parseExpression(cast(functExpr->body->statement)->expression); - hasBraces = false; - } else { - result += "{\n" + parseStatementList(functExpr->body); - } - } - - if (hasBraces) { - m_indentLevel--; - result += formatLine("}", false); - } - - return result; - -} - -QString DumpAstVisitor::parseType(Type *type) { - QString result = parseUiQualifiedId(type->typeId); - - if (type->typeArguments != nullptr) { - TypeArgumentList *list = cast(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) - return ""; - - switch (expression->kind) - { - case Node::Kind_ArrayPattern: - return "["+parsePatternElementList(cast(expression)->elements)+"]"; - case Node::Kind_IdentifierExpression: - return cast(expression)->name.toString(); - case Node::Kind_FieldMemberExpression: { - auto *fieldMemberExpr = cast(expression); - 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->isOptional ? "?." : ".") + fieldMemberExpr->name.toString(); - - return result; - } - case Node::Kind_ArrayMemberExpression: { - auto *arrayMemberExpr = cast(expression); - return parseExpression(arrayMemberExpr->base) - + (arrayMemberExpr->isOptional ? "?." : "") + "[" + parseExpression(arrayMemberExpr->expression) + "]"; - } - case Node::Kind_NestedExpression: - return "("+parseExpression(cast(expression)->expression)+")"; - case Node::Kind_TrueLiteral: - return "true"; - case Node::Kind_FalseLiteral: - return "false"; - case Node::Kind_FunctionExpression: - { - auto *functExpr = cast(expression); - return parseFunctionExpression(functExpr); - } - case Node::Kind_NullExpression: - return "null"; - case Node::Kind_ThisExpression: - return "this"; - case Node::Kind_PostIncrementExpression: - return parseExpression(cast(expression)->base)+"++"; - case Node::Kind_PreIncrementExpression: - return "++"+parseExpression(cast(expression)->expression); - case Node::Kind_PostDecrementExpression: - return parseExpression(cast(expression)->base)+"--"; - case Node::Kind_PreDecrementExpression: - return "--"+parseExpression(cast(expression)->expression); - case Node::Kind_NumericLiteral: - return QString::number(cast(expression)->value); - case Node::Kind_TemplateLiteral: { - auto firstSrcLoc = cast(expression)->firstSourceLocation(); - auto lastSrcLoc = cast(expression)->lastSourceLocation(); - return m_engine->code().mid(static_cast(firstSrcLoc.begin()), - static_cast(lastSrcLoc.end() - firstSrcLoc.begin())); - } - case Node::Kind_StringLiteral: { - auto srcLoc = cast(expression)->firstSourceLocation(); - return m_engine->code().mid(static_cast(srcLoc.begin()), - static_cast(srcLoc.end() - srcLoc.begin())); - } - case Node::Kind_BinaryExpression: { - auto *binExpr = expression->binaryExpressionCast(); - return parseExpression(binExpr->left) + " " + operatorToString(binExpr->op) - + " " + parseExpression(binExpr->right); - } - case Node::Kind_CallExpression: { - auto *callExpr = cast(expression); - - return parseExpression(callExpr->base) + (callExpr->isOptional ? "?." : "") + "(" + parseArgumentList(callExpr->arguments) + ")"; - } - case Node::Kind_NewExpression: - return "new "+parseExpression(cast(expression)->expression); - case Node::Kind_NewMemberExpression: { - auto *newMemberExpression = cast(expression); - return "new "+parseExpression(newMemberExpression->base) - + "(" +parseArgumentList(newMemberExpression->arguments)+")"; - } - case Node::Kind_DeleteExpression: - return "delete " + parseExpression(cast(expression)->expression); - case Node::Kind_VoidExpression: - return "void " + parseExpression(cast(expression)->expression); - case Node::Kind_TypeOfExpression: - return "typeof " + parseExpression(cast(expression)->expression); - case Node::Kind_UnaryPlusExpression: - return "+" + parseExpression(cast(expression)->expression); - case Node::Kind_UnaryMinusExpression: - return "-" + parseExpression(cast(expression)->expression); - case Node::Kind_NotExpression: - return "!" + parseExpression(cast(expression)->expression); - case Node::Kind_TildeExpression: - return "~" + parseExpression(cast(expression)->expression); - case Node::Kind_ConditionalExpression: { - auto *condExpr = cast(expression); - - QString result = ""; - - result += parseExpression(condExpr->expression) + " ? "; - result += parseExpression(condExpr->ok) + " : "; - result += parseExpression(condExpr->ko); - - return result; - } - case Node::Kind_YieldExpression: { - auto *yieldExpr = cast(expression); - - QString result = "yield"; - - if (yieldExpr->isYieldStar) - result += "*"; - - if (yieldExpr->expression) - result += " " + parseExpression(yieldExpr->expression); - - return result; - } - case Node::Kind_ObjectPattern: { - auto *objectPattern = cast(expression); - - if (objectPattern->properties == nullptr) - return "{}"; - - QString result = "{\n"; - - m_indentLevel++; - result += parsePatternPropertyList(objectPattern->properties); - m_indentLevel--; - - result += formatLine("}", false); - - return result; - } - case Node::Kind_Expression: { - auto* expr = cast(expression); - return parseExpression(expr->left)+", "+parseExpression(expr->right); - } - case Node::Kind_TypeExpression: { - auto* type = cast(expression); - return parseType(type->m_type); - } - case Node::Kind_RegExpLiteral: { - auto* regexpLiteral = cast(expression); - QString result = "/"+regexpLiteral->pattern+"/"; - - if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Unicode) - result += "u"; - if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Global) - result += "g"; - if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Multiline) - result += "m"; - if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Sticky) - result += "y"; - if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_IgnoreCase) - result += "i"; - - return result; - } - default: - m_error = true; - return "unknown_expression_"+QString::number(expression->kind); - } -} - -QString DumpAstVisitor::parseVariableDeclarationList(VariableDeclarationList *list) -{ - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) { - result += parsePatternElement(item->declaration, (item == list)) - + (item->next != nullptr ? ", " : ""); - } - - return result; -} - -QString DumpAstVisitor::parseCaseBlock(CaseBlock *block) -{ - QString result = "{\n"; - - for (auto *item = block->clauses; item != nullptr; item = item->next) { - result += formatLine("case "+parseExpression(item->clause->expression)+":"); - m_indentLevel++; - result += parseStatementList(item->clause->statements); - m_indentLevel--; - } - - if (block->defaultClause) { - result += formatLine("default:"); - m_indentLevel++; - result += parseStatementList(block->defaultClause->statements); - m_indentLevel--; - } - - result += formatLine("}", false); - - return result; -} - -QString DumpAstVisitor::parseExportSpecifier(ExportSpecifier *specifier) -{ - QString result = specifier->identifier.toString(); - - if (!specifier->exportedIdentifier.isEmpty()) - result += " as " + specifier->exportedIdentifier; - - return result; -} - -QString DumpAstVisitor::parseExportsList(ExportsList *list) -{ - QString result = ""; - - for (auto *item = list; item != nullptr; item = item->next) { - result += formatLine(parseExportSpecifier(item->exportSpecifier) - + (item->next != nullptr ? "," : "")); - } - - return result; -} - -static bool needsSemicolon(int kind) -{ - switch (kind) { - case Node::Kind_ForStatement: - case Node::Kind_ForEachStatement: - case Node::Kind_IfStatement: - case Node::Kind_SwitchStatement: - case Node::Kind_WhileStatement: - case Node::Kind_DoWhileStatement: - case Node::Kind_TryStatement: - case Node::Kind_WithStatement: - return false; - default: - return true; - } -} - -QString DumpAstVisitor::parseBlock(Block *block, bool hasNext, bool allowBraceless) -{ - bool hasOneLine = - (block->statements != nullptr && block->statements->next == nullptr) && allowBraceless; - - QString result = hasOneLine ? "\n" : "{\n"; - m_indentLevel++; - result += parseStatementList(block->statements); - m_indentLevel--; - - if (hasNext) - result += formatLine(hasOneLine ? "" : "} ", false); - - if (!hasNext && !hasOneLine) - result += formatLine("}", false); - - if (block->statements) { - m_blockNeededBraces |= !needsSemicolon(block->statements->statement->kind) - || (block->statements->next != nullptr); - } else { - m_blockNeededBraces = true; - } - - return result; -} - -static bool endsWithSemicolon(const QStringView s) -{ - return s.trimmed().endsWith(';'); -} - -QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, - bool blockAllowBraceless) -{ - if (statement == nullptr) - return ""; - - switch (statement->kind) - { - case Node::Kind_EmptyStatement: - return ""; - case Node::Kind_ExpressionStatement: - return parseExpression(cast(statement)->expression); - case Node::Kind_VariableStatement: - return parseVariableDeclarationList(cast(statement)->declarations); - case Node::Kind_ReturnStatement: - return "return "+parseExpression(cast(statement)->expression); - case Node::Kind_ContinueStatement: - return "continue"; - case Node::Kind_BreakStatement: - return "break"; - case Node::Kind_SwitchStatement: { - auto *switchStatement = cast(statement); - - QString result = "switch ("+parseExpression(switchStatement->expression)+") "; - - result += parseCaseBlock(switchStatement->block); - - return result; - } - case Node::Kind_IfStatement: { - auto *ifStatement = cast(statement); - - m_blockNeededBraces = !blockAllowBraceless; - - QString ifFalse = parseStatement(ifStatement->ko, false, true); - QString ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), true); - - bool ifTrueBlock = ifStatement->ok->kind == Node::Kind_Block; - bool ifFalseBlock = ifStatement->ko - ? (ifStatement->ko->kind == Node::Kind_Block || ifStatement->ko->kind == Node::Kind_IfStatement) - : false; - - if (m_blockNeededBraces) { - ifFalse = parseStatement(ifStatement->ko, false, false); - ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), false); - } - - if (ifStatement->ok->kind != Node::Kind_Block && !endsWithSemicolon(ifTrue)) - ifTrue += ";"; - - if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block - && ifStatement->ko->kind != Node::Kind_IfStatement && !endsWithSemicolon(ifFalse)) - ifFalse += ";"; - - QString result = "if (" + parseExpression(ifStatement->expression) + ")"; - - if (m_blockNeededBraces) { - if (ifStatement->ok->kind != Node::Kind_Block) { - QString result = "{\n"; - m_indentLevel++; - result += formatLine(ifTrue); - m_indentLevel--; - result += formatLine("} ", false); - ifTrue = result; - ifTrueBlock = true; - } - - if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) { - QString result = "{\n"; - m_indentLevel++; - result += formatLine(ifFalse); - m_indentLevel--; - result += formatLine("} ", false); - ifFalse = result; - ifFalseBlock = true; - } - } - - if (ifTrueBlock) { - result += " " + ifTrue; - } else { - result += "\n"; - m_indentLevel++; - result += formatPartlyFormatedLines(ifTrue); - m_indentLevel--; - } - - if (!ifFalse.isEmpty()) - { - if (ifTrueBlock) - result += "else"; - else - result += formatLine("else", false); - - if (ifFalseBlock) { - // Blocks generate an extra newline that we don't want here. - if (!m_blockNeededBraces && ifFalse.endsWith(QLatin1String("\n"))) - ifFalse.chop(1); - - result += " " + ifFalse; - } else { - result += "\n"; - m_indentLevel++; - result += formatPartlyFormatedLines(ifFalse, false); - m_indentLevel--; - } - } - - return result; - } - case Node::Kind_ForStatement: { - auto *forStatement = cast(statement); - - QString expr = parseExpression(forStatement->expression); - QString result = "for ("; - - result += parseVariableDeclarationList(forStatement->declarations); - - result += "; "; - - result += parseExpression(forStatement->condition) + "; "; - result += parseExpression(forStatement->expression)+")"; - - const QString statement = parseStatement(forStatement->statement); - - if (!statement.isEmpty()) - result += " "+statement; - else - result += ";"; - - return result; - } - case Node::Kind_ForEachStatement: { - auto *forEachStatement = cast(statement); - - QString result = "for ("; - - PatternElement *patternElement = cast(forEachStatement->lhs); - - if (patternElement != nullptr) - result += parsePatternElement(patternElement); - else - result += parseExpression(forEachStatement->lhs->expressionCast()); - - switch (forEachStatement->type) - { - case ForEachType::In: - result += " in "; - break; - case ForEachType::Of: - result += " of "; - break; - } - - result += parseExpression(forEachStatement->expression) + ")"; - - const QString statement = parseStatement(forEachStatement->statement); - - if (!statement.isEmpty()) - result += " "+statement; - else - result += ";"; - - return result; - } - case Node::Kind_WhileStatement: { - auto *whileStatement = cast(statement); - - m_blockNeededBraces = false; - - auto statement = parseStatement(whileStatement->statement, false, true); - - QString result = "while ("+parseExpression(whileStatement->expression) + ")"; - - if (!statement.isEmpty()) - result += (m_blockNeededBraces ? " " : "") + statement; - else - result += ";"; - - return result; - } - case Node::Kind_DoWhileStatement: { - auto *doWhileStatement = cast(statement); - return "do " + parseBlock(cast(doWhileStatement->statement), true, false) - + "while (" + parseExpression(doWhileStatement->expression) + ")"; - } - case Node::Kind_TryStatement: { - auto *tryStatement = cast(statement); - - Catch *catchExpr = tryStatement->catchExpression; - Finally *finallyExpr = tryStatement->finallyExpression; - - QString result; - - result += "try " + parseBlock(cast(tryStatement->statement), true, false); - - result += "catch (" + parsePatternElement(catchExpr->patternElement, false) + ") " - + parseBlock(cast(catchExpr->statement), finallyExpr, false); - - if (finallyExpr) { - result += "finally " + parseBlock(cast(tryStatement->statement), false, false); - } - - return result; - } - case Node::Kind_Block: { - return parseBlock(cast(statement), blockHasNext, blockAllowBraceless); - } - case Node::Kind_ThrowStatement: - return "throw "+parseExpression(cast(statement)->expression); - case Node::Kind_LabelledStatement: { - auto *labelledStatement = cast(statement); - QString result = labelledStatement->label+":\n"; - result += formatLine(parseStatement(labelledStatement->statement), false); - - return result; - } - case Node::Kind_WithStatement: { - auto *withStatement = cast(statement); - return "with (" + parseExpression(withStatement->expression) + ") " - + parseStatement(withStatement->statement); - } - case Node::Kind_DebuggerStatement: { - return "debugger"; - } - case Node::Kind_ExportDeclaration: - m_error = true; - return "export_decl_unsupported"; - case Node::Kind_ImportDeclaration: - m_error = true; - return "import_decl_unsupported"; - default: - m_error = true; - return "unknown_statement_"+QString::number(statement->kind); - } -} - -QString DumpAstVisitor::parseStatementList(StatementList *list) -{ - QString result = ""; - - if (list == nullptr) - return ""; - - result += getOrphanedComments(list); - - for (auto *item = list; item != nullptr; item = item->next) { - QString statement = parseStatement(item->statement->statementCast(), false, true); - if (statement.isEmpty()) - continue; - - QString commentFront = getComment(item->statement, Comment::Location::Front); - QString commentBackInline = getComment(item->statement, Comment::Location::Back_Inline); - - if (!commentFront.isEmpty()) - result += formatLine(commentFront); - - result += formatLine(statement + (needsSemicolon(item->statement->kind) ? ";" : "") - + commentBackInline); - } - - return result; -} - -bool DumpAstVisitor::visit(UiPublicMember *node) { - - QString commentFront = getComment(node, Comment::Location::Front); - QString commentBackInline = getComment(node, Comment::Location::Back_Inline); - - switch (node->type) - { - case UiPublicMember::Signal: - if (scope().m_firstSignal) { - if (scope().m_firstOfAll) - scope().m_firstOfAll = false; - else - addNewLine(); - - scope().m_firstSignal = false; - } - - addLine(commentFront); - addLine("signal "+node->name.toString()+"("+parseUiParameterList(node->parameters) + ")" - + commentBackInline); - break; - case UiPublicMember::Property: { - if (scope().m_firstProperty) { - if (scope().m_firstOfAll) - scope().m_firstOfAll = false; - else - addNewLine(); - - 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); - - if (!statement.isEmpty()) - statement.prepend(": "); - - if (is_required) - prefix += "required "; - - if (is_default) - prefix += "default "; - - if (is_readonly) - prefix += "readonly "; - - QString member_type = parseUiQualifiedId(node->memberType); - - if (has_type_modifier) - member_type = node->typeModifier + "<" + member_type + ">"; - - addLine(commentFront); - 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; - } - } - - return true; -} - -QString DumpAstVisitor::generateIndent(int indentLevel) const -{ - return QString(m_indentWidth * indentLevel, m_indentation == Indentation::Tabs ? '\t' : ' '); -} - -QString DumpAstVisitor::formatLine(QString line, bool newline) const -{ - QString result = generateIndent(m_indentLevel) + line; - if (newline) - result += "\n"; - - return result; -} - -QString DumpAstVisitor::formatPartlyFormatedLines(QString line, bool newline) const -{ - QString result; - - const auto lines = QStringView { line }.split('\n'); - auto it = lines.cbegin(); - const auto endi = lines.cend(); - if (it != endi) { - result += generateIndent(m_indentLevel) + *it; - ++it; - const QString addonIdent = generateIndent(1); - for (; it != endi; ++it) { - result += '\n'; - result += addonIdent + *it; - } - } - - if (newline) - result += "\n"; - - return result; -} - -void DumpAstVisitor::addNewLine(bool always) { - if (!always && m_result.endsWith("\n\n")) - return; - - m_result += "\n"; -} - -void DumpAstVisitor::addLine(QString line) { - // addLine does not support empty lines, use addNewLine(true) for that - if (line.isEmpty()) - return; - - m_result += formatLine(line); -} - -QHash findBindings(UiObjectMemberList *list) { - QHash 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(item->member); - - if (member->type != UiPublicMember::Property) - continue; - - bindings[member->name.toString()] = nullptr; - - break; - } - case Node::Kind_UiObjectBinding: { - UiObjectBinding *binding = cast(item->member); - - const QString name = parseUiQualifiedId(binding->qualifiedId); - - if (bindings.contains(name)) - bindings[name] = binding; - - break; - } - case Node::Kind_UiArrayBinding: { - UiArrayBinding *binding = cast(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(UiInlineComponent *node) -{ - m_component_name = node->name.toString(); - return true; -} - -bool DumpAstVisitor::visit(UiObjectDefinition *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(getComment(node, Comment::Location::Front_Inline)); - - QString component = ""; - - if (!m_component_name.isEmpty()) { - component = "component "+m_component_name+": "; - m_component_name = ""; - } - - addLine(component + 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(UiObjectDefinition *node) { - m_indentLevel--; - - m_scope_properties.pop(); - - bool need_comma = scope().m_inArrayBinding && scope().m_lastInArrayBinding != node; - - addLine(need_comma ? "}," : "}"); - addLine(getComment(node, Comment::Location::Back)); - if (!scope().m_inArrayBinding) - addNewLine(); -} - -bool DumpAstVisitor::visit(UiEnumDeclaration *node) { - - addNewLine(); - - addLine(getComment(node, Comment::Location::Front)); - addLine("enum " + node->name + " {"); - m_indentLevel++; - m_result += getOrphanedComments(node); - - return true; -} - -void DumpAstVisitor::endVisit(UiEnumDeclaration *) { - m_indentLevel--; - addLine("}"); - - addNewLine(); -} - -bool DumpAstVisitor::visit(UiEnumMemberList *node) { - for (auto *members = node; members != nullptr; members = members->next) { - - addLine(getListItemComment(members->memberToken, Comment::Location::Front)); - - QString line = members->member.toString(); - - if (members->valueToken.isValid()) - line += " = "+QString::number(members->value); - - if (members->next != nullptr) - line += ","; - - line += getListItemComment(members->memberToken, Comment::Location::Back_Inline); - - addLine(line); - } - - return true; -} - -bool DumpAstVisitor::visit(UiScriptBinding *node) { - if (scope().m_firstBinding) { - if (scope().m_firstOfAll) - scope().m_firstOfAll = false; - else - addNewLine(); - - if (parseUiQualifiedId(node->qualifiedId) != "id") - scope().m_firstBinding = false; - } - - addLine(getComment(node, Comment::Location::Front)); - - bool multiline = !needsSemicolon(node->statement->kind); - - if (multiline) { - m_indentLevel++; - } - - QString statement = parseStatement(node->statement); - - if (multiline) { - statement = "{\n" + formatLine(statement); - m_indentLevel--; - statement += formatLine("}", false); - } - - 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 (!scope().m_pendingBinding && scope().m_firstBinding) { - if (scope().m_firstOfAll) - scope().m_firstOfAll = false; - else - addNewLine(); - - scope().m_firstBinding = false; - } - - 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++; - - ScopeProperties props; - props.m_inArrayBinding = true; - - for (auto *item = node->members; item != nullptr; item = item->next) { - if (item->next == nullptr) - props.m_lastInArrayBinding = item->member; - } - - m_scope_properties.push(props); - - m_result += getOrphanedComments(node); - - return true; -} - -void DumpAstVisitor::endVisit(UiArrayBinding *) { - m_indentLevel--; - m_scope_properties.pop(); - addLine("]"); -} - -bool DumpAstVisitor::visit(FunctionDeclaration *node) { - if (scope().m_firstFunction) { - if (scope().m_firstOfAll) - scope().m_firstOfAll = false; - else - addNewLine(); - - scope().m_firstFunction = false; - } - - addLine(getComment(node, Comment::Location::Front)); - - QString head = "function"; - - if (node->isGenerator) - head += "*"; - - head += " "+node->name+"("+parseFormalParameterList(node->formals)+")"; - - if (node->typeAnnotation != nullptr) - head += " : " + parseType(node->typeAnnotation->type); - - head += " {"; - - addLine(head); - m_indentLevel++; - - return true; -} - -void DumpAstVisitor::endVisit(FunctionDeclaration *node) -{ - m_result += parseStatementList(node->body); - m_indentLevel--; - addLine("}"); - addNewLine(); -} - -bool DumpAstVisitor::visit(UiObjectBinding *node) { - if (!scope().m_pendingBinding && scope().m_firstObject) { - if (scope().m_firstOfAll) - scope().m_firstOfAll = false; - else - addNewLine(); - - 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) + ": "); - - if (scope().m_pendingBinding) { - m_result += result + " {\n"; - - scope().m_pendingBinding = false; - } else { - addNewLine(); - addLine(getComment(node, Comment::Location::Front)); - addLine(getComment(node, Comment::Location::Front_Inline)); - addLine(result + " {"); - } - - m_indentLevel++; - - return true; -} - -void DumpAstVisitor::endVisit(UiObjectBinding *node) { - m_indentLevel--; - m_scope_properties.pop(); - - addLine("}"); - addLine(getComment(node, Comment::Location::Back)); - - addNewLine(); -} - -bool DumpAstVisitor::visit(UiImport *node) { - scope().m_firstOfAll = false; - - addLine(getComment(node, Comment::Location::Front)); - - QString result = "import "; - - if (!node->fileName.isEmpty()) - result += escapeString(node->fileName.toString()); - else - result += parseUiQualifiedId(node->importUri); - - if (node->version) { - const auto version = node->version->version; - - if (version.hasMajorVersion()) { - result += " " + QString::number(version.majorVersion()); - - if (version.hasMinorVersion()) - result += "." + QString::number(version.minorVersion()); - } - } - - if (node->asToken.isValid()) { - result +=" as " + node->importId; - } - - result += getComment(node, Comment::Location::Back_Inline); - - addLine(result); - - 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 deleted file mode 100644 index 7d37a4b90f..0000000000 --- a/tools/qmlformat/dumpastvisitor.h +++ /dev/null @@ -1,158 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef DUMPAST_H -#define DUMPAST_H - -#include -#include - -#include -#include - -#include "commentastvisitor.h" - -class DumpAstVisitor : protected QQmlJS::AST::Visitor -{ -public: - enum Indentation { Tabs, Spaces }; - - DumpAstVisitor(QQmlJS::Engine *engine, QQmlJS::AST::Node *rootNode, CommentAstVisitor *comment, - int indentWidth, Indentation indentation); - - QString toString() const { return m_result; } - - bool preVisit(QQmlJS::AST::Node *) override; - - bool visit(QQmlJS::AST::UiScriptBinding *node) override; - - bool visit(QQmlJS::AST::UiArrayBinding *node) override; - void endVisit(QQmlJS::AST::UiArrayBinding *node) override; - - bool visit(QQmlJS::AST::UiObjectBinding *node) override; - void endVisit(QQmlJS::AST::UiObjectBinding *node) override; - - bool visit(QQmlJS::AST::FunctionDeclaration *node) override; - void endVisit(QQmlJS::AST::FunctionDeclaration *node) override; - - bool visit(QQmlJS::AST::UiInlineComponent *node) override; - - bool visit(QQmlJS::AST::UiObjectDefinition *node) override; - void endVisit(QQmlJS::AST::UiObjectDefinition *node) override; - - bool visit(QQmlJS::AST::UiEnumDeclaration *node) override; - void endVisit(QQmlJS::AST::UiEnumDeclaration *node) override; - - bool visit(QQmlJS::AST::UiEnumMemberList *node) override; - bool visit(QQmlJS::AST::UiPublicMember *node) override; - bool visit(QQmlJS::AST::UiImport *node) override; - bool visit(QQmlJS::AST::UiPragma *node) override; - - bool visit(QQmlJS::AST::UiAnnotation *node) override; - void endVisit(QQmlJS::AST::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_firstFunction = true; - bool m_inArrayBinding = false; - bool m_pendingBinding = false; - - QQmlJS::AST::UiObjectMember* m_lastInArrayBinding = nullptr; - QHash m_bindings; - }; - - QString generateIndent(int indentLevel) const; - QString formatLine(QString line, bool newline = true) const; - QString formatPartlyFormatedLines(QString line, bool newline = true) const; - - QString formatComment(const Comment &comment) const; - - QString getComment(QQmlJS::AST::Node *node, Comment::Location location) const; - QString getListItemComment(QQmlJS::SourceLocation srcLocation, Comment::Location location) const; - - void addNewLine(bool always = false); - void addLine(QString line); - - QString getOrphanedComments(QQmlJS::AST::Node *node) const; - - QString parseStatement(QQmlJS::AST::Statement *statement, bool blockHasNext = false, - bool blockAllowBraceless = false); - QString parseStatementList(QQmlJS::AST::StatementList *list); - - QString parseExpression(QQmlJS::AST::ExpressionNode *expression); - - QString parsePatternElement(QQmlJS::AST::PatternElement *element, bool scope = true); - QString parsePatternElementList(QQmlJS::AST::PatternElementList *element); - - QString parsePatternProperty(QQmlJS::AST::PatternProperty *property); - QString parsePatternPropertyList(QQmlJS::AST::PatternPropertyList *list); - - QString parseArgumentList(QQmlJS::AST::ArgumentList *list); - - QString parseUiParameterList(QQmlJS::AST::UiParameterList *list); - - QString parseVariableDeclarationList(QQmlJS::AST::VariableDeclarationList *list); - - QString parseCaseBlock(QQmlJS::AST::CaseBlock *block); - QString parseBlock(QQmlJS::AST::Block *block, bool hasNext, bool allowBraceless); - - QString parseExportsList(QQmlJS::AST::ExportsList *list); - QString parseExportSpecifier(QQmlJS::AST::ExportSpecifier *specifier); - - QString parseFormalParameterList(QQmlJS::AST::FormalParameterList *list); - - QString parseType(QQmlJS::AST::Type *type); - - QString parseFunctionExpression(QQmlJS::AST::FunctionExpression *expression, bool omitFunction = false); - - ScopeProperties& scope() { return m_scope_properties.top(); } - - int m_indentLevel = 0; - - bool m_error = false; - bool m_blockNeededBraces = false; - - QStack m_scope_properties; - - QString m_result = ""; - QString m_component_name = ""; - QQmlJS::Engine *m_engine; - CommentAstVisitor *m_comment; - int m_indentWidth; - Indentation m_indentation; -}; - -#endif // DUMPAST_H diff --git a/tools/qmlformat/main.cpp b/tools/qmlformat/main.cpp deleted file mode 100644 index 3f11ce257a..0000000000 --- a/tools/qmlformat/main.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#if QT_CONFIG(commandlineparser) -#include -#endif - -#include "commentastvisitor.h" -#include "dumpastvisitor.h" -#include "restructureastvisitor.h" - -struct Options -{ - bool verbose = false; - bool inplace = false; - bool force = false; - bool tabs = false; - bool valid = false; - - int indentWidth = 4; - bool indentWidthSet = false; - QString newline = "native"; - - QStringList files; - QStringList arguments; - QStringList errors; -}; - -bool parseFile(const QString &filename, const Options &options) -{ - QFile file(filename); - - if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) { - qWarning().noquote() << "Failed to open" << filename << "for reading."; - return false; - } - - QString code = QString::fromUtf8(file.readAll()); - file.close(); - - QQmlJS::Engine engine; - QQmlJS::Lexer lexer(&engine); - - lexer.setCode(code, 1, true); - QQmlJS::Parser parser(&engine); - - bool success = parser.parse(); - - if (!success) { - const auto diagnosticMessages = parser.diagnosticMessages(); - for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { - qWarning().noquote() << QString::fromLatin1("%1:%2 : %3") - .arg(filename).arg(m.loc.startLine).arg(m.message); - } - - qWarning().noquote() << "Failed to parse" << filename; - return false; - } - - // Try to attach comments to AST nodes - CommentAstVisitor comment(&engine, parser.rootNode()); - - if (options.verbose) - qWarning().noquote() << comment.attachedComments().size() << "comment(s) attached."; - - if (options.verbose) { - int orphaned = 0; - - for (const auto& orphanList : comment.orphanComments().values()) - orphaned += orphanList.size(); - - qWarning().noquote() << orphaned << "comments are orphans."; - } - - // Do the actual restructuring - RestructureAstVisitor restructure(parser.rootNode()); - - // Turn AST back into source code - if (options.verbose) - qWarning().noquote() << "Dumping" << filename; - - DumpAstVisitor dump( - &engine, parser.rootNode(), &comment, options.tabs ? 1 : options.indentWidth, - options.tabs ? DumpAstVisitor::Indentation::Tabs : DumpAstVisitor::Indentation::Spaces); - - QString dumpCode = dump.toString(); - - lexer.setCode(dumpCode, 1, true); - - bool dumpSuccess = parser.parse(); - - if (!dumpSuccess) { - if (options.verbose) { - const auto diagnosticMessages = parser.diagnosticMessages(); - for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { - qWarning().noquote() << QString::fromLatin1(":%2 : %3") - .arg(m.loc.startLine).arg(m.message); - } - } - - qWarning().noquote() << "Failed to parse formatted code."; - } - - if (dump.error() || !dumpSuccess) { - if (options.force) { - qWarning().noquote() << "An error has occurred. The output may not be reliable."; - } else { - qWarning().noquote() << "An error has occurred. Aborting."; - return false; - } - } - - const bool native = options.newline == "native"; - - if (!native) { - if (options.newline == "macos") { - dumpCode = dumpCode.replace("\n", "\r"); - } else if (options.newline == "windows") { - dumpCode = dumpCode.replace("\n", "\r\n"); - } else if (options.newline == "unix") { - // Nothing needs to be done for unix line-endings - } else { - qWarning().noquote() << "Unknown line ending type" << options.newline; - return false; - } - } - - if (options.inplace) { - if (options.verbose) - qWarning().noquote() << "Writing to file" << filename; - - if (!file.open(native ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::WriteOnly)) { - qWarning().noquote() << "Failed to open" << filename << "for writing"; - return false; - } - - file.write(dumpCode.toUtf8()); - file.close(); - } else { - QFile out; - out.open(stdout, QIODevice::WriteOnly); - out.write(dumpCode.toUtf8()); - } - - return true; -} - -Options buildCommandLineOptions(const QCoreApplication &app) -{ -#if QT_CONFIG(commandlineparser) - QCommandLineParser parser; - parser.setApplicationDescription("Formats QML files according to the QML Coding Conventions."); - parser.addHelpOption(); - parser.addVersionOption(); - - parser.addOption(QCommandLineOption({"V", "verbose"}, - QStringLiteral("Verbose mode. Outputs more detailed information."))); - - parser.addOption(QCommandLineOption({"i", "inplace"}, - QStringLiteral("Edit file in-place instead of outputting to stdout."))); - - parser.addOption(QCommandLineOption({"f", "force"}, - QStringLiteral("Continue even if an error has occurred."))); - - parser.addOption( - QCommandLineOption({ "t", "tabs" }, QStringLiteral("Use tabs instead of spaces."))); - - parser.addOption(QCommandLineOption({ "w", "indent-width" }, - QStringLiteral("How many spaces are used when indenting."), - "width", "4")); - - parser.addOption(QCommandLineOption( - { "F", "files" }, QStringLiteral("Format all files listed in file, in-place"), "file")); - - parser.addOption(QCommandLineOption({"l", "newline"}, - QStringLiteral("Override the new line format to use (native macos unix windows)."), - "newline", "native")); - - parser.addPositionalArgument("filenames", "files to be processed by qmlformat"); - - parser.process(app); - - bool indentWidthOkay = false; - const int indentWidth = parser.value("indent-width").toInt(&indentWidthOkay); - if (!indentWidthOkay) { - Options options; - options.errors.push_back("Error: Invalid value passed to -w"); - return options; - } - - QStringList files; - if (parser.isSet("files")) { - QFile file(parser.value("files")); - file.open(QIODevice::Text | QIODevice::ReadOnly); - if (file.isOpen()) { - QTextStream in(&file); - while (!in.atEnd()) { - QString file = in.readLine(); - - if (file.isEmpty()) - continue; - - files.push_back(file); - } - } - } - - Options options; - options.verbose = parser.isSet("verbose"); - options.inplace = parser.isSet("inplace"); - options.force = parser.isSet("force"); - options.tabs = parser.isSet("tabs"); - options.valid = true; - - options.indentWidth = indentWidth; - options.indentWidthSet = parser.isSet("indent-width"); - options.newline = parser.value("newline"); - options.files = files; - options.arguments = parser.positionalArguments(); - return options; -#else - return Options {}; -#endif -} - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - QCoreApplication::setApplicationName("qmlformat"); - QCoreApplication::setApplicationVersion("1.0"); - - const auto options = buildCommandLineOptions(app); - if (!options.valid) { - for (const auto &error : options.errors) { - qWarning().noquote() << error; - } - - return -1; - } - - bool success = true; - if (!options.files.isEmpty()) { - if (!options.arguments.isEmpty()) - qWarning() << "Warning: Positional arguments are ignored when -F is used"; - - for (const QString &file : options.files) { - Q_ASSERT(!file.isEmpty()); - - if (!parseFile(file, options)) - success = false; - } - } else { - for (const QString &file : options.arguments) { - if (!parseFile(file, options)) - success = false; - } - } - - return success ? 0 : 1; -} diff --git a/tools/qmlformat/qmlformat.cpp b/tools/qmlformat/qmlformat.cpp new file mode 100644 index 0000000000..7f7cdfbb24 --- /dev/null +++ b/tools/qmlformat/qmlformat.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_CONFIG(commandlineparser) +# include +#endif + +using namespace QQmlJS::Dom; + +struct Options +{ + bool verbose = false; + bool inplace = false; + bool force = false; + bool tabs = false; + bool valid = false; + bool normalize = false; + + int indentWidth = 4; + bool indentWidthSet = false; + QString newline = "native"; + + QStringList files; + QStringList arguments; + QStringList errors; +}; + +bool parseFile(const QString &filename, const Options &options) +{ + DomItem env = + DomEnvironment::create(QStringList(), + QQmlJS::Dom::DomEnvironment::Option::SingleThreaded + | QQmlJS::Dom::DomEnvironment::Option::NoDependencies); + DomItem tFile; // place where to store the loaded file + env.loadFile( + filename, QString(), + [&tFile](Path, const DomItem &, const DomItem &newIt) { + tFile = newIt; // callback called when everything is loaded that receives the loaded + // external file pair (path, oldValue, newValue) + }, + LoadOption::DefaultLoad); + env.loadPendingDependencies(); + DomItem qmlFile = tFile.fileObject(); + std::shared_ptr qmlFilePtr = qmlFile.ownerAs(); + if (!qmlFilePtr || !qmlFilePtr->isValid()) { + qmlFile.iterateErrors( + [](DomItem, ErrorMessage msg) { + errorToQDebug(msg); + return true; + }, + true); + qWarning().noquote() << "Failed to parse" << filename; + return false; + } + + // Turn AST back into source code + if (options.verbose) + qWarning().noquote() << "Dumping" << filename; + + LineWriterOptions lwOptions; + lwOptions.formatOptions.indentSize = options.indentWidth; + lwOptions.formatOptions.useTabs = options.tabs; + lwOptions.updateOptions = LineWriterOptions::Update::None; + if (options.newline == "native") { + // find out current line endings... + QStringView code = qmlFilePtr->code(); + int newlineIndex = code.indexOf(QChar(u'\n')); + int crIndex = code.indexOf(QChar(u'\r')); + if (newlineIndex >= 0) { + if (crIndex >= 0) { + if (crIndex + 1 == newlineIndex) + lwOptions.lineEndings = LineWriterOptions::LineEndings::Windows; + else + qWarning().noquote() << "Invalid line ending in file, using default"; + + } else { + lwOptions.lineEndings = LineWriterOptions::LineEndings::Unix; + } + } else if (crIndex >= 0) { + lwOptions.lineEndings = LineWriterOptions::LineEndings::OldMacOs; + } else { + qWarning().noquote() << "Unknown line ending in file, using default"; + } + } else if (options.newline == "macos") { + lwOptions.lineEndings = LineWriterOptions::LineEndings::OldMacOs; + } else if (options.newline == "windows") { + lwOptions.lineEndings = LineWriterOptions::LineEndings::Windows; + } else if (options.newline == "unix") { + lwOptions.lineEndings = LineWriterOptions::LineEndings::Unix; + } else { + qWarning().noquote() << "Unknown line ending type" << options.newline; + return false; + } + + if (options.normalize) + lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Normalize; + else + lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Preserve; + WriteOutChecks checks = WriteOutCheck::Default; + if (options.force || qmlFilePtr->code().size() > 32000) + checks = WriteOutCheck::None; + + MutableDomItem res; + if (options.inplace) { + if (options.verbose) + qWarning().noquote() << "Writing to file" << filename; + FileWriter fw; + res = qmlFile.writeOut(filename, 2, lwOptions, &fw, checks); + } else { + QFile out; + out.open(stdout, QIODevice::WriteOnly); + LineWriter lw([&out](QStringView s) { out.write(s.toUtf8()); }, filename, lwOptions); + OutWriter ow(lw); + res = qmlFile.writeOutForFile(ow, checks); + ow.flush(); + } + return bool(res); +} + +Options buildCommandLineOptions(const QCoreApplication &app) +{ +#if QT_CONFIG(commandlineparser) + QCommandLineParser parser; + parser.setApplicationDescription("Formats QML files according to the QML Coding Conventions."); + parser.addHelpOption(); + parser.addVersionOption(); + + parser.addOption( + QCommandLineOption({ "V", "verbose" }, + QStringLiteral("Verbose mode. Outputs more detailed information."))); + + parser.addOption(QCommandLineOption( + { "i", "inplace" }, + QStringLiteral("Edit file in-place instead of outputting to stdout."))); + + parser.addOption(QCommandLineOption({ "f", "force" }, + QStringLiteral("Continue even if an error has occurred."))); + + parser.addOption( + QCommandLineOption({ "t", "tabs" }, QStringLiteral("Use tabs instead of spaces."))); + + parser.addOption(QCommandLineOption({ "w", "indent-width" }, + QStringLiteral("How many spaces are used when indenting."), + "width", "4")); + + parser.addOption(QCommandLineOption({ "n", "normalize" }, + QStringLiteral("Reorders the attributes of the objects " + "according to the QML Coding Guidelines."))); + + parser.addOption(QCommandLineOption( + { "F", "files" }, QStringLiteral("Format all files listed in file, in-place"), "file")); + + parser.addOption(QCommandLineOption( + { "l", "newline" }, + QStringLiteral("Override the new line format to use (native macos unix windows)."), + "newline", "native")); + + parser.addPositionalArgument("filenames", "files to be processed by qmlformat"); + + parser.process(app); + + bool indentWidthOkay = false; + const int indentWidth = parser.value("indent-width").toInt(&indentWidthOkay); + if (!indentWidthOkay) { + Options options; + options.errors.push_back("Error: Invalid value passed to -w"); + return options; + } + + QStringList files; + if (!parser.value("files").isEmpty()) { + QFile file(parser.value("files")); + file.open(QIODevice::Text | QIODevice::ReadOnly); + if (file.isOpen()) { + QTextStream in(&file); + while (!in.atEnd()) { + QString file = in.readLine(); + + if (file.isEmpty()) + continue; + + files.push_back(file); + } + } + } + + Options options; + options.verbose = parser.isSet("verbose"); + options.inplace = parser.isSet("inplace"); + options.force = parser.isSet("force"); + options.tabs = parser.isSet("tabs"); + options.normalize = parser.isSet("normalize"); + options.valid = true; + + options.indentWidth = indentWidth; + options.indentWidthSet = parser.isSet("indent-width"); + options.newline = parser.value("newline"); + options.files = files; + options.arguments = parser.positionalArguments(); + return options; +#else + return Options {}; +#endif +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QCoreApplication::setApplicationName("qmlformat"); + QCoreApplication::setApplicationVersion("1.0"); + + const auto options = buildCommandLineOptions(app); + if (!options.valid) { + for (const auto &error : options.errors) { + qWarning().noquote() << error; + } + + return -1; + } + + bool success = true; + if (!options.files.isEmpty()) { + if (!options.arguments.isEmpty()) + qWarning() << "Warning: Positional arguments are ignored when -F is used"; + + for (const QString &file : options.files) { + Q_ASSERT(!file.isEmpty()); + + if (!parseFile(file, options)) + success = false; + } + } else { + for (const QString &file : options.arguments) { + if (!parseFile(file, options)) + success = false; + } + } + + return success ? 0 : 1; +} diff --git a/tools/qmlformat/restructureastvisitor.cpp b/tools/qmlformat/restructureastvisitor.cpp deleted file mode 100644 index 18021a0a2a..0000000000 --- a/tools/qmlformat/restructureastvisitor.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "restructureastvisitor.h" - -#include - -using namespace QQmlJS::AST; - -RestructureAstVisitor::RestructureAstVisitor(Node *rootNode) -{ - rootNode->accept(this); -} - -template -static QList findKind(UiObjectMemberList *list) -{ - QList members; - for (auto *item = list; item != nullptr; item = item->next) { - if (cast(item->member) != nullptr) - members.append(cast(item->member)); - } - - return members; -} - -template -static QList findKind(UiHeaderItemList *list) -{ - QList members; - for (auto *item = list; item != nullptr; item = item->next) { - if (cast(item->headerItem) != nullptr) - members.append(cast(item->headerItem)); - } - - return members; -} - -static QString parseUiQualifiedId(UiQualifiedId *id) -{ - QString name = id->name.toString(); - for (auto *item = id->next; item != nullptr; item = item->next) { - name += "." + item->name; - } - - return name; -} - -void RestructureAstVisitor::endVisit(UiObjectMemberList *node) -{ - QList correctOrder; - - QList largeScriptBinding; - - UiObjectMember *states = nullptr; - UiObjectMember *transitions = nullptr; - - auto enumDeclarations = findKind(node); - auto scriptBindings = findKind(node); - auto arrayBindings = findKind(node); - auto publicMembers = findKind(node); - auto sourceElements = findKind(node); - auto objectDefinitions = findKind(node); - - // Look for transitions and states - for (auto *binding : findKind(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 (parseUiQualifiedId(binding->qualifiedId) == "id") { - correctOrder.append(binding); - - scriptBindings.removeOne(binding); - break; - } - } - - // 2nd enums - for (auto *enumDeclaration : enumDeclarations) - correctOrder.append(enumDeclaration); - - // 3rd property declarations - for (auto *publicMember : publicMembers) { - if (publicMember->type != UiPublicMember::Property) - continue; - - correctOrder.append(publicMember); - } - - // 4th signals - for (auto *publicMember : publicMembers) { - if (publicMember->type != UiPublicMember::Signal) - continue; - - correctOrder.append(publicMember); - } - - // 5th functions - for (auto *source : sourceElements) - 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); - - // 7th child objects - for (auto *objectDefinition : objectDefinitions) - correctOrder.append(objectDefinition); - - // 8th all the rest - for (auto *item = node; item != nullptr; item = item->next) { - if (!correctOrder.contains(item->member)) - 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(); - correctOrder.pop_front(); - } -} diff --git a/tools/qmlformat/restructureastvisitor.h b/tools/qmlformat/restructureastvisitor.h deleted file mode 100644 index 4d1cb7e643..0000000000 --- a/tools/qmlformat/restructureastvisitor.h +++ /dev/null @@ -1,45 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef RESTRUCTUREASTVISITOR_H -#define RESTRUCTUREASTVISITOR_H - -#include -#include - -class RestructureAstVisitor : protected QQmlJS::AST::Visitor -{ -public: - RestructureAstVisitor(QQmlJS::AST::Node *rootNode); - - void throwRecursionDepthError() override {} - - void endVisit(QQmlJS::AST::UiObjectMemberList *node) override; -}; - -#endif // RESTRUCTUREASTVISITOR_H -- cgit v1.2.3