aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2020-09-02 12:34:29 +0200
committerMaximilian Goldstein <max.goldstein@qt.io>2020-11-24 15:05:13 +0100
commitdb0b7cfcb2923aa4f80ed4d2c2e4d2052d52f96a (patch)
tree477790e5ba9069123baaab4c7b63a5234fb05604
parentd2d8e90e9f218103d60737e1273ab5322834d9ec (diff)
qmlformat: Add indent options
Adds the ability to use tabs or any amount of spaces for indentation instead of the default of 4 spaces. [ChangeLog][QML][qmlformat] Added option to customize indentation. Fixes: QTBUG-86413 Change-Id: I3c370dda2d0069ef61202a2d862ce15bc513e55e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml151
-rw-r--r--tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml151
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp80
-rw-r--r--tools/qmlformat/dumpastvisitor.cpp11
-rw-r--r--tools/qmlformat/dumpastvisitor.h8
-rw-r--r--tools/qmlformat/main.cpp34
6 files changed, 384 insertions, 51 deletions
diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml
new file mode 100644
index 0000000000..20d2a03421
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml
@@ -0,0 +1,151 @@
+/* This file is licensed under the not a license license
+ 1. You may not comply
+ 2. Goodbye
+*/
+
+import A.B.B.A
+import A.LLOHA
+// Importing this is very important
+import QtQuick 5.15
+// Muddling the waters!
+import QtQuick.Models 3.14 as muddle
+import That
+import This // THIS IS VERY IMPORTANT!
+import X.Y
+import X.Z
+import Y
+// Importing that is important too
+import Z
+
+// This comment is related to Item
+Item {
+ // Orphan comment
+ // Another orphan
+ // More orphans
+
+ // This to id
+ // Also id. (line 2)
+ // This is the third id
+ // fourth id comment
+ id: foo
+
+ // This to enum
+ enum Foo {
+ A = 3, // This is A
+ B, // This is B
+ C = 4, // This is C
+ D // This is D
+ }
+
+ property bool some_bool: false
+ property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
+ property bool something_computed: function(x) {
+ // This is an orphan inside something_computed
+ // Are these getting duplicated?
+ // Another orphan inside something_computed
+
+ const PI = 3, DAYS_PER_YEAR = 365.25;
+ var x = 3 + 2;
+ x["bla"] = 50;
+ // This one to var few!
+ var few = new WhatEver();
+ x += Math.sin(3);
+ x--;
+ --x;
+ x++;
+ ++x;
+ for (var x = 0; x < 100; x++) {
+ x++;
+ console.log("Foo");
+ }
+ for (var x in [3, 2, 1]) {
+ y++;
+ console.log("Bar");
+ }
+ while (true)
+ console.log("Wee");
+
+ with (foo) {
+ bar;
+ x += 5;
+ } // This is related to with!
+ x3:
+ do {
+ console.log("Hello");
+ } while (3 == 0);
+ try {
+ dangerous();
+ } catch (e) {
+ console.log(e);
+ } finally {
+ dangerous();
+ }
+ switch (x) {
+ case 0:
+ x = 1;
+ break;
+ case 1:
+ x = 5;
+ break;
+ case 4:
+ x = 100;
+ break;
+ }
+ if (x == 50)
+ console.log("true");
+ else if (x == 50)
+ console.log("other thing");
+ else
+ console.log("false");
+ if (x == 50) {
+ console.log("true");
+ } else if (x == 50) {
+ console.log("other thing");
+ x--;
+ } else {
+ console.log("false");
+ }
+ return "foobar";
+ }()
+ default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool
+ // some_read_only_bool
+ readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0
+
+ signal say(string name, bool caps)
+
+ // This one to aFunc()
+ function aFunc() {
+ var x = 3;
+ return x;
+ }
+
+ x: 3 // Very cool
+ Component.onCompleted: console.log("Foo!")
+ myFavouriteThings: [
+ // This is an orphan
+
+ // This is a cool text
+ Text {
+ },
+ // This is a cool rectangle
+ Rectangle {
+ }
+ ]
+
+ Text {
+ required property string batman
+
+ signal boo(int count, int times, real duration)
+
+ text: "Bla"
+ }
+
+ // This comment is related to the property animation
+ PropertyAnimation on x {
+ id: foo
+
+ x: 3
+ y: x + 3
+ }
+
+}
diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml
new file mode 100644
index 0000000000..d569c414d2
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml
@@ -0,0 +1,151 @@
+/* This file is licensed under the not a license license
+ 1. You may not comply
+ 2. Goodbye
+*/
+
+import A.B.B.A
+import A.LLOHA
+// Importing this is very important
+import QtQuick 5.15
+// Muddling the waters!
+import QtQuick.Models 3.14 as muddle
+import That
+import This // THIS IS VERY IMPORTANT!
+import X.Y
+import X.Z
+import Y
+// Importing that is important too
+import Z
+
+// This comment is related to Item
+Item {
+ // Orphan comment
+ // Another orphan
+ // More orphans
+
+ // This to id
+ // Also id. (line 2)
+ // This is the third id
+ // fourth id comment
+ id: foo
+
+ // This to enum
+ enum Foo {
+ A = 3, // This is A
+ B, // This is B
+ C = 4, // This is C
+ D // This is D
+ }
+
+ property bool some_bool: false
+ property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
+ property bool something_computed: function(x) {
+ // This is an orphan inside something_computed
+ // Are these getting duplicated?
+ // Another orphan inside something_computed
+
+ const PI = 3, DAYS_PER_YEAR = 365.25;
+ var x = 3 + 2;
+ x["bla"] = 50;
+ // This one to var few!
+ var few = new WhatEver();
+ x += Math.sin(3);
+ x--;
+ --x;
+ x++;
+ ++x;
+ for (var x = 0; x < 100; x++) {
+ x++;
+ console.log("Foo");
+ }
+ for (var x in [3, 2, 1]) {
+ y++;
+ console.log("Bar");
+ }
+ while (true)
+ console.log("Wee");
+
+ with (foo) {
+ bar;
+ x += 5;
+ } // This is related to with!
+ x3:
+ do {
+ console.log("Hello");
+ } while (3 == 0);
+ try {
+ dangerous();
+ } catch (e) {
+ console.log(e);
+ } finally {
+ dangerous();
+ }
+ switch (x) {
+ case 0:
+ x = 1;
+ break;
+ case 1:
+ x = 5;
+ break;
+ case 4:
+ x = 100;
+ break;
+ }
+ if (x == 50)
+ console.log("true");
+ else if (x == 50)
+ console.log("other thing");
+ else
+ console.log("false");
+ if (x == 50) {
+ console.log("true");
+ } else if (x == 50) {
+ console.log("other thing");
+ x--;
+ } else {
+ console.log("false");
+ }
+ return "foobar";
+ }()
+ default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool
+ // some_read_only_bool
+ readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0
+
+ signal say(string name, bool caps)
+
+ // This one to aFunc()
+ function aFunc() {
+ var x = 3;
+ return x;
+ }
+
+ x: 3 // Very cool
+ Component.onCompleted: console.log("Foo!")
+ myFavouriteThings: [
+ // This is an orphan
+
+ // This is a cool text
+ Text {
+ },
+ // This is a cool rectangle
+ Rectangle {
+ }
+ ]
+
+ Text {
+ required property string batman
+
+ signal boo(int count, int times, real duration)
+
+ text: "Bla"
+ }
+
+ // This comment is related to the property animation
+ PropertyAnimation on x {
+ id: foo
+
+ x: 3
+ y: x + 3
+ }
+
+}
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 9e91ff8569..f114704b0a 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -53,7 +53,7 @@ private Q_SLOTS:
private:
QString readTestFile(const QString &path);
- QString runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed, const QString &newlineFormat = "native");
+ QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true);
QString m_qmlformatPath;
QStringList m_excludedDirs;
@@ -179,16 +179,18 @@ QString TestQmlformat::readTestFile(const QString &path)
void TestQmlformat::testLineEndings()
{
// macos
- const QString macosContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "macos");
+ const QString macosContents =
+ runQmlformat(testFile("Example1.formatted.qml"), { "-l", "macos" });
QVERIFY(!macosContents.contains("\n"));
QVERIFY(macosContents.contains("\r"));
// windows
- const QString windowsContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "windows");
+ const QString windowsContents =
+ runQmlformat(testFile("Example1.formatted.qml"), { "-l", "windows" });
QVERIFY(windowsContents.contains("\r\n"));
// unix
- const QString unixContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "unix");
+ const QString unixContents = runQmlformat(testFile("Example1.formatted.qml"), { "-l", "unix" });
QVERIFY(unixContents.contains("\n"));
QVERIFY(!unixContents.contains("\r"));
}
@@ -197,58 +199,63 @@ void TestQmlformat::testFormat_data()
{
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("fileFormatted");
- QTest::addColumn<bool>("sortImports");
- QTest::addColumn<bool>("shouldSucceed");
+ QTest::addColumn<QStringList>("args");
QTest::newRow("example1 (sorted)") << "Example1.qml"
- << "Example1.formatted.qml" << true << true;
- QTest::newRow("example1 (not sorted)") << "Example1.qml"
- << "Example1.formatted.nosort.qml" << false << true;
+ << "Example1.formatted.qml" << QStringList {};
+ QTest::newRow("example1 (not sorted)")
+ << "Example1.qml"
+ << "Example1.formatted.nosort.qml" << QStringList { "-n" };
+ QTest::newRow("example1 (tabs)") << "Example1.qml"
+ << "Example1.formatted.tabs.qml" << QStringList { "-t" };
+ QTest::newRow("example1 (two spaces)")
+ << "Example1.qml"
+ << "Example1.formatted.2spaces.qml" << QStringList { "-w", "2" };
QTest::newRow("annotation (sorted)") << "Annotations.qml"
- << "Annotations.formatted.qml" << true << true;
- QTest::newRow("annotation (not sorted)") << "Annotations.qml"
- << "Annotations.formatted.nosort.qml" << false << true;
+ << "Annotations.formatted.qml" << QStringList {};
+ QTest::newRow("annotation (not sorted)")
+ << "Annotations.qml"
+ << "Annotations.formatted.nosort.qml" << QStringList { "-n" };
QTest::newRow("front inline") << "FrontInline.qml"
- << "FrontInline.formatted.qml" << false << true;
+ << "FrontInline.formatted.qml" << QStringList {};
QTest::newRow("if blocks") << "IfBlocks.qml"
- << "IfBlocks.formatted.qml" << false << true;
+ << "IfBlocks.formatted.qml" << QStringList {};
QTest::newRow("read-only properties") << "readOnlyProps.qml"
- << "readOnlyProps.formatted.qml" << false << true;
+ << "readOnlyProps.formatted.qml" << QStringList {};
QTest::newRow("states and transitions")
<< "statesAndTransitions.qml"
- << "statesAndTransitions.formatted.qml" << false << true;
+ << "statesAndTransitions.formatted.qml" << QStringList {};
QTest::newRow("large bindings") << "largeBindings.qml"
- << "largeBindings.formatted.qml" << false << true;
+ << "largeBindings.formatted.qml" << QStringList {};
QTest::newRow("verbatim strings") << "verbatimString.qml"
- << "verbatimString.formatted.qml" << false << true;
+ << "verbatimString.formatted.qml" << QStringList {};
QTest::newRow("inline components") << "inlineComponents.qml"
- << "inlineComponents.formatted.qml" << false << true;
+ << "inlineComponents.formatted.qml" << QStringList {};
QTest::newRow("nested ifs") << "nestedIf.qml"
- << "nestedIf.formatted.qml" << false << true;
+ << "nestedIf.formatted.qml" << QStringList {};
QTest::newRow("QTBUG-85003") << "QtBug85003.qml"
- << "QtBug85003.formatted.qml" << false << true;
+ << "QtBug85003.formatted.qml" << QStringList {};
QTest::newRow("nested functions") << "nestedFunctions.qml"
- << "nestedFunctions.formatted.qml" << false << true;
+ << "nestedFunctions.formatted.qml" << QStringList {};
QTest::newRow("multiline comments") << "multilineComment.qml"
- << "multilineComment.formatted.qml" << false << true;
+ << "multilineComment.formatted.qml" << QStringList {};
QTest::newRow("for of") << "forOf.qml"
- << "forOf.formatted.qml" << false << true;
+ << "forOf.formatted.qml" << QStringList {};
QTest::newRow("property names") << "propertyNames.qml"
- << "propertyNames.formatted.qml" << false << true;
+ << "propertyNames.formatted.qml" << QStringList {};
QTest::newRow("empty object") << "emptyObject.qml"
- << "emptyObject.formatted.qml" << false << true;
+ << "emptyObject.formatted.qml" << QStringList {};
QTest::newRow("arrow functions") << "arrowFunctions.qml"
- << "arrowFunctions.formatted.qml" << false << true;
+ << "arrowFunctions.formatted.qml" << QStringList {};
}
void TestQmlformat::testFormat()
{
QFETCH(QString, file);
QFETCH(QString, fileFormatted);
- QFETCH(bool, sortImports);
- QFETCH(bool, shouldSucceed);
+ QFETCH(QStringList, args);
- QCOMPARE(runQmlformat(testFile(file), sortImports, shouldSucceed), readTestFile(fileFormatted));
+ QCOMPARE(runQmlformat(testFile(file), args), readTestFile(fileFormatted));
}
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
@@ -273,29 +280,24 @@ void TestQmlformat::testExample()
{
QFETCH(QString, file);
const bool isInvalid = isInvalidFile(QFileInfo(file));
- QString output = runQmlformat(file, true, !isInvalid);
+ QString output = runQmlformat(file, {}, !isInvalid);
if (!isInvalid)
QVERIFY(!output.isEmpty());
}
#endif
-QString TestQmlformat::runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed, const QString &newlineFormat)
+QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList args,
+ bool shouldSucceed)
{
// Copy test file to temporary location
QTemporaryDir tempDir;
const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml";
QFile::copy(fileToFormat, tempFile);
- QStringList args;
- args << "-i";
+ args << QLatin1String("-i");
args << tempFile;
- if (!sortImports)
- args << "-n";
-
- args << "-l" << newlineFormat;
-
auto verify = [&]() {
QProcess process;
process.start(m_qmlformatPath, args);
diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp
index 24286ba69b..771814ffd5 100644
--- a/tools/qmlformat/dumpastvisitor.cpp
+++ b/tools/qmlformat/dumpastvisitor.cpp
@@ -30,8 +30,9 @@
#include <QtQml/private/qqmljslexer_p.h>
-DumpAstVisitor::DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment)
- : m_engine(engine), m_comment(comment)
+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);
@@ -992,11 +993,9 @@ bool DumpAstVisitor::visit(UiPublicMember *node) {
return true;
}
-static QString generateIndent(int indentLevel)
+QString DumpAstVisitor::generateIndent(int indentLevel) const
{
- constexpr int IDENT_WIDTH = 4;
-
- return QString(IDENT_WIDTH * indentLevel, ' ');
+ return QString(m_indentWidth * indentLevel, m_indentation == Indentation::Tabs ? '\t' : ' ');
}
QString DumpAstVisitor::formatLine(QString line, bool newline) const
diff --git a/tools/qmlformat/dumpastvisitor.h b/tools/qmlformat/dumpastvisitor.h
index 729e15702a..657592f403 100644
--- a/tools/qmlformat/dumpastvisitor.h
+++ b/tools/qmlformat/dumpastvisitor.h
@@ -43,7 +43,10 @@ using namespace QQmlJS;
class DumpAstVisitor : protected Visitor
{
public:
- DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment);
+ enum Indentation { Tabs, Spaces };
+
+ DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment,
+ int indentWidth, Indentation indentation);
QString toString() const { return m_result; }
@@ -94,6 +97,7 @@ private:
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;
@@ -150,6 +154,8 @@ private:
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
index f9afd10a2c..a021971710 100644
--- a/tools/qmlformat/main.cpp
+++ b/tools/qmlformat/main.cpp
@@ -44,7 +44,8 @@
#include "dumpastvisitor.h"
#include "restructureastvisitor.h"
-bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImports, bool force, const QString& newline)
+bool parseFile(const QString &filename, bool inplace, bool verbose, bool sortImports, bool force,
+ int indentWidth, bool tabs, const QString &newline)
{
QFile file(filename);
@@ -100,7 +101,9 @@ bool parseFile(const QString& filename, bool inplace, bool verbose, bool sortImp
if (verbose)
qWarning().noquote() << "Dumping" << filename;
- DumpAstVisitor dump(&engine, parser.rootNode(), &comment);
+ DumpAstVisitor dump(&engine, parser.rootNode(), &comment, tabs ? 1 : indentWidth,
+ tabs ? DumpAstVisitor::Indentation::Tabs
+ : DumpAstVisitor::Indentation::Spaces);
QString dumpCode = dump.toString();
@@ -191,6 +194,13 @@ int main(int argc, char *argv[])
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"));
@@ -212,6 +222,19 @@ int main(int argc, char *argv[])
return -1;
}
+ if (parser.isSet("indent-width") && parser.isSet("tabs")) {
+ qWarning() << "Error: Cannot use --indent-width with --tabs";
+ return -1;
+ }
+
+ bool indentWidthOkay = false;
+ int indentWidth = parser.value("indent-width").toInt(&indentWidthOkay);
+
+ if (!indentWidthOkay) {
+ qWarning() << "Error: Invalid value passed to -w";
+ return -1;
+ }
+
if (parser.isSet("files")) {
if (!positionalArguments.isEmpty())
qWarning() << "Warning: Positional arguments are ignored when -F is used";
@@ -226,14 +249,15 @@ int main(int argc, char *argv[])
continue;
if (!parseFile(file, true, parser.isSet("verbose"), !parser.isSet("no-sort"),
- parser.isSet("force"), parser.value("newline")))
+ parser.isSet("force"), indentWidth, parser.isSet("tabs"),
+ parser.value("newline")))
success = false;
}
} else {
for (const QString &file : parser.positionalArguments()) {
if (!parseFile(file, parser.isSet("inplace"), parser.isSet("verbose"),
- !parser.isSet("no-sort"), parser.isSet("force"),
- parser.value("newline")))
+ !parser.isSet("no-sort"), parser.isSet("force"), indentWidth,
+ parser.isSet("tabs"), parser.value("newline")))
success = false;
}
}