forked from qt-creator/qt-creator
QmlJS: Try to implicitly import libraries in the same directory.
In QML, the current directory that holds a QML file is imported implicitly. If it contains a qmldir file, the library is imported. Since there is no explicit import statement, Creator can't know the URI of this library. However, if type information is available for it already - either through a previous dump or a qmltypes file - we can guess the URI by looking at the contained exports. Task-number: QTCREATORBUG-3768
This commit is contained in:
@@ -982,6 +982,7 @@ public:
|
|||||||
enum Type {
|
enum Type {
|
||||||
InvalidImport,
|
InvalidImport,
|
||||||
ImplicitDirectoryImport,
|
ImplicitDirectoryImport,
|
||||||
|
ImplicitLibraryImport,
|
||||||
LibraryImport,
|
LibraryImport,
|
||||||
FileImport,
|
FileImport,
|
||||||
DirectoryImport,
|
DirectoryImport,
|
||||||
|
@@ -40,12 +40,12 @@
|
|||||||
#include "qmljsscopebuilder.h"
|
#include "qmljsscopebuilder.h"
|
||||||
#include "qmljsmodelmanagerinterface.h"
|
#include "qmljsmodelmanagerinterface.h"
|
||||||
|
|
||||||
#include <languageutils/componentversion.h>
|
|
||||||
|
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace LanguageUtils;
|
using namespace LanguageUtils;
|
||||||
using namespace QmlJS;
|
using namespace QmlJS;
|
||||||
using namespace QmlJS::Interpreter;
|
using namespace QmlJS::Interpreter;
|
||||||
@@ -174,33 +174,15 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// implicit imports: the <default> package is always available
|
// implicit imports: the <default> package is always available
|
||||||
const QString defaultPackage = CppQmlTypes::defaultPackage;
|
loadImplicitDefaultImports(typeEnv);
|
||||||
if (engine()->cppQmlTypes().hasPackage(defaultPackage)) {
|
|
||||||
ImportInfo info(ImportInfo::LibraryImport, defaultPackage);
|
|
||||||
ObjectValue *import = d->importCache.value(ImportCacheKey(info));
|
|
||||||
if (!import) {
|
|
||||||
import = new ObjectValue(engine());
|
|
||||||
foreach (QmlObjectValue *object,
|
|
||||||
engine()->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
|
|
||||||
import->setProperty(object->className(), object);
|
|
||||||
}
|
|
||||||
d->importCache.insert(ImportCacheKey(info), import);
|
|
||||||
}
|
|
||||||
typeEnv->addImport(import, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// implicit imports:
|
// implicit imports:
|
||||||
// qml files in the same directory are available without explicit imports
|
// qml files in the same directory are available without explicit imports
|
||||||
ImportInfo implcitImportInfo(ImportInfo::ImplicitDirectoryImport, doc->path());
|
loadImplicitDirectoryImports(typeEnv, doc);
|
||||||
ObjectValue *directoryImport = d->importCache.value(ImportCacheKey(implcitImportInfo));
|
|
||||||
if (!directoryImport) {
|
// implicit imports:
|
||||||
directoryImport = importFile(doc, implcitImportInfo);
|
// a qmldir file in the same directory gets automatically imported at the highest version
|
||||||
if (directoryImport)
|
loadImplicitLibraryImports(typeEnv, doc->path());
|
||||||
d->importCache.insert(ImportCacheKey(implcitImportInfo), directoryImport);
|
|
||||||
}
|
|
||||||
if (directoryImport)
|
|
||||||
typeEnv->addImport(directoryImport, implcitImportInfo);
|
|
||||||
|
|
||||||
// explicit imports, whether directories, files or libraries
|
// explicit imports, whether directories, files or libraries
|
||||||
foreach (const ImportInfo &info, doc->bind()->imports()) {
|
foreach (const ImportInfo &info, doc->bind()->imports()) {
|
||||||
@@ -305,23 +287,7 @@ ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSet<QString> importedTypes;
|
loadQmldirComponents(import, version, libraryInfo, libraryPath);
|
||||||
foreach (const QmlDirParser::Component &component, libraryInfo.components()) {
|
|
||||||
if (importedTypes.contains(component.typeName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ComponentVersion componentVersion(component.majorVersion,
|
|
||||||
component.minorVersion);
|
|
||||||
if (version < componentVersion)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
importedTypes.insert(component.typeName);
|
|
||||||
if (Document::Ptr importedDoc = d->snapshot.document(
|
|
||||||
libraryPath + QDir::separator() + component.fileName)) {
|
|
||||||
if (ObjectValue *v = importedDoc->bind()->rootObjectValue())
|
|
||||||
import->setProperty(component.typeName, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -369,3 +335,104 @@ void Link::warning(const Document::Ptr &doc, const AST::SourceLocation &loc, con
|
|||||||
if (doc->fileName() == d->doc->fileName())
|
if (doc->fileName() == d->doc->fileName())
|
||||||
d->diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
|
d->diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Link::loadQmldirComponents(Interpreter::ObjectValue *import, ComponentVersion version,
|
||||||
|
const LibraryInfo &libraryInfo, const QString &libraryPath)
|
||||||
|
{
|
||||||
|
Q_D(Link);
|
||||||
|
|
||||||
|
QSet<QString> importedTypes;
|
||||||
|
foreach (const QmlDirParser::Component &component, libraryInfo.components()) {
|
||||||
|
if (importedTypes.contains(component.typeName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ComponentVersion componentVersion(component.majorVersion,
|
||||||
|
component.minorVersion);
|
||||||
|
if (version < componentVersion)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
importedTypes.insert(component.typeName);
|
||||||
|
if (Document::Ptr importedDoc = d->snapshot.document(
|
||||||
|
libraryPath + QDir::separator() + component.fileName)) {
|
||||||
|
if (ObjectValue *v = importedDoc->bind()->rootObjectValue())
|
||||||
|
import->setProperty(component.typeName, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Link::loadImplicitDirectoryImports(TypeEnvironment *typeEnv, Document::Ptr doc)
|
||||||
|
{
|
||||||
|
Q_D(Link);
|
||||||
|
|
||||||
|
ImportInfo implcitDirectoryImportInfo(ImportInfo::ImplicitDirectoryImport, doc->path());
|
||||||
|
ObjectValue *directoryImport = d->importCache.value(ImportCacheKey(implcitDirectoryImportInfo));
|
||||||
|
if (!directoryImport) {
|
||||||
|
directoryImport = importFile(doc, implcitDirectoryImportInfo);
|
||||||
|
if (directoryImport)
|
||||||
|
d->importCache.insert(ImportCacheKey(implcitDirectoryImportInfo), directoryImport);
|
||||||
|
}
|
||||||
|
if (directoryImport)
|
||||||
|
typeEnv->addImport(directoryImport, implcitDirectoryImportInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Link::loadImplicitLibraryImports(TypeEnvironment *typeEnv, const QString &path)
|
||||||
|
{
|
||||||
|
Q_D(Link);
|
||||||
|
|
||||||
|
ImportInfo implicitLibraryImportInfo(ImportInfo::ImplicitLibraryImport, path);
|
||||||
|
ObjectValue *libraryImport = d->importCache.value(ImportCacheKey(implicitLibraryImportInfo));
|
||||||
|
LibraryInfo libraryInfo = d->snapshot.libraryInfo(path);
|
||||||
|
if (!libraryImport && libraryInfo.isValid()) {
|
||||||
|
libraryImport = new ObjectValue(engine());
|
||||||
|
ComponentVersion latestVersion(std::numeric_limits<int>::max(),
|
||||||
|
std::numeric_limits<int>::max());
|
||||||
|
|
||||||
|
loadQmldirComponents(libraryImport, latestVersion, libraryInfo, path);
|
||||||
|
|
||||||
|
// ### since there is no way of determining the plugin URI, we can't dump
|
||||||
|
// the plugin if it has not been dumped already.
|
||||||
|
if (!libraryInfo.plugins().isEmpty()
|
||||||
|
&& libraryInfo.dumpStatus() == LibraryInfo::DumpDone) {
|
||||||
|
// add types to the engine
|
||||||
|
engine()->cppQmlTypes().load(engine(), libraryInfo.metaObjects());
|
||||||
|
|
||||||
|
// guess a likely URI
|
||||||
|
QMap<QString, int> uris;
|
||||||
|
foreach (const FakeMetaObject::ConstPtr &fmo, libraryInfo.metaObjects()) {
|
||||||
|
foreach (const FakeMetaObject::Export &exp, fmo->exports()) {
|
||||||
|
++uris[exp.package];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!uris.isEmpty()) {
|
||||||
|
const QString uri = (uris.end() - 1).key();
|
||||||
|
|
||||||
|
foreach (QmlObjectValue *object,
|
||||||
|
engine()->cppQmlTypes().typesForImport(uri, latestVersion)) {
|
||||||
|
libraryImport->setProperty(object->className(), object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (libraryImport)
|
||||||
|
typeEnv->addImport(libraryImport, implicitLibraryImportInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Link::loadImplicitDefaultImports(TypeEnvironment *typeEnv)
|
||||||
|
{
|
||||||
|
Q_D(Link);
|
||||||
|
|
||||||
|
const QString defaultPackage = CppQmlTypes::defaultPackage;
|
||||||
|
if (engine()->cppQmlTypes().hasPackage(defaultPackage)) {
|
||||||
|
ImportInfo info(ImportInfo::LibraryImport, defaultPackage);
|
||||||
|
ObjectValue *import = d->importCache.value(ImportCacheKey(info));
|
||||||
|
if (!import) {
|
||||||
|
import = new ObjectValue(engine());
|
||||||
|
foreach (QmlObjectValue *object,
|
||||||
|
engine()->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
|
||||||
|
import->setProperty(object->className(), object);
|
||||||
|
}
|
||||||
|
d->importCache.insert(ImportCacheKey(info), import);
|
||||||
|
}
|
||||||
|
typeEnv->addImport(import, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
#include <qmljs/qmljsdocument.h>
|
#include <qmljs/qmljsdocument.h>
|
||||||
#include <qmljs/qmljsinterpreter.h>
|
#include <qmljs/qmljsinterpreter.h>
|
||||||
#include <qmljs/parser/qmljsastfwd_p.h>
|
#include <qmljs/parser/qmljsastfwd_p.h>
|
||||||
|
#include <languageutils/componentversion.h>
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
|
||||||
@@ -73,6 +74,14 @@ private:
|
|||||||
Interpreter::ObjectValue *importNonFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo);
|
Interpreter::ObjectValue *importNonFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo);
|
||||||
void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace);
|
void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace);
|
||||||
|
|
||||||
|
void loadQmldirComponents(Interpreter::ObjectValue *import,
|
||||||
|
LanguageUtils::ComponentVersion version,
|
||||||
|
const LibraryInfo &libraryInfo,
|
||||||
|
const QString &libraryPath);
|
||||||
|
void loadImplicitDirectoryImports(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc);
|
||||||
|
void loadImplicitLibraryImports(Interpreter::TypeEnvironment *typeEnv, const QString &path);
|
||||||
|
void loadImplicitDefaultImports(Interpreter::TypeEnvironment *typeEnv);
|
||||||
|
|
||||||
void error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message);
|
void error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message);
|
||||||
void warning(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message);
|
void warning(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user