aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSemih Yavuz <[email protected]>2025-08-11 22:28:47 +0200
committerSemih Yavuz <[email protected]>2025-08-14 19:54:37 +0200
commite3891d74590bcb9c0a81ff603ddc92ddaa7e7fc8 (patch)
tree416ef9c7e59b0a5ba4dbd488a1818c172ad22c48
parent141993c0a5b2a2b6ea244c6bee2ca3141b1a020a (diff)
qmltoolings add --dry-run option for qmlformat and qmllint
Add reportConfigForFiles to generic tooling class. It will perform the search for the given files. Use stdout to report. qmllint had already an option --dry-run for printing the fixed codes, but we can still allow this to use that option name. For qmlls, report the path if verbose option is set. Fixes: QTBUG-137874 Change-Id: I6bd43866073b3df832b6fd89d477bced869d74c0 Reviewed-by: Ulf Hermann <[email protected]>
-rw-r--r--src/qmlformat/qqmlformatoptions.cpp10
-rw-r--r--src/qmlformat/qqmlformatoptions_p.h3
-rw-r--r--src/qmlls/qmllsmain.cpp7
-rw-r--r--src/qmlls/qqmlcodemodel.cpp7
-rw-r--r--src/qmlls/qqmlcodemodel_p.h4
-rw-r--r--src/qmlls/qqmlcodemodelmanager.cpp7
-rw-r--r--src/qmlls/qqmlcodemodelmanager_p.h3
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings.cpp53
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings_p.h16
-rw-r--r--tests/auto/qml/qmltoolingsettings/tst_qmltoolingsettings.cpp39
-rw-r--r--tools/qmlformat/qmlformat.cpp5
-rw-r--r--tools/qmllint/main.cpp6
12 files changed, 146 insertions, 14 deletions
diff --git a/src/qmlformat/qqmlformatoptions.cpp b/src/qmlformat/qqmlformatoptions.cpp
index 76b919fee4..281f5997df 100644
--- a/src/qmlformat/qqmlformatoptions.cpp
+++ b/src/qmlformat/qqmlformatoptions.cpp
@@ -188,6 +188,13 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
"rule"_L1, "always"_L1);
parser.addOption(semicolonRuleOption);
+ QCommandLineOption dryrunOption(
+ QStringList() << "dry-run"_L1,
+ QStringLiteral("Prints the settings file that would be used for this instance."
+ "This is useful to see what settings would be used "
+ "without actually performing anything."));
+ parser.addOption(dryrunOption);
+
parser.addPositionalArgument("filenames"_L1, "files to be processed by qmlformat"_L1);
parser.process(args);
@@ -250,6 +257,7 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
}
}
+ options.setDryRun(parser.isSet(dryrunOption));
options.setIsVerbose(parser.isSet("verbose"_L1));
options.setIsInplace(parser.isSet("inplace"_L1));
options.setForceEnabled(parser.isSet("force"_L1));
@@ -322,7 +330,7 @@ QQmlFormatOptions QQmlFormatOptions::optionsForFile(const QString &fileName,
if (hasFiles)
perFileOptions.setIsInplace(true);
- if (!ignoreSettingsEnabled() && settings->search(fileName).isValid())
+ if (!ignoreSettingsEnabled() && settings->search(fileName, { m_verbose }).isValid())
perFileOptions.applySettings(*settings);
return perFileOptions;
diff --git a/src/qmlformat/qqmlformatoptions_p.h b/src/qmlformat/qqmlformatoptions_p.h
index 011ad4f9fa..ba265ff3ce 100644
--- a/src/qmlformat/qqmlformatoptions_p.h
+++ b/src/qmlformat/qqmlformatoptions_p.h
@@ -126,6 +126,8 @@ public:
bool indentWidthSet() const { return m_indentWidthSet; }
void setIndentWidthSet(bool newIndentWidthSet) { m_indentWidthSet = newIndentWidthSet; }
+ bool dryRun() const { return m_dryRun; }
+ void setDryRun(bool newDryRun) { m_dryRun = newDryRun; }
QStringList errors() const { return m_errors; }
void addError(const QString &newError) { m_errors.append(newError); };
@@ -171,6 +173,7 @@ private:
bool m_writeDefaultSettings = false;
bool m_indentWidthSet = false;
std::bitset<SettingsCount> m_settingBits;
+ bool m_dryRun = false;
};
QT_END_NAMESPACE
diff --git a/src/qmlls/qmllsmain.cpp b/src/qmlls/qmllsmain.cpp
index bb6d86b075..9cf5ebc9fa 100644
--- a/src/qmlls/qmllsmain.cpp
+++ b/src/qmlls/qmllsmain.cpp
@@ -301,6 +301,7 @@ int qmllsMain(int argv, char *argc[])
if (parser.isSet(writeDefaultsOption)) {
return settings.writeDefaults() ? 0 : 1;
}
+
if (parser.isSet(logFileOption)) {
QString fileName = parser.value(logFileOption);
qInfo() << "will log to" << fileName;
@@ -319,8 +320,6 @@ int qmllsMain(int argv, char *argc[])
logFile->flush();
});
}
- if (parser.isSet(verboseOption))
- QLoggingCategory::setFilterRules("qt.languageserver*.debug=true\n"_L1);
if (parser.isSet(waitOption)) {
int waitSeconds = parser.value(waitOption).toInt();
if (waitSeconds > 0)
@@ -337,6 +336,10 @@ int qmllsMain(int argv, char *argc[])
},
(parser.isSet(ignoreSettings) ? nullptr : &settings));
+ if (parser.isSet(verboseOption)) {
+ QLoggingCategory::setFilterRules("qt.languageserver*.debug=true\n"_L1);
+ qmlServer.codeModelManager()->setVerbose(true);
+ }
if (parser.isSet(docDir))
qmlServer.codeModelManager()->setDocumentationRootPath(
QString::fromUtf8(parser.value(docDir).toUtf8()));
diff --git a/src/qmlls/qqmlcodemodel.cpp b/src/qmlls/qqmlcodemodel.cpp
index 76b7aead36..66e53ec5c5 100644
--- a/src/qmlls/qqmlcodemodel.cpp
+++ b/src/qmlls/qqmlcodemodel.cpp
@@ -277,7 +277,7 @@ void QQmlCodeModel::initializeCMakeStatus(const QString &pathForSettings)
{
if (m_settings) {
const QString cmakeCalls = u"no-cmake-calls"_s;
- m_settings->search(pathForSettings);
+ m_settings->search(pathForSettings, { m_verbose });
if (m_settings->isSet(cmakeCalls) && m_settings->value(cmakeCalls).toBool()) {
qWarning() << "Disabling CMake calls via .qmlls.ini setting.";
m_cmakeStatus = DoesNotHaveCMake;
@@ -553,7 +553,8 @@ QStringList QQmlCodeModel::importPathsForUrl(const QByteArray &url)
QStringList result = importPaths();
const QString importPaths = u"importPaths"_s;
- if (m_settings && m_settings->search(fileName).isValid() && m_settings->isSet(importPaths)) {
+ if (m_settings && m_settings->search(fileName, { m_verbose }).isValid()
+ && m_settings->isSet(importPaths)) {
result.append(m_settings->value(importPaths).toString().split(QDir::listSeparator()));
}
@@ -628,7 +629,7 @@ QStringList QQmlCodeModel::buildPathsForFileUrl(const QByteArray &url)
// look in the settings.
// This is the one that is passed via the .qmlls.ini file.
if (buildPaths.isEmpty() && m_settings) {
- m_settings->search(path);
+ m_settings->search(path, { m_verbose });
QString buildDir = QStringLiteral(u"buildDir");
if (m_settings->isSet(buildDir))
buildPaths += m_settings->value(buildDir).toString().split(QDir::listSeparator(),
diff --git a/src/qmlls/qqmlcodemodel_p.h b/src/qmlls/qqmlcodemodel_p.h
index 1df8201f16..3f05286d69 100644
--- a/src/qmlls/qqmlcodemodel_p.h
+++ b/src/qmlls/qqmlcodemodel_p.h
@@ -135,6 +135,9 @@ public:
QSet<QString> ignoreForWatching() const { return m_ignoreForWatching; }
HelpManager *helpManager() { return &m_helpManager; }
+ void setVerbose(bool verbose) { m_verbose = verbose; }
+ bool verbose() const { return m_verbose; }
+
Q_SIGNALS:
void updatedSnapshot(const QByteArray &url);
void documentationRootPathChanged(const QString &path);
@@ -174,6 +177,7 @@ private:
QString m_documentationRootPath;
QSet<QString> m_ignoreForWatching;
HelpManager m_helpManager;
+ bool m_verbose = false;
private slots:
void onCppFileChanged(const QString &);
};
diff --git a/src/qmlls/qqmlcodemodelmanager.cpp b/src/qmlls/qqmlcodemodelmanager.cpp
index 23a7d49d68..0e48ffd9eb 100644
--- a/src/qmlls/qqmlcodemodelmanager.cpp
+++ b/src/qmlls/qqmlcodemodelmanager.cpp
@@ -242,6 +242,13 @@ void QQmlCodeModelManager::setDocumentationRootPath(const QString &path)
ws.codeModel->setDocumentationRootPath(path);
}
+void QQmlCodeModelManager::setVerbose(bool verbose)
+{
+ m_verbose = verbose;
+ for (const auto &ws : m_workspaces)
+ ws.codeModel->setVerbose(verbose);
+}
+
void QQmlCodeModelManager::setBuildPathsForRootUrl(const QByteArray &url, const QStringList &paths)
{
m_buildInformation.loadSettingsFrom(paths);
diff --git a/src/qmlls/qqmlcodemodelmanager_p.h b/src/qmlls/qqmlcodemodelmanager_p.h
index 3ad14ae233..06ac1f7a4e 100644
--- a/src/qmlls/qqmlcodemodelmanager_p.h
+++ b/src/qmlls/qqmlcodemodelmanager_p.h
@@ -71,6 +71,8 @@ public:
void setDocumentationRootPath(const QString &path);
HelpManager *helpManagerForUrl(const QByteArray &);
+ void setVerbose(bool verbose);
+
protected:
using Workspaces = std::vector<QQmlWorkspace>;
using WorkspaceIterator = Workspaces::const_iterator;
@@ -96,6 +98,7 @@ protected:
QStringList m_defaultImportPaths;
bool m_defaultDisableCMakeCalls = false;
QString m_defaultDocumentationRootPath;
+ bool m_verbose = false;
Q_SIGNALS:
void updatedSnapshot(const QByteArray &url);
diff --git a/src/qmltoolingsettings/qqmltoolingsettings.cpp b/src/qmltoolingsettings/qqmltoolingsettings.cpp
index 9d2f33d1f1..081d351c26 100644
--- a/src/qmltoolingsettings/qqmltoolingsettings.cpp
+++ b/src/qmltoolingsettings/qqmltoolingsettings.cpp
@@ -7,6 +7,7 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qset.h>
+#include <QtCore/qtextstream.h>
#if QT_CONFIG(settings)
#include <QtCore/qsettings.h>
#endif
@@ -161,8 +162,13 @@ QQmlToolingSettings::SearchResult QQmlToolingSettings::Searcher::search(const QS
return SearchResult();
}
-QQmlToolingSettings::SearchResult QQmlToolingSettings::search(const QString &path)
+QQmlToolingSettings::SearchResult QQmlToolingSettings::search(const QString &path, SearchOptions options)
{
+ const auto maybeReport = qScopeGuard([&]() {
+ if (options.verbose)
+ reportConfigForFiles({ path });
+ });
+
if (const SearchResult result = m_searcher.search(path); result.isValid())
return read(result.iniFilePath);
@@ -184,3 +190,48 @@ bool QQmlToolingSettings::isSet(const QString &name) const
// Unset is encoded as an empty string
return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
}
+
+bool QQmlToolingSettings::reportConfigForFiles(const QStringList &files)
+{
+ constexpr int maxAllowedFileLength = 255;
+ constexpr int minAllowedFileLength = 40;
+ bool headerPrinted = false;
+ auto lengthForFile = [maxAllowedFileLength](const QString &file) {
+ return std::min(int(file.length()), maxAllowedFileLength);
+ };
+
+ int maxFileLength =
+ std::accumulate(files.begin(), files.end(), 0, [&](int acc, const QString &file) {
+ return std::max(acc, lengthForFile(file));
+ });
+
+ if (maxFileLength < minAllowedFileLength)
+ maxFileLength = minAllowedFileLength;
+
+ for (const auto &file : files) {
+ if (file.isEmpty()) {
+ qWarning().noquote() << "Error: Could not find file" << file;
+ return false;
+ }
+
+ QString displayFile = file;
+ if (displayFile.length() > maxAllowedFileLength) {
+ displayFile = "..." + displayFile.right(maxAllowedFileLength - 3);
+ }
+
+ const auto result = search(file);
+
+ if (!headerPrinted) {
+ QString header =
+ QStringLiteral("%1 | %2").arg("File", -maxFileLength).arg("Settings File");
+ qWarning().noquote() << header;
+ qWarning().noquote() << QString(header.length(), u'-');
+ headerPrinted = true;
+ }
+ QString line =
+ QStringLiteral("%1 | %2").arg(displayFile, -maxFileLength).arg(result.iniFilePath);
+ qWarning().noquote() << line;
+ }
+
+ return true;
+}
diff --git a/src/qmltoolingsettings/qqmltoolingsettings_p.h b/src/qmltoolingsettings/qqmltoolingsettings_p.h
index c2142e1790..035b7d0cb6 100644
--- a/src/qmltoolingsettings/qqmltoolingsettings_p.h
+++ b/src/qmltoolingsettings/qqmltoolingsettings_p.h
@@ -27,6 +27,11 @@ QT_BEGIN_NAMESPACE
class QQmlToolingSettings
{
public:
+ QQmlToolingSettings(const QString &toolName);
+ struct SearchOptions
+ {
+ bool verbose;
+ };
struct SearchResult
{
enum class ResultType { Found, NotFound };
@@ -56,16 +61,15 @@ public:
QHash<QString, QString> m_seenDirectories;
};
- QQmlToolingSettings(const QString &toolName);
-
void addOption(const QString &name, const QVariant defaultValue = QVariant());
-
+ SearchResult search(const QString &path, SearchOptions options = {});
bool writeDefaults() const;
- SearchResult search(const QString &path);
QVariant value(const QString &name) const;
bool isSet(const QString &name) const;
+ bool reportConfigForFiles(const QStringList &files);
+
private:
QString m_currentSettingsPath;
QVariantHash m_values;
@@ -91,10 +95,10 @@ public:
return QQmlToolingSettings::writeDefaults();
}
- SearchResult search(const QString &path)
+ SearchResult search(const QString &path, SearchOptions options = {})
{
QMutexLocker lock(&m_mutex);
- return QQmlToolingSettings::search(path);
+ return QQmlToolingSettings::search(path, options);
}
QVariant value(const QString &name) const
diff --git a/tests/auto/qml/qmltoolingsettings/tst_qmltoolingsettings.cpp b/tests/auto/qml/qmltoolingsettings/tst_qmltoolingsettings.cpp
index f7415aafea..cf5dcd231e 100644
--- a/tests/auto/qml/qmltoolingsettings/tst_qmltoolingsettings.cpp
+++ b/tests/auto/qml/qmltoolingsettings/tst_qmltoolingsettings.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QTest>
+#include <QtLogging>
#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -16,6 +17,9 @@ public:
private Q_SLOTS:
void searchConfig_data();
void searchConfig();
+
+ void reportConfigForFiles_data();
+ void reportConfigForFiles();
};
tst_qmltoolingsettings::tst_qmltoolingsettings() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
@@ -62,5 +66,40 @@ void tst_qmltoolingsettings::searchConfig()
QCOMPARE(actualResult.iniFilePath, expectedResult.iniFilePath);
}
+void tst_qmltoolingsettings::reportConfigForFiles_data()
+{
+ QTest::addColumn<QStringList>("files");
+ QTest::addColumn<QString>("expectedResultCapture"); // string captured from output
+
+ QStringList files = { testFile("B/B1/test.qml"), testFile("B/B2/test.qml") };
+ QTest::newRow("validFiles") << files << "B/B2/test.qml";
+}
+
+void tst_qmltoolingsettings::reportConfigForFiles()
+{
+ QFETCH(QStringList, files);
+ QFETCH(QString, expectedResultCapture);
+
+ static QString out;
+
+ QtMessageHandler handler([](QtMsgType type, const QMessageLogContext &, const QString &msg) {
+ if (type == QtWarningMsg) {
+ QTextStream stream(&out);
+ stream << msg << Qt::endl;
+ }
+ });
+
+ const auto oldMessageHandler = qInstallMessageHandler(handler);
+ const auto guard =
+ qScopeGuard([&oldMessageHandler]() { qInstallMessageHandler(oldMessageHandler); });
+
+ QQmlToolingSettings settings("qmlformat");
+ settings.reportConfigForFiles(files);
+
+ QVERIFY(out.contains("File"));
+ QVERIFY(out.contains("Settings File"));
+ QVERIFY(out.contains(expectedResultCapture));
+}
+
QTEST_MAIN(tst_qmltoolingsettings)
#include "tst_qmltoolingsettings.moc"
diff --git a/tools/qmlformat/qmlformat.cpp b/tools/qmlformat/qmlformat.cpp
index 6cdfa907c3..786783a933 100644
--- a/tools/qmlformat/qmlformat.cpp
+++ b/tools/qmlformat/qmlformat.cpp
@@ -136,6 +136,11 @@ int main(int argc, char *argv[])
return -1;
}
+ if (options.dryRun()) {
+ settings.reportConfigForFiles(options.arguments());
+ return 0;
+ }
+
if (options.writeDefaultSettingsEnabled())
return settings.writeDefaults() ? 0 : -1;
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index b05c60bec7..28f8e9e610 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -165,7 +165,8 @@ All warnings can be set to three levels:
QCommandLineOption dryRun(QStringList() << "dry-run",
QLatin1String("Only print out the contents of the file after fix "
- "suggestions without applying them"));
+ "suggestions without applying them. Also prints the "
+ "settings file that would be used for this instance."));
parser.addOption(dryRun);
QCommandLineOption listPluginsOption(QStringList() << "list-plugins",
@@ -333,6 +334,9 @@ All warnings can be set to three levels:
parser.showHelp(-1);
}
+ if (parser.isSet(dryRun))
+ settings.reportConfigForFiles(positionalArguments);
+
QJsonArray jsonFiles;
for (const QString &filename : positionalArguments) {