aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmlformat
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmlformat')
-rw-r--r--tools/qmlformat/.prev_CMakeLists.txt21
-rw-r--r--tools/qmlformat/CMakeLists.txt15
-rw-r--r--tools/qmlformat/commentastvisitor.cpp285
-rw-r--r--tools/qmlformat/commentastvisitor.h143
-rw-r--r--tools/qmlformat/dumpastvisitor.cpp1433
-rw-r--r--tools/qmlformat/dumpastvisitor.h161
-rw-r--r--tools/qmlformat/main.cpp293
-rw-r--r--tools/qmlformat/qmlformat.cpp390
-rw-r--r--tools/qmlformat/restructureastvisitor.cpp194
-rw-r--r--tools/qmlformat/restructureastvisitor.h47
10 files changed, 399 insertions, 2583 deletions
diff --git a/tools/qmlformat/.prev_CMakeLists.txt b/tools/qmlformat/.prev_CMakeLists.txt
deleted file mode 100644
index 618e1bbfaa..0000000000
--- a/tools/qmlformat/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated from qmlformat.pro.
-
-#####################################################################
-## qmlformat Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlformat)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Formatter"
- 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
index e492a3ec56..908901b9f5 100644
--- a/tools/qmlformat/CMakeLists.txt
+++ b/tools/qmlformat/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlformat.pro.
#####################################################################
@@ -9,13 +12,13 @@ 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
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
+ qmlformat.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::QmlToolingSettingsPrivate
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmlformat.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Formatter"
diff --git a/tools/qmlformat/commentastvisitor.cpp b/tools/qmlformat/commentastvisitor.cpp
deleted file mode 100644
index b8d916d3fb..0000000000
--- a/tools/qmlformat/commentastvisitor.cpp
+++ /dev/null
@@ -1,285 +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"
-
-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<Comment> 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<SourceLocation> CommentAstVisitor::findCommentsInLine(quint32 line, bool includePrevious) const
-{
- QList<SourceLocation> 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<Comment> CommentAstVisitor::findOrphanComments(Node *node) const
-{
- QVector<Comment> 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 09bc786985..0000000000
--- a/tools/qmlformat/commentastvisitor.h
+++ /dev/null
@@ -1,143 +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 <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-#include <QtQml/private/qqmljsengine_p.h>
-
-#include <QHash>
-#include <QString>
-#include <QVector>
-
-using namespace QQmlJS::AST;
-using namespace QQmlJS;
-
-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<SourceLocation> srcLocations)
- : m_location(location), m_srcLocations(srcLocations) {
- for (const auto& srcLoc : srcLocations) {
- m_text += engine->code().mid(static_cast<int>(srcLoc.begin()),
- static_cast<int>(srcLoc.end() - srcLoc.begin())) + "\n";
- }
-
- m_text.chop(1);
- }
-
- QList<SourceLocation> 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 SourceLocation& location) const {
- for (const 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;
-};
-
-class CommentAstVisitor : protected Visitor
-{
-public:
- CommentAstVisitor(QQmlJS::Engine *engine, Node *rootNode);
-
- void throwRecursionDepthError() override {}
-
- const QHash<Node *, Comment> attachedComments() const { return m_attachedComments; }
- const QHash<quint32, Comment> listComments() const { return m_listItemComments; }
- const QHash<Node *, QVector<Comment>> orphanComments() const { return m_orphanComments; }
-
- bool visit(UiScriptBinding *node) override;
- bool visit(UiObjectBinding *node) override;
-
- bool visit(UiArrayBinding *node) override;
- void endVisit(UiArrayBinding *node) override;
-
- bool visit(UiObjectDefinition *node) override;
- void endVisit(UiObjectDefinition *) override;
-
- bool visit(UiEnumDeclaration *node) override;
- void endVisit(UiEnumDeclaration *node) override;
-
- bool visit(UiEnumMemberList *node) override;
-
- bool visit(StatementList *node) override;
- 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:
- bool isCommentAttached(const SourceLocation& location) const;
-
- QList<SourceLocation> findCommentsInLine(quint32 line, bool includePrevious = false) const;
-
- Comment findComment(SourceLocation first, SourceLocation last,
- int locations = Comment::DefaultLocations) const;
-
- Comment findComment(Node *node, int locations = Comment::DefaultLocations) const;
- QVector<Comment> findOrphanComments(Node *node) const;
- void attachComment(Node *node, int locations = Comment::DefaultLocations);
-
- QQmlJS::Engine *m_engine;
- QHash<Node *, Comment> m_attachedComments;
- QHash<quint32, Comment> m_listItemComments;
- QHash<Node *, QVector<Comment>> m_orphanComments;
-};
-
-#endif // COMMENTASTVISITOR_H
diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp
deleted file mode 100644
index 723be4e445..0000000000
--- a/tools/qmlformat/dumpastvisitor.cpp
+++ /dev/null
@@ -1,1433 +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 <QtQml/private/qqmljslexer_p.h>
-
-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(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<FunctionExpression *>(property->initializer), true);
- case PatternElement::Setter:
- return "set "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true);
- default:
- if (property->name->kind == Node::Kind_ComputedPropertyName) {
- return "["+parseExpression(cast<ComputedPropertyName *>(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<ReturnStatement *>(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<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)
- return "";
-
- switch (expression->kind)
- {
- case Node::Kind_ArrayPattern:
- return "["+parsePatternElementList(cast<ArrayPattern *>(expression)->elements)+"]";
- case Node::Kind_IdentifierExpression:
- return cast<IdentifierExpression*>(expression)->name.toString();
- case Node::Kind_FieldMemberExpression: {
- auto *fieldMemberExpr = cast<FieldMemberExpression *>(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->name.toString();
-
- return result;
- }
- case Node::Kind_ArrayMemberExpression: {
- auto *arrayMemberExpr = cast<ArrayMemberExpression *>(expression);
- return parseExpression(arrayMemberExpr->base)
- + "[" + parseExpression(arrayMemberExpr->expression) + "]";
- }
- case Node::Kind_NestedExpression:
- return "("+parseExpression(cast<NestedExpression *>(expression)->expression)+")";
- case Node::Kind_TrueLiteral:
- return "true";
- case Node::Kind_FalseLiteral:
- return "false";
- case Node::Kind_FunctionExpression:
- {
- auto *functExpr = cast<FunctionExpression *>(expression);
- return parseFunctionExpression(functExpr);
- }
- case Node::Kind_NullExpression:
- return "null";
- case Node::Kind_ThisExpression:
- return "this";
- case Node::Kind_PostIncrementExpression:
- return parseExpression(cast<PostIncrementExpression *>(expression)->base)+"++";
- case Node::Kind_PreIncrementExpression:
- return "++"+parseExpression(cast<PreIncrementExpression *>(expression)->expression);
- case Node::Kind_PostDecrementExpression:
- return parseExpression(cast<PostDecrementExpression *>(expression)->base)+"--";
- case Node::Kind_PreDecrementExpression:
- return "--"+parseExpression(cast<PreDecrementExpression *>(expression)->expression);
- case Node::Kind_NumericLiteral:
- return QString::number(cast<NumericLiteral *>(expression)->value);
- case Node::Kind_TemplateLiteral: {
- auto firstSrcLoc = cast<TemplateLiteral *>(expression)->firstSourceLocation();
- auto lastSrcLoc = cast<TemplateLiteral *>(expression)->lastSourceLocation();
- return m_engine->code().mid(static_cast<int>(firstSrcLoc.begin()),
- static_cast<int>(lastSrcLoc.end() - firstSrcLoc.begin()));
- }
- case Node::Kind_StringLiteral: {
- auto srcLoc = cast<StringLiteral *>(expression)->firstSourceLocation();
- return m_engine->code().mid(static_cast<int>(srcLoc.begin()),
- static_cast<int>(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<CallExpression *>(expression);
-
- return parseExpression(callExpr->base) + "(" + parseArgumentList(callExpr->arguments) + ")";
- }
- case Node::Kind_NewExpression:
- return "new "+parseExpression(cast<NewExpression *>(expression)->expression);
- case Node::Kind_NewMemberExpression: {
- auto *newMemberExpression = cast<NewMemberExpression *>(expression);
- return "new "+parseExpression(newMemberExpression->base)
- + "(" +parseArgumentList(newMemberExpression->arguments)+")";
- }
- case Node::Kind_DeleteExpression:
- return "delete " + parseExpression(cast<DeleteExpression *>(expression)->expression);
- case Node::Kind_VoidExpression:
- return "void " + parseExpression(cast<VoidExpression *>(expression)->expression);
- case Node::Kind_TypeOfExpression:
- return "typeof " + parseExpression(cast<TypeOfExpression *>(expression)->expression);
- case Node::Kind_UnaryPlusExpression:
- return "+" + parseExpression(cast<UnaryPlusExpression *>(expression)->expression);
- case Node::Kind_UnaryMinusExpression:
- return "-" + parseExpression(cast<UnaryMinusExpression *>(expression)->expression);
- case Node::Kind_NotExpression:
- return "!" + parseExpression(cast<NotExpression *>(expression)->expression);
- case Node::Kind_TildeExpression:
- return "~" + parseExpression(cast<TildeExpression *>(expression)->expression);
- case Node::Kind_ConditionalExpression: {
- auto *condExpr = cast<ConditionalExpression *>(expression);
-
- QString result = "";
-
- result += parseExpression(condExpr->expression) + " ? ";
- result += parseExpression(condExpr->ok) + " : ";
- result += parseExpression(condExpr->ko);
-
- return result;
- }
- case Node::Kind_YieldExpression: {
- auto *yieldExpr = cast<YieldExpression*>(expression);
-
- QString result = "yield";
-
- if (yieldExpr->isYieldStar)
- result += "*";
-
- if (yieldExpr->expression)
- result += " " + parseExpression(yieldExpr->expression);
-
- return result;
- }
- case Node::Kind_ObjectPattern: {
- auto *objectPattern = cast<ObjectPattern*>(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*>(expression);
- return parseExpression(expr->left)+", "+parseExpression(expr->right);
- }
- case Node::Kind_TypeExpression: {
- auto* type = cast<TypeExpression*>(expression);
- return parseType(type->m_type);
- }
- case Node::Kind_RegExpLiteral: {
- auto* regexpLiteral = cast<RegExpLiteral*>(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<ExpressionStatement *>(statement)->expression);
- case Node::Kind_VariableStatement:
- return parseVariableDeclarationList(cast<VariableStatement *>(statement)->declarations);
- case Node::Kind_ReturnStatement:
- return "return "+parseExpression(cast<ReturnStatement *>(statement)->expression);
- case Node::Kind_ContinueStatement:
- return "continue";
- case Node::Kind_BreakStatement:
- return "break";
- case Node::Kind_SwitchStatement: {
- auto *switchStatement = cast<SwitchStatement *>(statement);
-
- QString result = "switch ("+parseExpression(switchStatement->expression)+") ";
-
- result += parseCaseBlock(switchStatement->block);
-
- return result;
- }
- case Node::Kind_IfStatement: {
- auto *ifStatement = cast<IfStatement *>(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<ForStatement *>(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<ForEachStatement *>(statement);
-
- QString result = "for (";
-
- PatternElement *patternElement = cast<PatternElement *>(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<WhileStatement *>(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<DoWhileStatement *>(statement);
- return "do " + parseBlock(cast<Block *>(doWhileStatement->statement), true, false)
- + "while (" + parseExpression(doWhileStatement->expression) + ")";
- }
- case Node::Kind_TryStatement: {
- auto *tryStatement = cast<TryStatement *>(statement);
-
- Catch *catchExpr = tryStatement->catchExpression;
- Finally *finallyExpr = tryStatement->finallyExpression;
-
- QString result;
-
- result += "try " + parseBlock(cast<Block *>(tryStatement->statement), true, false);
-
- result += "catch (" + parsePatternElement(catchExpr->patternElement, false) + ") "
- + parseBlock(cast<Block *>(catchExpr->statement), finallyExpr, false);
-
- if (finallyExpr) {
- result += "finally " + parseBlock(cast<Block *>(tryStatement->statement), false, false);
- }
-
- return result;
- }
- case Node::Kind_Block: {
- return parseBlock(cast<Block *>(statement), blockHasNext, blockAllowBraceless);
- }
- case Node::Kind_ThrowStatement:
- return "throw "+parseExpression(cast<ThrowStatement *>(statement)->expression);
- case Node::Kind_LabelledStatement: {
- auto *labelledStatement = cast<LabelledStatement *>(statement);
- QString result = labelledStatement->label+":\n";
- result += formatLine(parseStatement(labelledStatement->statement), false);
-
- return result;
- }
- case Node::Kind_WithStatement: {
- auto *withStatement = cast<WithStatement *>(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<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(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 657592f403..0000000000
--- a/tools/qmlformat/dumpastvisitor.h
+++ /dev/null
@@ -1,161 +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 <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
-{
-public:
- enum Indentation { Tabs, Spaces };
-
- DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment,
- int indentWidth, Indentation indentation);
-
- QString toString() const { return m_result; }
-
- bool preVisit(Node *) override;
-
- bool visit(UiScriptBinding *node) override;
-
- bool visit(UiArrayBinding *node) override;
- void endVisit(UiArrayBinding *node) override;
-
- bool visit(UiObjectBinding *node) override;
- void endVisit(UiObjectBinding *node) override;
-
- bool visit(FunctionDeclaration *node) override;
- void endVisit(FunctionDeclaration *node) override;
-
- bool visit(UiInlineComponent *node) override;
-
- bool visit(UiObjectDefinition *node) override;
- void endVisit(UiObjectDefinition *node) override;
-
- bool visit(UiEnumDeclaration *node) override;
- void endVisit(UiEnumDeclaration *node) override;
-
- 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_firstFunction = true;
- bool m_inArrayBinding = false;
- bool m_pendingBinding = false;
-
- UiObjectMember* m_lastInArrayBinding = nullptr;
- QHash<QString, UiObjectMember*> 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(Node *node, Comment::Location location) const;
- QString getListItemComment(SourceLocation srcLocation, Comment::Location location) const;
-
- void addNewLine(bool always = false);
- void addLine(QString line);
-
- QString getOrphanedComments(Node *node) const;
-
- QString parseStatement(Statement *statement, bool blockHasNext = false,
- bool blockAllowBraceless = false);
- QString parseStatementList(StatementList *list);
-
- QString parseExpression(ExpressionNode *expression);
-
- QString parsePatternElement(PatternElement *element, bool scope = true);
- QString parsePatternElementList(PatternElementList *element);
-
- QString parsePatternProperty(PatternProperty *property);
- QString parsePatternPropertyList(PatternPropertyList *list);
-
- QString parseArgumentList(ArgumentList *list);
-
- QString parseUiParameterList(UiParameterList *list);
-
- QString parseVariableDeclarationList(VariableDeclarationList *list);
-
- QString parseCaseBlock(CaseBlock *block);
- QString parseBlock(Block *block, bool hasNext, bool allowBraceless);
-
- QString parseExportsList(ExportsList *list);
- QString parseExportSpecifier(ExportSpecifier *specifier);
-
- 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;
-
- QStack<ScopeProperties> 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 da3c1772fb..0000000000
--- a/tools/qmlformat/main.cpp
+++ /dev/null
@@ -1,293 +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 <QCoreApplication>
-#include <QFile>
-#include <QTextStream>
-
-#include <QtQml/private/qqmljslexer_p.h>
-#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qqmljsengine_p.h>
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-#if QT_CONFIG(commandlineparser)
-#include <QCommandLineParser>
-#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("<formatted>:%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;
- 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..e26a6412c9
--- /dev/null
+++ b/tools/qmlformat/qmlformat.cpp
@@ -0,0 +1,390 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QTextStream>
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+
+#if QT_CONFIG(commandlineparser)
+# include <QCommandLineParser>
+#endif
+
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+
+
+using namespace QQmlJS::Dom;
+
+struct Options
+{
+ bool verbose = false;
+ bool inplace = false;
+ bool force = false;
+ bool tabs = false;
+ bool valid = false;
+ bool normalize = false;
+ bool ignoreSettings = false;
+ bool writeDefaultSettings = false;
+ bool objectsSpacing = false;
+ bool functionsSpacing = false;
+
+ int indentWidth = 4;
+ bool indentWidthSet = false;
+ QString newline = "native";
+
+ QStringList files;
+ QStringList arguments;
+ QStringList errors;
+};
+
+// TODO refactor
+// Move out to the LineWriterOptions class / helper
+static LineWriterOptions composeLwOptions(const Options &options, QStringView code)
+{
+ 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...
+ 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 << ", using default";
+ }
+
+ if (options.normalize)
+ lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Normalize;
+ else
+ lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+
+ lwOptions.objectsSpacing = options.objectsSpacing;
+ lwOptions.functionsSpacing = options.functionsSpacing;
+ return lwOptions;
+}
+
+static void logParsingErrors(const DomItem &fileItem, const QString &filename)
+{
+ fileItem.iterateErrors(
+ [](const DomItem &, const ErrorMessage &msg) {
+ errorToQDebug(msg);
+ return true;
+ },
+ true);
+ qWarning().noquote() << "Failed to parse" << filename;
+}
+
+// TODO
+// refactor this workaround. ExternalOWningItem is not recognized as an owning type
+// in ownerAs.
+static std::shared_ptr<ExternalOwningItem> getFileItemOwner(const DomItem &fileItem)
+{
+ std::shared_ptr<ExternalOwningItem> filePtr = nullptr;
+ switch (fileItem.internalKind()) {
+ case DomType::JsFile:
+ filePtr = fileItem.ownerAs<JsFile>();
+ break;
+ default:
+ filePtr = fileItem.ownerAs<QmlFile>();
+ break;
+ }
+ return filePtr;
+}
+
+// TODO refactor
+// Introduce better encapsulation and separation of concerns and move to DOM API
+// returns a DomItem corresponding to the loaded file and bool indicating the validity of the file
+static std::pair<DomItem, bool> parse(const QString &filename)
+{
+ auto envPtr =
+ DomEnvironment::create(QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ // placeholder for a node
+ // containing metadata (ExternalItemInfo) about the loaded file
+ DomItem fMetadataItem;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filename),
+ // callback called when everything is loaded that receives the
+ // loaded external file pair (path, oldValue, newValue)
+ [&fMetadataItem](Path, const DomItem &, const DomItem &extItemInfo) {
+ fMetadataItem = extItemInfo;
+ });
+ auto fItem = fMetadataItem.fileObject();
+ auto filePtr = getFileItemOwner(fItem);
+ return { fItem, filePtr && filePtr->isValid() };
+}
+
+static bool parseFile(const QString &filename, const Options &options)
+{
+ const auto [fileItem, validFile] = parse(filename);
+ if (!validFile) {
+ logParsingErrors(fileItem, filename);
+ return false;
+ }
+
+ // Turn AST back into source code
+ if (options.verbose)
+ qWarning().noquote() << "Dumping" << filename;
+
+ const auto &code = getFileItemOwner(fileItem)->code();
+ auto lwOptions = composeLwOptions(options, code);
+ WriteOutChecks checks = WriteOutCheck::Default;
+ //Disable writeOutChecks for some usecases
+ if (options.force ||
+ code.size() > 32000 ||
+ fileItem.internalKind() == DomType::JsFile) {
+ checks = WriteOutCheck::None;
+ }
+
+ bool res = false;
+ if (options.inplace) {
+ if (options.verbose)
+ qWarning().noquote() << "Writing to file" << filename;
+ FileWriter fw;
+ const unsigned numberOfBackupFiles = 0;
+ res = fileItem.writeOut(filename, numberOfBackupFiles, lwOptions, &fw, checks);
+ } else {
+ QFile out;
+ if (out.open(stdout, QIODevice::WriteOnly)) {
+ LineWriter lw([&out](QStringView s) { out.write(s.toUtf8()); }, filename, lwOptions);
+ OutWriter ow(lw);
+ res = fileItem.writeOutForFile(ow, checks);
+ ow.flush();
+ } else {
+ res = false;
+ }
+ }
+ return 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.")));
+
+ QCommandLineOption writeDefaultsOption(
+ QStringList() << "write-defaults",
+ QLatin1String("Writes defaults settings to .qmlformat.ini and exits (Warning: This "
+ "will overwrite any existing settings and comments!)"));
+ parser.addOption(writeDefaultsOption);
+
+ QCommandLineOption ignoreSettings(QStringList() << "ignore-settings",
+ QLatin1String("Ignores all settings files and only takes "
+ "command line options into consideration"));
+ parser.addOption(ignoreSettings);
+
+ 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.addOption(QCommandLineOption(QStringList() << "objects-spacing", QStringLiteral("Ensure spaces between objects (only works with normalize option).")));
+
+ parser.addOption(QCommandLineOption(QStringList() << "functions-spacing", QStringLiteral("Ensure spaces between functions (only works with normalize option).")));
+
+ parser.addPositionalArgument("filenames", "files to be processed by qmlformat");
+
+ parser.process(app);
+
+ if (parser.isSet(writeDefaultsOption)) {
+ Options options;
+ options.writeDefaultSettings = true;
+ options.valid = true;
+ return options;
+ }
+
+ 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"));
+ if (file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ 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.ignoreSettings = parser.isSet("ignore-settings");
+ options.objectsSpacing = parser.isSet("objects-spacing");
+ options.functionsSpacing = parser.isSet("functions-spacing");
+ 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(QT_VERSION_STR);
+
+ QQmlToolingSettings settings(QLatin1String("qmlformat"));
+
+ const QString &useTabsSetting = QStringLiteral("UseTabs");
+ settings.addOption(useTabsSetting);
+
+ const QString &indentWidthSetting = QStringLiteral("IndentWidth");
+ settings.addOption(indentWidthSetting, 4);
+
+ const QString &normalizeSetting = QStringLiteral("NormalizeOrder");
+ settings.addOption(normalizeSetting);
+
+ const QString &newlineSetting = QStringLiteral("NewlineType");
+ settings.addOption(newlineSetting, QStringLiteral("native"));
+
+ const QString &objectsSpacingSetting = QStringLiteral("ObjectsSpacing");
+ settings.addOption(objectsSpacingSetting);
+
+ const QString &functionsSpacingSetting = QStringLiteral("FunctionsSpacing");
+ settings.addOption(functionsSpacingSetting);
+
+ const auto options = buildCommandLineOptions(app);
+ if (!options.valid) {
+ for (const auto &error : options.errors) {
+ qWarning().noquote() << error;
+ }
+
+ return -1;
+ }
+
+ if (options.writeDefaultSettings)
+ return settings.writeDefaults() ? 0 : -1;
+
+ auto getSettings = [&](const QString &file, Options options) {
+ // Perform formatting inplace if --files option is set.
+ if (!options.files.isEmpty())
+ options.inplace = true;
+
+ if (options.ignoreSettings || !settings.search(file))
+ return options;
+
+ Options perFileOptions = options;
+
+ // Allow for tab settings to be overwritten by the command line
+ if (!options.indentWidthSet) {
+ if (settings.isSet(indentWidthSetting))
+ perFileOptions.indentWidth = settings.value(indentWidthSetting).toInt();
+ if (settings.isSet(useTabsSetting))
+ perFileOptions.tabs = settings.value(useTabsSetting).toBool();
+ }
+
+ if (settings.isSet(normalizeSetting))
+ perFileOptions.normalize = settings.value(normalizeSetting).toBool();
+
+ if (settings.isSet(newlineSetting))
+ perFileOptions.newline = settings.value(newlineSetting).toString();
+
+ if (settings.isSet(objectsSpacingSetting))
+ perFileOptions.objectsSpacing = settings.value(objectsSpacingSetting).toBool();
+
+ if (settings.isSet(functionsSpacingSetting))
+ perFileOptions.functionsSpacing = settings.value(functionsSpacingSetting).toBool();
+
+ return perFileOptions;
+ };
+
+ 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, getSettings(file, options)))
+ success = false;
+ }
+ } else {
+ for (const QString &file : options.arguments) {
+ if (!parseFile(file, getSettings(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 45957230d8..0000000000
--- a/tools/qmlformat/restructureastvisitor.cpp
+++ /dev/null
@@ -1,194 +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 <QList>
-
-RestructureAstVisitor::RestructureAstVisitor(Node *rootNode)
-{
- rootNode->accept(this);
-}
-
-template<typename T>
-static QList<T *> findKind(UiObjectMemberList *list)
-{
- QList<T *> members;
- for (auto *item = list; item != nullptr; item = item->next) {
- if (cast<T *>(item->member) != nullptr)
- members.append(cast<T *>(item->member));
- }
-
- return members;
-}
-
-template<typename T>
-static QList<T *> findKind(UiHeaderItemList *list)
-{
- QList<T *> members;
- for (auto *item = list; item != nullptr; item = item->next) {
- if (cast<T *>(item->headerItem) != nullptr)
- members.append(cast<T *>(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<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);
- auto publicMembers = findKind<UiPublicMember>(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 (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 7b3573300f..0000000000
--- a/tools/qmlformat/restructureastvisitor.h
+++ /dev/null
@@ -1,47 +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 <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-using namespace QQmlJS::AST;
-
-class RestructureAstVisitor : protected Visitor
-{
-public:
- RestructureAstVisitor(Node *rootNode);
-
- void throwRecursionDepthError() override {}
-
- void endVisit(UiObjectMemberList *node) override;
-};
-
-#endif // RESTRUCTUREASTVISITOR_H