forked from qt-creator/qt-creator
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 <alessandro.portale@qt.io>
This commit is contained in:
@@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "qmljsdocument.h"
|
||||||
|
#include <qmljs/parser/qmljsastfwd_p.h>
|
||||||
#include <qmljs/qmljs_global.h>
|
#include <qmljs/qmljs_global.h>
|
||||||
#include <qmljs/qmljsconstants.h>
|
#include <qmljs/qmljsconstants.h>
|
||||||
#include <qmljs/qmljsimportdependencies.h>
|
#include <qmljs/qmljsimportdependencies.h>
|
||||||
#include <qmljs/parser/qmljsastfwd_p.h>
|
|
||||||
|
|
||||||
#include <languageutils/fakemetaobject.h>
|
#include <languageutils/fakemetaobject.h>
|
||||||
|
|
||||||
@@ -1110,12 +1111,20 @@ class QMLJS_EXPORT CustomImportsProvider : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
typedef QHash<const Document *, QSharedPointer<const Imports>> ImportsPerDocument;
|
||||||
explicit CustomImportsProvider(QObject *parent = nullptr);
|
explicit CustomImportsProvider(QObject *parent = nullptr);
|
||||||
virtual ~CustomImportsProvider();
|
virtual ~CustomImportsProvider();
|
||||||
|
|
||||||
static const QList<CustomImportsProvider *> allProviders();
|
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
|
} // namespace QmlJS
|
||||||
|
@@ -209,9 +209,16 @@ Context::ImportsPerDocument LinkPrivate::linkImports()
|
|||||||
|
|
||||||
// Add custom imports for the opened document
|
// Add custom imports for the opened document
|
||||||
for (const auto &provider : CustomImportsProvider::allProviders()) {
|
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)
|
for (const auto &import : providerImports)
|
||||||
importCache.insert(ImportCacheKey(import.info), import);
|
importCache.insert(ImportCacheKey(import.info), import);
|
||||||
|
provider->loadBuiltins(&importsPerDocument,
|
||||||
|
imports,
|
||||||
|
document.data(),
|
||||||
|
m_valueOwner,
|
||||||
|
&m_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
populateImportedTypes(imports, document);
|
populateImportedTypes(imports, document);
|
||||||
|
@@ -25,6 +25,7 @@ add_qtc_plugin(McuSupport
|
|||||||
mcuqmlprojectnode.cpp mcuqmlprojectnode.h
|
mcuqmlprojectnode.cpp mcuqmlprojectnode.h
|
||||||
mcubuildstep.cpp mcubuildstep.h
|
mcubuildstep.cpp mcubuildstep.h
|
||||||
dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h
|
dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h
|
||||||
|
mcusupportimportprovider.cpp mcusupportimportprovider.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
@@ -60,6 +60,8 @@ QtcPlugin {
|
|||||||
"settingshandler.cpp",
|
"settingshandler.cpp",
|
||||||
"dialogs/mcukitcreationdialog.h",
|
"dialogs/mcukitcreationdialog.h",
|
||||||
"dialogs/mcukitcreationdialog.cpp",
|
"dialogs/mcukitcreationdialog.cpp",
|
||||||
|
"mcusupportimportprovider.h",
|
||||||
|
"mcusupportimportprovider.cpp",
|
||||||
]
|
]
|
||||||
|
|
||||||
QtcTestFiles {
|
QtcTestFiles {
|
||||||
|
185
src/plugins/mcusupport/mcusupportimportprovider.cpp
Normal file
185
src/plugins/mcusupport/mcusupportimportprovider.cpp
Normal file
@@ -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
|
48
src/plugins/mcusupport/mcusupportimportprovider.h
Normal file
48
src/plugins/mcusupport/mcusupportimportprovider.h
Normal file
@@ -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
|
@@ -8,6 +8,7 @@
|
|||||||
#include "mcuqmlprojectnode.h"
|
#include "mcuqmlprojectnode.h"
|
||||||
#include "mcusupportconstants.h"
|
#include "mcusupportconstants.h"
|
||||||
#include "mcusupportdevice.h"
|
#include "mcusupportdevice.h"
|
||||||
|
#include "mcusupportimportprovider.h"
|
||||||
#include "mcusupportoptions.h"
|
#include "mcusupportoptions.h"
|
||||||
#include "mcusupportoptionspage.h"
|
#include "mcusupportoptionspage.h"
|
||||||
#include "mcusupportrunconfiguration.h"
|
#include "mcusupportrunconfiguration.h"
|
||||||
@@ -102,6 +103,7 @@ public:
|
|||||||
McuSupportOptions m_options{m_settingsHandler};
|
McuSupportOptions m_options{m_settingsHandler};
|
||||||
McuSupportOptionsPage optionsPage{m_options, m_settingsHandler};
|
McuSupportOptionsPage optionsPage{m_options, m_settingsHandler};
|
||||||
MCUBuildStepFactory mcuBuildStepFactory;
|
MCUBuildStepFactory mcuBuildStepFactory;
|
||||||
|
McuSupportImportProvider mcuImportProvider;
|
||||||
}; // class McuSupportPluginPrivate
|
}; // class McuSupportPluginPrivate
|
||||||
|
|
||||||
static McuSupportPluginPrivate *dd{nullptr};
|
static McuSupportPluginPrivate *dd{nullptr};
|
||||||
|
Reference in New Issue
Block a user