diff options
author | Semih Yavuz <[email protected]> | 2025-08-11 22:28:47 +0200 |
---|---|---|
committer | Semih Yavuz <[email protected]> | 2025-08-14 19:54:37 +0200 |
commit | e3891d74590bcb9c0a81ff603ddc92ddaa7e7fc8 (patch) | |
tree | 416ef9c7e59b0a5ba4dbd488a1818c172ad22c48 | |
parent | 141993c0a5b2a2b6ea244c6bee2ca3141b1a020a (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.cpp | 10 | ||||
-rw-r--r-- | src/qmlformat/qqmlformatoptions_p.h | 3 | ||||
-rw-r--r-- | src/qmlls/qmllsmain.cpp | 7 | ||||
-rw-r--r-- | src/qmlls/qqmlcodemodel.cpp | 7 | ||||
-rw-r--r-- | src/qmlls/qqmlcodemodel_p.h | 4 | ||||
-rw-r--r-- | src/qmlls/qqmlcodemodelmanager.cpp | 7 | ||||
-rw-r--r-- | src/qmlls/qqmlcodemodelmanager_p.h | 3 | ||||
-rw-r--r-- | src/qmltoolingsettings/qqmltoolingsettings.cpp | 53 | ||||
-rw-r--r-- | src/qmltoolingsettings/qqmltoolingsettings_p.h | 16 | ||||
-rw-r--r-- | tests/auto/qml/qmltoolingsettings/tst_qmltoolingsettings.cpp | 39 | ||||
-rw-r--r-- | tools/qmlformat/qmlformat.cpp | 5 | ||||
-rw-r--r-- | tools/qmllint/main.cpp | 6 |
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) { |