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();
210QVariant QQmlToolingSettings::value(
const QString &name)
const
212 return m_values.value(name);
215QStringList QQmlToolingSettings::valueAsStringList(
const QString &name)
const
217 return value(name).toString().split(QDir::listSeparator());
220void QQmlToolingSettings::resolveRelativeImportPaths(
const QString &filePath, QStringList *paths)
223 const QDir fileDir = QFileInfo(filePath).absoluteDir();
224 for (
auto it = paths->begin(), end = paths->end(); it != end; ++it) {
225 if (QFileInfo(*it).isAbsolute())
227 *it = QDir::cleanPath(fileDir.filePath(*it));
231QStringList QQmlToolingSettings::valueAsAbsolutePathList(
const QString &name,
232 const QString &baseForRelativePaths)
const
234 QStringList paths = valueAsStringList(name);
235 resolveRelativeImportPaths(baseForRelativePaths, &paths);
239bool QQmlToolingSettings::isSet(
const QString &name)
const
241 if (!m_values.contains(name))
244 QVariant variant = m_values[name];
247 return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
250bool QQmlToolingSettings::reportConfigForFiles(
const QStringList &files)
252 constexpr int maxAllowedFileLength = 255;
253 constexpr int minAllowedFileLength = 40;
254 bool headerPrinted =
false;
255 auto lengthForFile = [maxAllowedFileLength](
const QString &file) {
256 return std::min(
int(file.length()), maxAllowedFileLength);
260 std::accumulate(files.begin(), files.end(), 0, [&](
int acc,
const QString &file) {
261 return std::max(acc, lengthForFile(file));
264 if (maxFileLength < minAllowedFileLength)
265 maxFileLength = minAllowedFileLength;
267 for (
const auto &file : files) {
268 if (file.isEmpty()) {
269 qWarning().noquote() <<
"Error: Could not find file" << file;
273 QString displayFile = file;
274 if (displayFile.length() > maxAllowedFileLength) {
275 displayFile = u"..." + displayFile.right(maxAllowedFileLength - 3);
278 const auto result = search(file);
280 if (!headerPrinted) {
282 QStringLiteral(
"%1 | %2").arg(
"File", -maxFileLength).arg(
"Settings File");
283 qWarning().noquote() << header;
284 qWarning().noquote() << QString(header.length(), u'-');
285 headerPrinted =
true;
288 QStringLiteral(
"%1 | %2").arg(displayFile, -maxFileLength).arg(result.iniFilePath);
289 qWarning().noquote() << line;