7#include <QtCore/qdebug.h>
8#include <QtCore/qdir.h>
9#include <QtCore/qfileinfo.h>
10#include <QtCore/qset.h>
11#include <QtCore/qtextstream.h>
12#if QT_CONFIG(settings)
13#include <QtCore/qsettings.h>
15#include <QtCore/qstandardpaths.h>
17using namespace Qt::StringLiterals;
19QQmlToolingSettings::SearchOptions::SearchOptions() =
default;
20QQmlToolingSettings::SearchOptions::SearchOptions(
21 const QString &settingFileName,
bool reportFoundSettingsFiles,
bool isQmllintSilent)
22 : settingsFileName(settingFileName), reportFoundSettingsFiles(reportFoundSettingsFiles),
23 isQmllintSilent(isQmllintSilent)
28void QQmlToolingSettings::addOption(
const QString &name,
const QVariant &defaultValue)
30 if (defaultValue.isValid()) {
31 m_values[name] = defaultValue;
35QQmlToolingSettings::QQmlToolingSettings(
const QString &toolName,
36 const QStringList &recognizedIniSections,
37 const QString &localSettingsFile,
38 const QString &globalSettingsFile)
39 : m_searcher(localSettingsFile.isEmpty() ? u".%1.ini"_s.arg(toolName) : localSettingsFile,
40 globalSettingsFile.isEmpty() ? u"%1.ini"_s.arg(toolName) : globalSettingsFile),
41 m_recognizedIniSections(recognizedIniSections)
45QQmlToolingSettings::SearchResult QQmlToolingSettings::read(
const QString &settingsFilePath,
46 SearchOptions options)
48#if QT_CONFIG(settings)
49 Q_ASSERT(QFileInfo::exists(settingsFilePath));
51 if (m_currentSettingsPath == settingsFilePath)
52 return { SearchResult::ResultType::Found, settingsFilePath };
54 QSettings settings(settingsFilePath, QSettings::IniFormat);
56 if (!options.isQmllintSilent && !m_recognizedIniSections.isEmpty()) {
57 const QStringList sections = settings.childGroups() << QLatin1String(
"General");
58 for (
const QString §ion : sections) {
59 if (!m_recognizedIniSections.contains(section)) {
61 <<
"Unrecognized section \"%1\" in %2\n"_L1.arg(section).arg(settingsFilePath)
62 +
"Recognized sections are: ["_L1
63 + m_recognizedIniSections.join(
", "_L1) + u']';
68 for (
const QString &key : settings.allKeys())
69 m_values[key] = settings.value(key).toString();
71 m_currentSettingsPath = settingsFilePath;
72 return { SearchResult::ResultType::Found, settingsFilePath };
74 Q_UNUSED(settingsFilePath);
75 return SearchResult();
79bool QQmlToolingSettings::writeDefaults()
const
81#if QT_CONFIG(settings)
82 const QString path = QFileInfo(m_searcher.localSettingsFile()).absoluteFilePath();
84 QSettings settings(path, QSettings::IniFormat);
85 for (
auto it = m_values.constBegin(); it != m_values.constEnd(); ++it) {
86 settings.setValue(it.key(), it.value().isNull() ? QString() : it.value());
91 if (settings.status() != QSettings::NoError) {
92 qWarning() <<
"Failed to write default settings to" << path
93 <<
"Error:" << settings.status();
97 qInfo() <<
"Wrote default settings to" << path;
104QQmlToolingSettings::SearchResult
105QQmlToolingSettings::Searcher::searchCurrentDirInCache(
const QString &dirPath)
107 const auto it = m_seenDirectories.constFind(dirPath);
108 return it != m_seenDirectories.constEnd()
109 ? SearchResult{ SearchResult::ResultType::Found, *it }
110 : SearchResult{ SearchResult::ResultType::NotFound, {} };
118 const QString iniFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, local);
119 if (!iniFile.isEmpty())
123 return QStandardPaths::locate(QStandardPaths::GenericConfigLocation, global);
126QQmlToolingSettings::SearchResult
127QQmlToolingSettings::Searcher::searchDefaultLocation(
const QSet<QString> *visitedDirs)
129 QString iniFile = findIniFile(m_localSettingsFile, m_globalSettingsFile);
132 for (
const QString &dir : *visitedDirs)
133 m_seenDirectories[dir] = iniFile;
135 const SearchResult::ResultType found = iniFile.isEmpty()
136 ? SearchResult::ResultType::NotFound
137 : SearchResult::ResultType::Found;
138 return SearchResult { found, std::move(iniFile) };
141QQmlToolingSettings::SearchResult
142QQmlToolingSettings::Searcher::searchDirectoryHierarchy(
143 QSet<QString> *visitedDirs,
const QString &path)
145 const QFileInfo fileInfo(path);
146 QDir dir(fileInfo.isDir() ? path : fileInfo.dir());
148 while (dir.exists() && dir.isReadable()) {
149 const QString dirPath = dir.absolutePath();
156 if (
const SearchResult result = searchCurrentDirInCache(dirPath); result.isValid())
159 visitedDirs->insert(dirPath);
163 if (QString ini = dir.absoluteFilePath(m_localSettingsFile); QFileInfo::exists(ini)) {
164 for (
const QString &visitedDir : std::as_const(*visitedDirs))
165 m_seenDirectories[visitedDir] = ini;
167 return { SearchResult::ResultType::Found, std::move(ini) };
174 return SearchResult();
177QQmlToolingSettings::SearchResult QQmlToolingSettings::Searcher::search(
const QString &path)
179 QSet<QString> visitedDirs;
182 if (
const SearchResult result = searchDirectoryHierarchy(&visitedDirs, path); result.isValid())
187 if (
const SearchResult result = searchDefaultLocation(&visitedDirs); result.isValid())
190 return SearchResult();
193QQmlToolingSettings::SearchResult QQmlToolingSettings::search(
194 const QString &path,
const SearchOptions &options)
196 const auto maybeReport = qScopeGuard([&]() {
197 if (options.reportFoundSettingsFiles)
198 reportConfigForFiles({ path });
202 if (!options.settingsFileName.isEmpty()) {
203 QFileInfo fileInfo(options.settingsFileName);
204 return fileInfo.exists() ? read(fileInfo.absoluteFilePath(), options) : SearchResult();
207 if (
const SearchResult result = m_searcher.search(path); result.isValid())
208 return read(result.iniFilePath, options);
210 return SearchResult();
213void QQmlToolingSettings::setValue(
const QString &name, QVariant value)
215 m_values[name] = value;
218QVariant QQmlToolingSettings::value(
const QString &name)
const
220 return m_values.value(name);
223QStringList QQmlToolingSettings::valueAsStringList(
const QString &name)
const
225 return value(name).toString().split(QDir::listSeparator());
228void QQmlToolingSettings::resolveRelativeImportPaths(
const QString &filePath, QStringList *paths)
231 const QDir fileDir = QFileInfo(filePath).absoluteDir();
232 for (
auto it = paths->begin(), end = paths->end(); it != end; ++it) {
233 if (QFileInfo(*it).isAbsolute())
235 *it = QDir::cleanPath(fileDir.filePath(*it));
239QStringList QQmlToolingSettings::valueAsAbsolutePathList(
const QString &name,
240 const QString &baseForRelativePaths)
const
242 QStringList paths = valueAsStringList(name);
243 resolveRelativeImportPaths(baseForRelativePaths, &paths);
247bool QQmlToolingSettings::isSet(
const QString &name)
const
249 if (!m_values.contains(name))
252 QVariant variant = m_values[name];
255 return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
258bool QQmlToolingSettings::reportConfigForFiles(
const QStringList &files)
260 constexpr int maxAllowedFileLength = 255;
261 constexpr int minAllowedFileLength = 40;
262 bool headerPrinted =
false;
263 auto lengthForFile = [maxAllowedFileLength](
const QString &file) {
264 return std::min(
int(file.length()), maxAllowedFileLength);
268 std::accumulate(files.begin(), files.end(), 0, [&](
int acc,
const QString &file) {
269 return std::max(acc, lengthForFile(file));
272 if (maxFileLength < minAllowedFileLength)
273 maxFileLength = minAllowedFileLength;
275 for (
const auto &file : files) {
276 if (file.isEmpty()) {
277 qWarning().noquote() <<
"Error: Could not find file" << file;
281 QString displayFile = file;
282 if (displayFile.length() > maxAllowedFileLength) {
283 displayFile = u"..." + displayFile.right(maxAllowedFileLength - 3);
286 const auto result = m_searcher.search(file);
288 if (!headerPrinted) {
290 QStringLiteral(
"%1 | %2").arg(
"File", -maxFileLength).arg(
"Settings File");
291 qWarning().noquote() << header;
292 qWarning().noquote() << QString(header.length(), u'-');
293 headerPrinted =
true;
296 QStringLiteral(
"%1 | %2").arg(displayFile, -maxFileLength).arg(result.iniFilePath);
297 qWarning().noquote() << line;