aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2021-10-25 18:30:26 +0200
committerMaximilian Goldstein <max.goldstein@qt.io>2021-10-26 12:03:40 +0200
commite14d13bea68a5bf40a5475e5065651f750d4af04 (patch)
tree360afa7ca59445a0e960df18d716ef9666a47a9c
parente2532e8773e0ee123d2b27e978026386b7ebc19d (diff)
qmlformat: Implement settings file
Implements controlling qmlformat via a settings file as can be done for qmllint. [ChangeLog][General][qmlformat] Adds the ability to set linting options via a settings file rather than using command line parameters. Use --write-defaults to generate a template with default values for editing. Use --ignore-settings to disable this feature. Fixes: QTBUG-86415 Change-Id: I282c3b994ca6cc491a27b45f531f1ba1c2652ef7 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc3
-rw-r--r--tests/auto/qml/qmlformat/data/settings/.qmlformat.ini5
-rw-r--r--tests/auto/qml/qmlformat/data/settings/Example1.formatted.qml157
-rw-r--r--tests/auto/qml/qmlformat/data/settings/Example1.qml105
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp2
-rw-r--r--tools/qmlformat/CMakeLists.txt2
-rw-r--r--tools/qmlformat/qmlformat.cpp67
7 files changed, 339 insertions, 2 deletions
diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
index 96d2163eda..266cbf064c 100644
--- a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
@@ -111,4 +111,7 @@ by specifying the \c{-n} flag.
By default, qmlformat writes the formatted version of the file to stdout.
If you wish to have your file updated in-place specify the \c{-i} flag.
+
+You may also change tab widths and line ending types among other settings,
+either via command line options or by using a settings file.
*/
diff --git a/tests/auto/qml/qmlformat/data/settings/.qmlformat.ini b/tests/auto/qml/qmlformat/data/settings/.qmlformat.ini
new file mode 100644
index 0000000000..ae1eed5254
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/settings/.qmlformat.ini
@@ -0,0 +1,5 @@
+[General]
+IndentWidth=2
+NewlineType=macos
+NormalizeOrder=true
+UseTabs=
diff --git a/tests/auto/qml/qmlformat/data/settings/Example1.formatted.qml b/tests/auto/qml/qmlformat/data/settings/Example1.formatted.qml
new file mode 100644
index 0000000000..a679e8a010
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/settings/Example1.formatted.qml
@@ -0,0 +1,157 @@
+/* This file is licensed under the not a license license
+ 1. You may not comply
+ 2. Goodbye
+*/
+
+// Importing this is very important
+import QtQuick 5.15
+// Muddling the waters!
+import QtQuick.Models 3.14 as muddle
+// Importing that is important too
+import Z
+import That
+import This // THIS IS VERY IMPORTANT!
+import Y
+import X.Z
+import X.Y
+import A.LLOHA
+import A.B.B.A
+
+// This comment is related to Item
+Item {
+
+ // This to id
+ // Also id. (line 2)
+ // This is the third id
+ // fourth id comment
+ id: foo
+ x: 3 // Very cool
+
+ // This to enum
+ enum Foo {
+ A = 3, // This is A
+ B, // This is B
+ C = 4, // This is C
+ D // This is D
+ }
+
+ // This one to aFunc()
+ function aFunc() {
+ var x = 3;
+ return x;
+ }
+
+ property bool some_bool: false
+ // This comment is related to the property animation
+ PropertyAnimation on x {
+ id: foo2
+ x: 3
+ y: x + 3
+ }
+
+ // Orphan comment
+
+ // Another orphan
+
+ // More orphans
+ property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3]
+
+ property bool something_computed: function (x) {
+ const PI = 3, DAYS_PER_YEAR = 365.25;
+ var x = 3 + 2;
+ x["bla"] = 50;
+
+ // This is an orphan inside something_computed
+
+ // Are these getting duplicated?
+
+ // 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 {
+ console.log("What else?");
+ }
+ 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");
+ }
+
+ // Another orphan inside something_computed
+ return "foobar";
+ }()
+
+ default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool
+ myFavouriteThings: [
+ // This is an orphan
+
+ // This is a cool text
+ Text {
+ },
+ // This is a cool rectangle
+ Rectangle {
+ }
+ ]
+
+ // 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)
+
+ Text {
+ text: "Bla"
+
+ signal boo(int count, int times, real duration)
+
+ required property string batman
+ }
+
+ Component.onCompleted: console.log("Foo!")
+}
diff --git a/tests/auto/qml/qmlformat/data/settings/Example1.qml b/tests/auto/qml/qmlformat/data/settings/Example1.qml
new file mode 100644
index 0000000000..36ffc6b058
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/settings/Example1.qml
@@ -0,0 +1,105 @@
+
+
+/* This file is licensed under the not a license license
+ 1. You may not comply
+ 2. Goodbye
+*/
+
+// Importing this is very important
+import QtQuick 5.15
+// Muddling the waters!
+import QtQuick.Models 3.14 as muddle
+// Importing that is important too
+import Z
+import That
+import This // THIS IS VERY IMPORTANT!
+import Y
+import X.Z
+import X.Y
+import A.LLOHA
+import A.B.B.A
+
+// This comment is related to Item
+Item {
+ x: 3 // Very cool
+
+ // This to enum
+ enum Foo {
+ A = 3, // This is A
+ B, // This is B
+ C = 4, // This is C
+ D // This is D
+ }
+
+ // This one to aFunc()
+ function aFunc() {
+ var x = 3;
+ return x;
+ }
+
+ property bool some_bool : false
+ // This comment is related to the property animation
+ PropertyAnimation on x {
+ id: foo2; x: 3; y: x + 3
+ }
+
+ // Orphan comment
+
+ // Another orphan
+
+ // More orphans
+
+
+ property variant some_array_literal: [30,20,Math["PI"],[4,3,2],"foo",0.3]
+ property bool something_computed: function(x) {
+ const PI = 3, DAYS_PER_YEAR=365.25; var x = 3 + 2; x["bla"] = 50;
+
+ // This is an orphan inside something_computed
+
+ // Are these getting duplicated?
+
+
+ // 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 { console.log("What else?"); }
+ 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"); }
+
+ // Another orphan inside something_computed
+
+ return "foobar"; }();
+
+ default property bool some_default_bool : 500 % 5 !== 0 // some_default_bool
+
+ myFavouriteThings: [
+ // This is an orphan
+
+ // This is a cool text
+ Text {},
+ // This is a cool rectangle
+ Rectangle {}]
+
+ // 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);
+
+ Text { text: "Bla"; signal boo(int count, int times, real duration); required property string batman; }
+
+ Component.onCompleted: console.log("Foo!");
+
+ // This to id
+ // Also id. (line 2)
+ // This is the third id
+ // fourth id comment
+ id: foo
+
+}
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 65b423b919..e018c4828a 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -252,6 +252,8 @@ void TestQmlformat::testFormat_data()
<< "emptyObject.formatted.qml" << QStringList {};
QTest::newRow("arrow functions") << "arrowFunctions.qml"
<< "arrowFunctions.formatted.qml" << QStringList {};
+ QTest::newRow("settings") << "settings/Example1.qml"
+ << "settings/Example1.formatted.qml" << QStringList {};
}
void TestQmlformat::testFormat()
diff --git a/tools/qmlformat/CMakeLists.txt b/tools/qmlformat/CMakeLists.txt
index 232aefcfbf..0ffe698cf0 100644
--- a/tools/qmlformat/CMakeLists.txt
+++ b/tools/qmlformat/CMakeLists.txt
@@ -10,6 +10,8 @@ qt_internal_add_tool(${target_name}
TOOLS_TARGET Qml # special case
SOURCES
qmlformat.cpp
+ ../shared/qqmltoolingsettings.h
+ ../shared/qqmltoolingsettings.cpp
PUBLIC_LIBRARIES
Qt::Core
Qt::QmlDomPrivate
diff --git a/tools/qmlformat/qmlformat.cpp b/tools/qmlformat/qmlformat.cpp
index dab1ee42c7..75120b3ab0 100644
--- a/tools/qmlformat/qmlformat.cpp
+++ b/tools/qmlformat/qmlformat.cpp
@@ -44,6 +44,8 @@
# include <QCommandLineParser>
#endif
+#include "../shared/qqmltoolingsettings.h"
+
using namespace QQmlJS::Dom;
struct Options
@@ -54,6 +56,8 @@ struct Options
bool tabs = false;
bool valid = false;
bool normalize = false;
+ bool ignoreSettings = false;
+ bool writeDefaultSettings = false;
int indentWidth = 4;
bool indentWidthSet = false;
@@ -168,6 +172,17 @@ Options buildCommandLineOptions(const QCoreApplication &app)
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.")));
@@ -198,6 +213,13 @@ Options buildCommandLineOptions(const QCoreApplication &app)
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) {
@@ -229,6 +251,7 @@ Options buildCommandLineOptions(const QCoreApplication &app)
options.force = parser.isSet("force");
options.tabs = parser.isSet("tabs");
options.normalize = parser.isSet("normalize");
+ options.ignoreSettings = parser.isSet("ignore-settings");
options.valid = true;
options.indentWidth = indentWidth;
@@ -248,6 +271,20 @@ int main(int argc, char *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 auto options = buildCommandLineOptions(app);
if (!options.valid) {
for (const auto &error : options.errors) {
@@ -257,6 +294,32 @@ int main(int argc, char *argv[])
return -1;
}
+ if (options.writeDefaultSettings)
+ return settings.writeDefaults() ? 0 : -1;
+
+ auto getSettings = [&](const QString &file, Options options) {
+ 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();
+
+ return perFileOptions;
+ };
+
bool success = true;
if (!options.files.isEmpty()) {
if (!options.arguments.isEmpty())
@@ -265,12 +328,12 @@ int main(int argc, char *argv[])
for (const QString &file : options.files) {
Q_ASSERT(!file.isEmpty());
- if (!parseFile(file, options))
+ if (!parseFile(file, getSettings(file, options)))
success = false;
}
} else {
for (const QString &file : options.arguments) {
- if (!parseFile(file, options))
+ if (!parseFile(file, getSettings(file, options)))
success = false;
}
}