aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom/qqmldomexternalitems.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmldom/qqmldomexternalitems.cpp')
-rw-r--r--src/qmldom/qqmldomexternalitems.cpp629
1 files changed, 584 insertions, 45 deletions
diff --git a/src/qmldom/qqmldomexternalitems.cpp b/src/qmldom/qqmldomexternalitems.cpp
index 2d71cbc170..6f48aa19e3 100644
--- a/src/qmldom/qqmldomexternalitems.cpp
+++ b/src/qmldom/qqmldomexternalitems.cpp
@@ -1,43 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**/
-#include "qqmldomexternalitems_p.h"
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmldomtop_p.h"
+#include "qqmldomoutwriter_p.h"
+#include "qqmldomcomments_p.h"
+#include "qqmldommock_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldom_utils_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -47,21 +16,24 @@
#include <QtCore/QDir>
#include <QtCore/QScopeGuard>
#include <QtCore/QFileInfo>
+#include <QtCore/QRegularExpressionMatch>
#include <algorithm>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace QQmlJS {
namespace Dom {
-ExternalOwningItem::ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path path, int derivedFrom):
- OwningItem(derivedFrom, lastDataUpdateAt), m_canonicalFilePath(filePath), m_path(path)
-{}
-
-ExternalOwningItem::ExternalOwningItem(const ExternalOwningItem &o):
- OwningItem(o), m_canonicalFilePath(o.m_canonicalFilePath),
- m_path(o.m_path), m_isValid(o.m_isValid)
+ExternalOwningItem::ExternalOwningItem(
+ const QString &filePath, const QDateTime &lastDataUpdateAt, const Path &path,
+ int derivedFrom, const QString &code)
+ : OwningItem(derivedFrom, lastDataUpdateAt),
+ m_canonicalFilePath(filePath),
+ m_code(code),
+ m_path(path)
{}
QString ExternalOwningItem::canonicalFilePath(const DomItem &) const
@@ -84,6 +56,573 @@ Path ExternalOwningItem::canonicalPath() const
return m_path;
}
+ErrorGroups QmldirFile::myParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Qmldir"),
+ NewErrorGroup("Parsing") } };
+ return res;
+}
+
+std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(const QString &path, const QString &code)
+{
+ QString canonicalFilePath = QFileInfo(path).canonicalFilePath();
+
+ QDateTime dataUpdate = QDateTime::currentDateTimeUtc();
+ auto res = std::make_shared<QmldirFile>(canonicalFilePath, code, dataUpdate);
+
+ if (canonicalFilePath.isEmpty() && !path.isEmpty())
+ res->addErrorLocal(
+ myParsingErrors().error(tr("QmldirFile started from invalid path '%1'").arg(path)));
+ res->parse();
+ return res;
+}
+
+void QmldirFile::parse()
+{
+ if (canonicalFilePath().isEmpty()) {
+ addErrorLocal(myParsingErrors().error(tr("canonicalFilePath is empty")));
+ setIsValid(false);
+ } else {
+ m_qmldir.parse(m_code);
+ setFromQmldir();
+ }
+}
+
+void QmldirFile::setFromQmldir()
+{
+ m_uri = QmlUri::fromUriString(m_qmldir.typeNamespace());
+ if (m_uri.isValid())
+ m_uri = QmlUri::fromDirectoryString(canonicalFilePath());
+ Path exportsPath = Path::Field(Fields::exports);
+ QDir baseDir = QFileInfo(canonicalFilePath()).dir();
+ int majorVersion = Version::Undefined;
+ bool ok;
+ int vNr = QFileInfo(baseDir.dirName()).suffix().toInt(&ok);
+ if (ok && vNr > 0) // accept 0?
+ majorVersion = vNr;
+ Path exportSource = canonicalPath();
+ for (auto const &el : m_qmldir.components()) {
+ QString exportFilePath = baseDir.filePath(el.fileName);
+ QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath();
+ if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
+ // created where we expect it)
+ canonicalExportFilePath = exportFilePath;
+ Export exp;
+ exp.exportSourcePath = exportSource;
+ exp.isSingleton = el.singleton;
+ exp.isInternal = el.internal;
+ exp.version =
+ Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion),
+ el.version.hasMinorVersion() ? el.version.minorVersion() : 0);
+ exp.typeName = el.typeName;
+ exp.typePath = Paths::qmlFileObjectPath(canonicalExportFilePath);
+ exp.uri = uri().toString();
+ m_exports.insert(exp.typeName, exp);
+ if (exp.version.majorVersion > 0)
+ m_majorVersions.insert(exp.version.majorVersion);
+ }
+ for (auto const &el : m_qmldir.scripts()) {
+ QString exportFilePath = baseDir.filePath(el.fileName);
+ QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath();
+ if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
+ // created where we expect it)
+ canonicalExportFilePath = exportFilePath;
+ Export exp;
+ exp.exportSourcePath = exportSource;
+ exp.isSingleton = true;
+ exp.isInternal = false;
+ exp.version =
+ Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion),
+ el.version.hasMinorVersion() ? el.version.minorVersion() : 0);
+ exp.typePath = Paths::jsFilePath(canonicalExportFilePath).field(Fields::rootComponent);
+ exp.uri = uri().toString();
+ exp.typeName = el.nameSpace;
+ m_exports.insert(exp.typeName, exp);
+ if (exp.version.majorVersion > 0)
+ m_majorVersions.insert(exp.version.majorVersion);
+ }
+ for (QQmlDirParser::Import const &imp : m_qmldir.imports()) {
+ QString uri = imp.module;
+ bool isAutoImport = imp.flags & QQmlDirParser::Import::Auto;
+ Version v;
+ if (isAutoImport)
+ v = Version(majorVersion, int(Version::Latest));
+ else {
+ v = Version((imp.version.hasMajorVersion() ? imp.version.majorVersion()
+ : int(Version::Latest)),
+ (imp.version.hasMinorVersion() ? imp.version.minorVersion()
+ : int(Version::Latest)));
+ }
+ m_imports.append(Import(QmlUri::fromUriString(uri), v));
+ m_autoExports.append(
+ ModuleAutoExport { Import(QmlUri::fromUriString(uri), v), isAutoImport });
+ }
+ for (QQmlDirParser::Import const &imp : m_qmldir.dependencies()) {
+ QString uri = imp.module;
+ if (imp.flags & QQmlDirParser::Import::Auto) {
+ qCDebug(QQmlJSDomImporting) << "QmldirFile::setFromQmlDir: ignoring initial version"
+ " 'auto' in depends command, using latest version"
+ " instead.";
+ }
+ Version v = Version(
+ (imp.version.hasMajorVersion() ? imp.version.majorVersion() : int(Version::Latest)),
+ (imp.version.hasMinorVersion() ? imp.version.minorVersion()
+ : int(Version::Latest)));
+ m_imports.append(Import(QmlUri::fromUriString(uri), v));
+ }
+ bool hasInvalidTypeinfo = false;
+ for (auto const &el : m_qmldir.typeInfos()) {
+ QString elStr = el;
+ QFileInfo elPath(elStr);
+ if (elPath.isRelative())
+ elPath = QFileInfo(baseDir.filePath(elStr));
+ QString typeInfoPath = elPath.canonicalFilePath();
+ if (typeInfoPath.isEmpty()) {
+ hasInvalidTypeinfo = true;
+ typeInfoPath = elPath.absoluteFilePath();
+ }
+ m_qmltypesFilePaths.append(Paths::qmltypesFilePath(typeInfoPath));
+ }
+ if (m_qmltypesFilePaths.isEmpty() || hasInvalidTypeinfo) {
+ // add all type info files in the directory...
+ for (QFileInfo const &entry :
+ baseDir.entryInfoList(QStringList({ QLatin1String("*.qmltypes") }),
+ QDir::Filter::Readable | QDir::Filter::Files)) {
+ Path p = Paths::qmltypesFilePath(entry.canonicalFilePath());
+ if (!m_qmltypesFilePaths.contains(p))
+ m_qmltypesFilePaths.append(p);
+ }
+ }
+ bool hasErrors = false;
+ for (auto const &el : m_qmldir.errors(uri().toString())) {
+ ErrorMessage msg = myParsingErrors().errorMessage(el);
+ if (msg.level == ErrorLevel::Error || msg.level == ErrorLevel::Fatal)
+ hasErrors = true;
+ addErrorLocal(std::move(msg));
+ }
+ setIsValid(!hasErrors); // consider it valid also with errors?
+ m_plugins = m_qmldir.plugins();
+}
+
+QList<ModuleAutoExport> QmldirFile::autoExports() const
+{
+ return m_autoExports;
+}
+
+void QmldirFile::setAutoExports(const QList<ModuleAutoExport> &autoExport)
+{
+ m_autoExports = autoExport;
+}
+
+void QmldirFile::ensureInModuleIndex(const DomItem &self, const QString &uri) const
+{
+ // ModuleIndex keeps the various sources of types from a given module uri import
+ // this method ensures that all major versions that are contained in this qmldir
+ // file actually have a ModuleIndex. This is required so that when importing the
+ // latest version the correct "lastest major version" is found, for example for
+ // qml only modules (qmltypes files also register their versions)
+ DomItem env = self.environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
+ for (int majorV : m_majorVersions) {
+ auto mIndex = envPtr->moduleIndexWithUri(env, uri, majorV, EnvLookup::Normal,
+ Changeable::Writable);
+ }
+ }
+}
+
+QCborValue pluginData(const QQmlDirParser::Plugin &pl, const QStringList &cNames)
+{
+ QCborArray names;
+ for (const QString &n : cNames)
+ names.append(n);
+ return QCborMap({ { QCborValue(QStringView(Fields::name)), pl.name },
+ { QStringView(Fields::path), pl.path },
+ { QStringView(Fields::classNames), names } });
+}
+
+bool QmldirFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvValueField(visitor, Fields::uri, uri().toString());
+ cont = cont && self.dvValueField(visitor, Fields::designerSupported, designerSupported());
+ cont = cont && self.dvReferencesField(visitor, Fields::qmltypesFiles, m_qmltypesFilePaths);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
+ cont = cont && self.dvItemField(visitor, Fields::plugins, [this, &self]() {
+ QStringList cNames = classNames();
+ return self.subListItem(List::fromQListRef<QQmlDirParser::Plugin>(
+ self.pathFromOwner().field(Fields::plugins), m_plugins,
+ [cNames](const DomItem &list, const PathEls::PathComponent &p,
+ const QQmlDirParser::Plugin &plugin) {
+ return list.subDataItem(p, pluginData(plugin, cNames));
+ }));
+ });
+ // add qmlfiles as map because this way they are presented the same way as
+ // the qmlfiles in a directory
+ cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() {
+ const QMap<QString, QString> typeFileMap = qmlFiles();
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::qmlFiles),
+ [typeFileMap](const DomItem &map, const QString &typeV) {
+ QString path = typeFileMap.value(typeV);
+ if (path.isEmpty())
+ return DomItem();
+ else
+ return map.subReferencesItem(
+ PathEls::Key(typeV),
+ QList<Path>({ Paths::qmlFileObjectPath(path) }));
+ },
+ [typeFileMap](const DomItem &) {
+ return QSet<QString>(typeFileMap.keyBegin(), typeFileMap.keyEnd());
+ },
+ QStringLiteral(u"QList<Reference>")));
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::autoExports, m_autoExports);
+ return cont;
+}
+
+QMap<QString, QString> QmldirFile::qmlFiles() const
+{
+ // add qmlfiles as map because this way they are presented the same way as
+ // the qmlfiles in a directory which gives them as fileName->list of references to files
+ // this is done only to ensure that they are loaded as dependencies
+ QMap<QString, QString> res;
+ for (const auto &e : m_exports)
+ res.insert(e.typeName + QStringLiteral(u"-") + e.version.stringValue(),
+ e.typePath[2].headName());
+ return res;
+}
+
+JsFile::JsFile(
+ const QString &filePath, const QString &code, const QDateTime &lastDataUpdateAt,
+ int derivedFrom)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(filePath), derivedFrom,
+ code)
+{
+ m_engine = std::make_shared<QQmlJS::Engine>();
+ LegacyDirectivesCollector directivesCollector(*this);
+ m_engine->setDirectives(&directivesCollector);
+
+ QQmlJS::Lexer lexer(m_engine.get());
+ lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/false);
+ QQmlJS::Parser parser(m_engine.get());
+
+ bool isESM = filePath.endsWith(u".mjs", Qt::CaseInsensitive);
+ bool isValid = isESM ? parser.parseModule() : parser.parseProgram();
+ setIsValid(isValid);
+
+ const auto diagnostics = parser.diagnosticMessages();
+ for (const DiagnosticMessage &msg : diagnostics) {
+ addErrorLocal(
+ std::move(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path)));
+ }
+
+ auto astComments = std::make_shared<AstComments>(m_engine);
+
+ CommentCollector collector;
+ collector.collectComments(m_engine, parser.rootNode(), astComments);
+ m_script = std::make_shared<ScriptExpression>(code, m_engine, parser.rootNode(), astComments,
+ isESM ? ScriptExpression::ExpressionType::ESMCode
+ : ScriptExpression::ExpressionType::JSCode);
+}
+
+ErrorGroups JsFile::myParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("JsFile"),
+ NewErrorGroup("Parsing") } };
+ return res;
+}
+
+bool JsFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::fileLocationsTree, m_fileLocationsTree);
+ if (m_script)
+ cont = cont && self.dvItemField(visitor, Fields::expression, [this, &self]() {
+ return self.subOwnerItem(PathEls::Field(Fields::expression), m_script);
+ });
+ return cont;
+}
+
+void JsFile::writeOut(const DomItem &self, OutWriter &ow) const
+{
+ writeOutDirectives(ow);
+ ow.ensureNewline(2);
+ if (DomItem script = self.field(Fields::expression)) {
+ ow.ensureNewline();
+ script.writeOut(ow);
+ }
+}
+
+void JsFile::addFileImport(const QString &jsfile, const QString &module)
+{
+ LegacyImport import;
+ import.fileName = jsfile;
+ import.asIdentifier = module;
+ m_imports.append(std::move(import));
+}
+
+void JsFile::addModuleImport(const QString &uri, const QString &version, const QString &module)
+{
+ LegacyImport import;
+ import.uri = uri;
+ import.version = version;
+ import.asIdentifier = module;
+ m_imports.append(std::move(import));
+}
+
+void JsFile::LegacyPragmaLibrary::writeOut(OutWriter &lw) const
+{
+ lw.write(u".pragma").space().write(u"library").ensureNewline();
+}
+
+void JsFile::LegacyImport::writeOut(OutWriter &lw) const
+{
+ // either filename or module uri must be present
+ Q_ASSERT(!fileName.isEmpty() || !uri.isEmpty());
+
+ lw.write(u".import").space();
+ if (!uri.isEmpty()) {
+ lw.write(uri).space();
+ if (!version.isEmpty()) {
+ lw.write(version).space();
+ }
+ } else {
+ lw.write(u"\"").write(fileName).write(u"\"").space();
+ }
+ lw.writeRegion(AsTokenRegion).space().write(asIdentifier);
+
+ lw.ensureNewline();
+}
+
+/*!
+ * \internal JsFile::writeOutDirectives
+ * \brief Performs writeOut of the .js Directives (.import, .pragma)
+ *
+ * Watch out!
+ * Currently directives in .js files do not have representative AST::Node-s (see QTBUG-119770),
+ * which makes it hard to preserve attached comments during the WriteOut process,
+ * because currently they are being attached to the first AST::Node.
+ * In case when the first AST::Node is absent, they are not collected, hence lost.
+ */
+void JsFile::writeOutDirectives(OutWriter &ow) const
+{
+ if (m_pragmaLibrary.has_value()) {
+ m_pragmaLibrary->writeOut(ow);
+ }
+ for (const auto &import : m_imports) {
+ import.writeOut(ow);
+ }
+}
+
+std::shared_ptr<OwningItem> QmlFile::doCopy(const DomItem &) const
+{
+ auto res = std::make_shared<QmlFile>(*this);
+ return res;
+}
+
+/*!
+ \class QmlFile
+
+ A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,
+ will be lazily constructed. That means that its member m_lazyMembers is uninitialized, and will
+ only be populated when it is accessed (through a getter, a setter or the DomItem interface).
+
+ The reason for the laziness is that the qqmljsscopes are created lazily and at the same time as
+ the Dom QmlFile representations. So instead of eagerly generating all qqmljsscopes when
+ constructing the Dom, the QmlFile itself becomes lazy and will only be populated on demand at
+ the same time as the corresponding qqmljsscopes.
+
+ The QDeferredFactory<QQmlJSScope> will, when the qqmljsscope is populated, take care of
+ populating all fields of the QmlFile.
+ Therefore, population of the QmlFile is done by populating the qqmljsscope.
+
+*/
+
+QmlFile::QmlFile(
+ const QString &filePath, const QString &code, const QDateTime &lastDataUpdateAt,
+ int derivedFrom, RecoveryOption option)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(filePath), derivedFrom,
+ code),
+ m_engine(new QQmlJS::Engine)
+{
+ QQmlJS::Lexer lexer(m_engine.get());
+ lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/true);
+ QQmlJS::Parser parser(m_engine.get());
+ if (option == EnableParserRecovery) {
+ parser.setIdentifierInsertionEnabled(true);
+ parser.setIncompleteBindingsEnabled(true);
+ }
+ m_isValid = parser.parse();
+ const auto diagnostics = parser.diagnosticMessages();
+ for (const DiagnosticMessage &msg : diagnostics) {
+ addErrorLocal(
+ std::move(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path)));
+ }
+ m_ast = parser.ast();
+}
+
+ErrorGroups QmlFile::myParsingErrors()
+{
+ static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("QmlFile"),
+ NewErrorGroup("Parsing") } };
+ return res;
+}
+
+bool QmlFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ auto &members = lazyMembers();
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::components, members.m_components);
+ cont = cont && self.dvWrapField(visitor, Fields::pragmas, members.m_pragmas);
+ cont = cont && self.dvWrapField(visitor, Fields::imports, members.m_imports);
+ cont = cont && self.dvWrapField(visitor, Fields::importScope, members.m_importScope);
+ cont = cont
+ && self.dvWrapField(visitor, Fields::fileLocationsTree, members.m_fileLocationsTree);
+ cont = cont && self.dvWrapField(visitor, Fields::comments, members.m_comments);
+ cont = cont && self.dvWrapField(visitor, Fields::astComments, members.m_astComments);
+ return cont;
+}
+
+DomItem QmlFile::field(const DomItem &self, QStringView name) const
+{
+ ensurePopulated();
+ if (name == Fields::components)
+ return self.wrapField(Fields::components, lazyMembers().m_components);
+ return DomBase::field(self, name);
+}
+
+void QmlFile::addError(const DomItem &self, ErrorMessage &&msg)
+{
+ self.containingObject().addError(std::move(msg));
+}
+
+void QmlFile::writeOut(const DomItem &self, OutWriter &ow) const
+{
+ ensurePopulated();
+ for (const DomItem &p : self.field(Fields::pragmas).values()) {
+ p.writeOut(ow);
+ }
+ for (auto i : self.field(Fields::imports).values()) {
+ i.writeOut(ow);
+ }
+ ow.ensureNewline(2);
+ DomItem mainC = self.field(Fields::components).key(QString()).index(0);
+ mainC.writeOut(ow);
+}
+
+std::shared_ptr<OwningItem> GlobalScope::doCopy(const DomItem &self) const
+{
+ auto res = std::make_shared<GlobalScope>(
+ canonicalFilePath(self), lastDataUpdateAt(), revision());
+ return res;
+}
+
+bool GlobalScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ return cont;
+}
+
+void QmltypesFile::ensureInModuleIndex(const DomItem &self) const
+{
+ auto it = m_uris.begin();
+ auto end = m_uris.end();
+ DomItem env = self.environment();
+ if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
+ while (it != end) {
+ QString uri = it.key();
+ for (int majorV : it.value()) {
+ auto mIndex = envPtr->moduleIndexWithUri(env, uri, majorV, EnvLookup::Normal,
+ Changeable::Writable);
+ mIndex->addQmltypeFilePath(self.canonicalPath());
+ }
+ ++it;
+ }
+ }
+}
+
+bool QmltypesFile::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvItemField(visitor, Fields::uris, [this, &self]() {
+ return self.subMapItem(Map::fromMapRef<QSet<int>>(
+ self.pathFromOwner().field(Fields::uris), m_uris,
+ [](const DomItem &map, const PathEls::PathComponent &p, const QSet<int> &el) {
+ QList<int> l(el.cbegin(), el.cend());
+ std::sort(l.begin(), l.end());
+ return map.subListItem(
+ List::fromQList<int>(map.pathFromOwner().appendComponent(p), l,
+ [](const DomItem &list, const PathEls::PathComponent &p,
+ int el) { return list.subDataItem(p, el); }));
+ }));
+ });
+ cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
+ return cont;
+}
+
+QmlDirectory::QmlDirectory(
+ const QString &filePath, const QStringList &dirList, const QDateTime &lastDataUpdateAt,
+ int derivedFrom)
+ : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlDirectoryPath(filePath), derivedFrom,
+ dirList.join(QLatin1Char('\n')))
+{
+ for (const QString &f : dirList) {
+ addQmlFilePath(f);
+ }
+}
+
+bool QmlDirectory::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
+ cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() -> DomItem {
+ QDir baseDir(canonicalFilePath());
+ return self.subMapItem(Map(
+ self.pathFromOwner().field(Fields::qmlFiles),
+ [this, baseDir](const DomItem &map, const QString &key) -> DomItem {
+ QList<Path> res;
+ auto it = m_qmlFiles.find(key);
+ while (it != m_qmlFiles.end() && it.key() == key) {
+ res.append(Paths::qmlFilePath(
+ QFileInfo(baseDir.filePath(it.value())).canonicalFilePath()));
+ ++it;
+ }
+ return map.subReferencesItem(PathEls::Key(key), res);
+ },
+ [this](const DomItem &) {
+ auto keys = m_qmlFiles.keys();
+ return QSet<QString>(keys.begin(), keys.end());
+ },
+ u"List<Reference>"_s));
+ });
+ return cont;
+}
+
+bool QmlDirectory::addQmlFilePath(const QString &relativePath)
+{
+ static const QRegularExpression qmlFileRegularExpression{
+ QRegularExpression::anchoredPattern(
+ uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation|ui\.qml))")
+ };
+ QRegularExpressionMatch m = qmlFileRegularExpression.match(relativePath);
+ if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
+ m_qmlFiles.insert(m.captured(u"compName"), relativePath);
+ Export e;
+ QDir dir(canonicalFilePath());
+ QFileInfo fInfo(dir.filePath(relativePath));
+ e.exportSourcePath = canonicalPath();
+ e.typeName = m.captured(u"compName");
+ e.typePath = Paths::qmlFileObjectPath(fInfo.canonicalFilePath());
+ e.uri = QLatin1String("file://") + canonicalFilePath();
+ m_exports.insert(e.typeName, e);
+ return true;
+ }
+ return false;
+}
+
} // end namespace Dom
} // end namespace QQmlJS