diff options
author | Yasser Grimes <[email protected]> | 2023-07-12 17:23:44 +0300 |
---|---|---|
committer | Yasser Grimes <[email protected]> | 2023-09-14 12:02:10 +0000 |
commit | 7295bc96ccf9548d0f07051979f8395fd57a3fd4 (patch) | |
tree | 19de09b027f9fd095bc15f8793f0e0d8e5783662 | |
parent | 0b08a9752ab0ef396d9ac5b16638dff6569c72c5 (diff) |
McuSupport: Make QmlProject c++ interfaces accecible to the CodeModel
QtMCUs relies on the QmlProject file to list c++ interfaces that are
available as components in Qml, roughly similar to qmlRegisterType.
Prior to this commit there was no possible methode for QtCreator to
know about thos c++ interfaces so whenever there are used "Uknown
Components" error is shown.
After building the project qmlprojectexporter through
qmlinterfacegenerator generate Qml files for each .h interface in the
"CMakeFiles/<target>.dir/*.qml" or ".../<target.dir>/<module>/*.qml"
To make those documents available we make use of CustomImportProvider
imports: for all importable qmlproject modules
loadBuiltins (new): for documents that should be available implicitly
for both the signatures is changed as all created documents require the
snapshot object (insert functioin) to bound their lifetime to the
snapshot as they are created as shared_ptrs.
duo to passing snapshot as copies between threads, storing those
documents locally will cause QtC to crash
Task-number: UL-6167
Change-Id: I09781f6d4b62b203944bf30ab322502d25263b56
Reviewed-by: Alessandro Portale <[email protected]>
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.h | 13 | ||||
-rw-r--r-- | src/libs/qmljs/qmljslink.cpp | 9 | ||||
-rw-r--r-- | src/plugins/mcusupport/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/mcusupport/mcusupport.qbs | 2 | ||||
-rw-r--r-- | src/plugins/mcusupport/mcusupportimportprovider.cpp | 185 | ||||
-rw-r--r-- | src/plugins/mcusupport/mcusupportimportprovider.h | 48 | ||||
-rw-r--r-- | src/plugins/mcusupport/mcusupportplugin.cpp | 2 |
7 files changed, 257 insertions, 3 deletions
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 8574e37febe..8ca9f4ec26c 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -3,10 +3,11 @@ #pragma once +#include "qmljsdocument.h" +#include <qmljs/parser/qmljsastfwd_p.h> #include <qmljs/qmljs_global.h> #include <qmljs/qmljsconstants.h> #include <qmljs/qmljsimportdependencies.h> -#include <qmljs/parser/qmljsastfwd_p.h> #include <languageutils/fakemetaobject.h> @@ -1110,12 +1111,20 @@ class QMLJS_EXPORT CustomImportsProvider : public QObject { Q_OBJECT public: + typedef QHash<const Document *, QSharedPointer<const Imports>> ImportsPerDocument; explicit CustomImportsProvider(QObject *parent = nullptr); virtual ~CustomImportsProvider(); static const QList<CustomImportsProvider *> allProviders(); - virtual QList<Import> imports(ValueOwner *valueOwner, const Document *context) const = 0; + virtual QList<Import> imports(ValueOwner *valueOwner, + const Document *context, + Snapshot *snapshot = nullptr) const = 0; + virtual void loadBuiltins([[maybe_unused]] ImportsPerDocument *importsPerDocument, + [[maybe_unused]] Imports *imports, + [[maybe_unused]] const Document *context, + [[maybe_unused]] ValueOwner *valueOwner, + [[maybe_unused]] Snapshot *snapshot) {} }; } // namespace QmlJS diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 7a34fbaa10d..b837037b7c8 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -209,9 +209,16 @@ Context::ImportsPerDocument LinkPrivate::linkImports() // Add custom imports for the opened document for (const auto &provider : CustomImportsProvider::allProviders()) { - const auto providerImports = provider->imports(m_valueOwner, document.data()); + const auto providerImports = provider->imports(m_valueOwner, + document.data(), + &m_snapshot); for (const auto &import : providerImports) importCache.insert(ImportCacheKey(import.info), import); + provider->loadBuiltins(&importsPerDocument, + imports, + document.data(), + m_valueOwner, + &m_snapshot); } populateImportedTypes(imports, document); diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index c5ce1bfa64c..f52c31b82ab 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -25,6 +25,7 @@ add_qtc_plugin(McuSupport mcuqmlprojectnode.cpp mcuqmlprojectnode.h mcubuildstep.cpp mcubuildstep.h dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h + mcusupportimportprovider.cpp mcusupportimportprovider.h ) add_subdirectory(test) diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index 752b6a3e6ff..96fea109d75 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -60,6 +60,8 @@ QtcPlugin { "settingshandler.cpp", "dialogs/mcukitcreationdialog.h", "dialogs/mcukitcreationdialog.cpp", + "mcusupportimportprovider.h", + "mcusupportimportprovider.cpp", ] QtcTestFiles { diff --git a/src/plugins/mcusupport/mcusupportimportprovider.cpp b/src/plugins/mcusupport/mcusupportimportprovider.cpp new file mode 100644 index 00000000000..b78baf8bd94 --- /dev/null +++ b/src/plugins/mcusupport/mcusupportimportprovider.cpp @@ -0,0 +1,185 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "mcusupportimportprovider.h" + +#include <cmakeprojectmanager/cmakeprojectconstants.h> +#include <languageutils/componentversion.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> +#include <qmljs/qmljsbind.h> +#include <qmljs/qmljsdocument.h> +#include <qmljs/qmljsinterpreter.h> +#include <qmljs/qmljsvalueowner.h> + +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonValue> +#include <QRegularExpression> + +#include <optional> + +static const char uriPattern[]{R"(uri\s*:\s*[\'\"](\w+)[\'\"])"}; + +namespace McuSupport::Internal { +using namespace ProjectExplorer; + +// Get the MCU target build folder for a file +static std::optional<FilePath> getTargetBuildFolder(const FilePath &path) +{ + const Project *project = ProjectExplorer::ProjectManager::projectForFile(path); + if (!project) + return std::nullopt; + auto node = project->nodeForFilePath(path); + if (!node) + return std::nullopt; + + // Get the cmake target node (CMakeTargetNode is internal) + // Go up in hierarchy until the first CMake target node + const ProjectNode *targetNode = nullptr; + FilePath projectBuildFolder; + while (node) { + targetNode = node->asProjectNode(); + if (!targetNode) { + node = node->parentProjectNode(); + continue; + } + projectBuildFolder = FilePath::fromVariant( + targetNode->data(CMakeProjectManager::Constants::BUILD_FOLDER_ROLE)); + if (!projectBuildFolder.isDir()) { + node = node->parentProjectNode(); + targetNode = nullptr; + continue; + } + break; + } + + if (!targetNode) + return std::nullopt; + + return projectBuildFolder / "CMakeFiles" / (project->displayName() + ".dir"); +}; + +QList<Import> McuSupportImportProvider::imports(ValueOwner *valueOwner, + const Document *context, + Snapshot *snapshot) const +{ + QList<Import> ret; + + const FilePath path = context->fileName(); + const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(path); + if (!cmakeFilesPathOpt) + return {}; + + const FilePath inputFilePath = *cmakeFilesPathOpt / "config/input.json"; + + const QJsonDocument doc = QJsonDocument::fromJson(inputFilePath.fileContents().value_or("")); + if (!doc.isObject()) + return {}; + const QJsonObject mainProjectObj = doc.object(); + for (const QJsonValue &moduleValue : mainProjectObj["modulesDependencies"].toArray()) { + if (!moduleValue.isObject()) + continue; + const FilePath modulePath = FilePath::fromUserInput( + moduleValue.toObject()["qmlProjectFile"].toString()); + const QString fileContent = QString::fromLatin1(modulePath.fileContents().value_or("")); + QRegularExpressionMatch uriMatch = QRegularExpression(uriPattern).match(fileContent); + if (!uriMatch.hasMatch()) + continue; + QString moduleUri = uriMatch.captured(1); + const FilePath moduleFolder = *cmakeFilesPathOpt / moduleUri; + + Import import; + import.valid = true; + import.object = new ObjectValue(valueOwner, moduleUri); + import.info = ImportInfo::moduleImport(moduleUri, {1, 0}, QString()); + for (const auto &qmlFilePath : moduleFolder.dirEntries(FileFilter({"*.qml"}, QDir::Files))) { + Document::MutablePtr doc = Document::create(qmlFilePath, Dialect::Qml); + doc->setSource(QString::fromLatin1(qmlFilePath.fileContents().value_or(""))); + doc->parseQml(); + snapshot->insert(doc, true); + import.object->setMember(doc->componentName(), doc->bind()->rootObjectValue()); + } + ret << import; + } + return ret; +}; + +void McuSupportImportProvider::loadBuiltins(ImportsPerDocument *importsPerDocument, + Imports *imports, + const Document *context, + ValueOwner *valueOwner, + Snapshot *snapshot) +{ + Import import; + import.valid = true; + import.object = new ObjectValue(valueOwner, "<qul>"); + import.info = ImportInfo::moduleImport("qul", {1, 0}, QString()); + getInterfacesImport(context->fileName(), importsPerDocument, import, valueOwner, snapshot); + imports->append(import); +}; + +void McuSupportImportProvider::getInterfacesImport(const FilePath &path, + ImportsPerDocument *importsPerDocument, + Import &import, + ValueOwner *valueOwner, + Snapshot *snapshot) const +{ + const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(path); + if (!cmakeFilesPathOpt) + return; + + const FilePath inputFilePath = *cmakeFilesPathOpt / "config/input.json"; + + std::optional<FilePath> fileModule = getFileModule(path, inputFilePath); + FilePath lookupDir = *cmakeFilesPathOpt + / (fileModule && !fileModule->isEmpty() + ? QRegularExpression(uriPattern) + .match(QString::fromLatin1( + fileModule->fileContents().value_or(""))) + .captured(1) + : ""); + + for (const auto &qmlFilePath : lookupDir.dirEntries(FileFilter({"*.qml"}, QDir::Files))) { + Document::MutablePtr doc = Document::create(qmlFilePath, Dialect::Qml); + doc->setSource(QString::fromLatin1(qmlFilePath.fileContents().value_or(""))); + doc->parseQml(); + snapshot->insert(doc, true); + import.object->setMember(doc->componentName(), doc->bind()->rootObjectValue()); + importsPerDocument->insert(doc.data(), QSharedPointer<Imports>(new Imports(valueOwner))); + } +} + +std::optional<FilePath> McuSupportImportProvider::getFileModule(const FilePath &file, + const FilePath &inputFile) const +{ + const auto doc = QJsonDocument::fromJson(inputFile.fileContents().value_or("")); + if (!doc.isObject()) + return {}; + + const QJsonObject mainProjectObject = doc.object(); + + // Mapping module objects starting with mainProject + const QJsonArray mainProjectQmlFiles = mainProjectObject["QmlFiles"].toArray(); + if (std::any_of(mainProjectQmlFiles.constBegin(), + mainProjectQmlFiles.constEnd(), + [&file](const QJsonValue &value) { + return FilePath::fromUserInput(value.toString()) == file; + })) + return std::nullopt; + + for (const QJsonValue &module : mainProjectObject["modulesDependencies"].toArray()) { + const QJsonObject moduleObject = module.toObject(); + const QJsonArray moduleQmlFiles = moduleObject["QmlFiles"].toArray(); + if (std::any_of(moduleQmlFiles.constBegin(), + moduleQmlFiles.constEnd(), + [&file](const QJsonValue &value) { + return FilePath::fromUserInput(value.toString()) == file; + })) + return FilePath::fromUserInput(moduleObject["qmlProjectFile"].toString()); + } + return std::nullopt; +} +}; // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcusupportimportprovider.h b/src/plugins/mcusupport/mcusupportimportprovider.h new file mode 100644 index 00000000000..829fa711505 --- /dev/null +++ b/src/plugins/mcusupport/mcusupportimportprovider.h @@ -0,0 +1,48 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "mcusupport_global.h" +#include "mcusupportplugin.h" + +#include <utils/filepath.h> +#include <qmljs/qmljsdocument.h> +#include <qmljs/qmljsinterpreter.h> + +namespace McuSupport::Internal { +using namespace QmlJS; +using namespace Utils; +class McuSupportImportProvider : public CustomImportsProvider +{ +public: + McuSupportImportProvider() {} + ~McuSupportImportProvider() {} + + // Overridden functions + virtual QList<Import> imports(ValueOwner *valueOwner, + const Document *context, + Snapshot *snapshot) const override; + virtual void loadBuiltins(ImportsPerDocument *importsPerDocument, + Imports *imports, + const Document *context, + ValueOwner *valueOwner, + Snapshot *snapshot) override; + + // Add to the interfaces needed for a document + // path: opened qml document + // importsPerDocument: imports available in the document (considered imported) + // import: qul import containing builtin types (interfaces) + // valueOwner: imports members owner + // snapshot: where qul documenents live + void getInterfacesImport(const FilePath &path, + ImportsPerDocument *importsPerDocument, + Import &import, + ValueOwner *valueOwner, + Snapshot *snapshot) const; + + // Get the qmlproject module which a qmlfile is part of + // nullopt means the file is part of the main project + std::optional<FilePath> getFileModule(const FilePath &file, const FilePath &inputFile) const; +}; +}; // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index f531b93a2aa..a9f8ff0d499 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -8,6 +8,7 @@ #include "mcuqmlprojectnode.h" #include "mcusupportconstants.h" #include "mcusupportdevice.h" +#include "mcusupportimportprovider.h" #include "mcusupportoptions.h" #include "mcusupportoptionspage.h" #include "mcusupportrunconfiguration.h" @@ -102,6 +103,7 @@ public: McuSupportOptions m_options{m_settingsHandler}; McuSupportOptionsPage optionsPage{m_options, m_settingsHandler}; MCUBuildStepFactory mcuBuildStepFactory; + McuSupportImportProvider mcuImportProvider; }; // class McuSupportPluginPrivate static McuSupportPluginPrivate *dd{nullptr}; |