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 : m_searcher(u".%1.ini"_s.arg(toolName), u"%1.ini"_s.arg(toolName)),
38 m_recognizedIniSections(recognizedIniSections)
42QQmlToolingSettings::SearchResult QQmlToolingSettings::read(
const QString &settingsFilePath,
43 SearchOptions options)
45#if QT_CONFIG(settings)
46 Q_ASSERT(QFileInfo::exists(settingsFilePath));
48 if (m_currentSettingsPath == settingsFilePath)
49 return { SearchResult::ResultType::Found, settingsFilePath };
51 QSettings settings(settingsFilePath, QSettings::IniFormat);
53 if (!options.isQmllintSilent) {
54 const QStringList sections = settings.childGroups() << QLatin1String(
"General");
55 for (
const QString §ion : sections) {
56 if (!m_recognizedIniSections.contains(section)) {
58 <<
"Unrecognized section \"%1\" in %2\n"_L1.arg(section).arg(settingsFilePath)
59 +
"Recognized sections are: ["_L1
60 + m_recognizedIniSections.join(
", "_L1) + u']';
65 for (
const QString &key : settings.allKeys())
66 m_values[key] = settings.value(key).toString();
68 m_currentSettingsPath = settingsFilePath;
69 return { SearchResult::ResultType::Found, settingsFilePath };
71 Q_UNUSED(settingsFilePath);
72 return SearchResult();
76bool QQmlToolingSettings::writeDefaults()
const
78#if QT_CONFIG(settings)
79 const QString path = QFileInfo(m_searcher.localSettingsFile()).absoluteFilePath();
81 QSettings settings(path, QSettings::IniFormat);
82 for (
auto it = m_values.constBegin(); it != m_values.constEnd(); ++it) {
83 settings.setValue(it.key(), it.value().isNull() ? QString() : it.value());
88 if (settings.status() != QSettings::NoError) {
89 qWarning() <<
"Failed to write default settings to" << path
90 <<
"Error:" << settings.status();
94 qInfo() <<
"Wrote default settings to" << path;
101QQmlToolingSettings::SearchResult
102QQmlToolingSettings::Searcher::searchCurrentDirInCache(
const QString &dirPath)
104 const auto it = m_seenDirectories.constFind(dirPath);
105 return it != m_seenDirectories.constEnd()
106 ? SearchResult{ SearchResult::ResultType::Found, *it }
107 : SearchResult{ SearchResult::ResultType::NotFound, {} };
115 const QString iniFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, local);
116 if (!iniFile.isEmpty())
120 return QStandardPaths::locate(QStandardPaths::GenericConfigLocation, global);
123QQmlToolingSettings::SearchResult
124QQmlToolingSettings::Searcher::searchDefaultLocation(
const QSet<QString> *visitedDirs)
126 QString iniFile = findIniFile(m_localSettingsFile, m_globalSettingsFile);
129 for (
const QString &dir : *visitedDirs)
130 m_seenDirectories[dir] = iniFile;
132 const SearchResult::ResultType found = iniFile.isEmpty()
133 ? SearchResult::ResultType::NotFound
134 : SearchResult::ResultType::Found;
135 return SearchResult { found, std::move(iniFile) };
138QQmlToolingSettings::SearchResult
139QQmlToolingSettings::Searcher::searchDirectoryHierarchy(
140 QSet<QString> *visitedDirs,
const QString &path)
142 const QFileInfo fileInfo(path);
143 QDir dir(fileInfo.isDir() ? path : fileInfo.dir());
145 while (dir.exists() && dir.isReadable()) {
146 const QString dirPath = dir.absolutePath();
153 if (
const SearchResult result = searchCurrentDirInCache(dirPath); result.isValid())
156 visitedDirs->insert(dirPath);
160 if (QString ini = dir.absoluteFilePath(m_localSettingsFile); QFileInfo::exists(ini)) {
161 for (
const QString &visitedDir : std::as_const(*visitedDirs))
162 m_seenDirectories[visitedDir] = ini;
164 return { SearchResult::ResultType::Found, std::move(ini) };
171 return SearchResult();
174QQmlToolingSettings::SearchResult QQmlToolingSettings::Searcher::search(
const QString &path)
176 QSet<QString> visitedDirs;
179 if (
const SearchResult result = searchDirectoryHierarchy(&visitedDirs, path); result.isValid())
184 if (
const SearchResult result = searchDefaultLocation(&visitedDirs); result.isValid())
187 return SearchResult();
190QQmlToolingSettings::SearchResult QQmlToolingSettings::search(
191 const QString &path,
const SearchOptions &options)
193 const auto maybeReport = qScopeGuard([&]() {
194 if (options.reportFoundSettingsFiles)
195 reportConfigForFiles({ path });
199 if (!options.settingsFileName.isEmpty()) {
200 QFileInfo fileInfo(options.settingsFileName);
201 return fileInfo.exists() ? read(fileInfo.absoluteFilePath(), options) : SearchResult();
204 if (
const SearchResult result = m_searcher.search(path); result.isValid())
205 return read(result.iniFilePath, options);
207 return SearchResult();
210void QQmlToolingSettings::setValue(
const QString &name, QVariant value)
212 m_values[name] = value;
215QVariant QQmlToolingSettings::value(
const QString &name)
const
217 return m_values.value(name);
220QStringList QQmlToolingSettings::valueAsStringList(
const QString &name)
const
222 return value(name).toString().split(QDir::listSeparator());
225void QQmlToolingSettings::resolveRelativeImportPaths(
const QString &filePath, QStringList *paths)
228 const QDir fileDir = QFileInfo(filePath).absoluteDir();
229 for (
auto it = paths->begin(), end = paths->end(); it != end; ++it) {
230 if (QFileInfo(*it).isAbsolute())
232 *it = QDir::cleanPath(fileDir.filePath(*it));
236QStringList QQmlToolingSettings::valueAsAbsolutePathList(
const QString &name,
237 const QString &baseForRelativePaths)
const
239 QStringList paths = valueAsStringList(name);
240 resolveRelativeImportPaths(baseForRelativePaths, &paths);
244bool QQmlToolingSettings::isSet(
const QString &name)
const
246 if (!m_values.contains(name))
249 QVariant variant = m_values[name];
252 return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
255bool QQmlToolingSettings::reportConfigForFiles(
const QStringList &files)
257 constexpr int maxAllowedFileLength = 255;
258 constexpr int minAllowedFileLength = 40;
259 bool headerPrinted =
false;
260 auto lengthForFile = [maxAllowedFileLength](
const QString &file) {
261 return std::min(
int(file.length()), maxAllowedFileLength);
265 std::accumulate(files.begin(), files.end(), 0, [&](
int acc,
const QString &file) {
266 return std::max(acc, lengthForFile(file));
269 if (maxFileLength < minAllowedFileLength)
270 maxFileLength = minAllowedFileLength;
272 for (
const auto &file : files) {
273 if (file.isEmpty()) {
274 qWarning().noquote() <<
"Error: Could not find file" << file;
278 QString displayFile = file;
279 if (displayFile.length() > maxAllowedFileLength) {
280 displayFile = u"..." + displayFile.right(maxAllowedFileLength - 3);
283 const auto result = m_searcher.search(file);
285 if (!headerPrinted) {
287 QStringLiteral(
"%1 | %2").arg(
"File", -maxFileLength).arg(
"Settings File");
288 qWarning().noquote() << header;
289 qWarning().noquote() << QString(header.length(), u'-');
290 headerPrinted =
true;
293 QStringLiteral(
"%1 | %2").arg(displayFile, -maxFileLength).arg(result.iniFilePath);
294 qWarning().noquote() << line;