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 {
|
||||
InvalidImport,
|
||||
ImplicitDirectoryImport,
|
||||
ImplicitLibraryImport,
|
||||
LibraryImport,
|
||||
FileImport,
|
||||
DirectoryImport,
|
||||
|
||||
@@ -40,12 +40,12 @@
|
||||
#include "qmljsscopebuilder.h"
|
||||
#include "qmljsmodelmanagerinterface.h"
|
||||
|
||||
#include <languageutils/componentversion.h>
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <limits>
|
||||
|
||||
using namespace LanguageUtils;
|
||||
using namespace QmlJS;
|
||||
using namespace QmlJS::Interpreter;
|
||||
@@ -174,33 +174,15 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc)
|
||||
return;
|
||||
|
||||
// implicit imports: the <default> package is always available
|
||||
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);
|
||||
}
|
||||
|
||||
loadImplicitDefaultImports(typeEnv);
|
||||
|
||||
// implicit imports:
|
||||
// qml files in the same directory are available without explicit imports
|
||||
ImportInfo implcitImportInfo(ImportInfo::ImplicitDirectoryImport, doc->path());
|
||||
ObjectValue *directoryImport = d->importCache.value(ImportCacheKey(implcitImportInfo));
|
||||
if (!directoryImport) {
|
||||
directoryImport = importFile(doc, implcitImportInfo);
|
||||
if (directoryImport)
|
||||
d->importCache.insert(ImportCacheKey(implcitImportInfo), directoryImport);
|
||||
}
|
||||
if (directoryImport)
|
||||
typeEnv->addImport(directoryImport, implcitImportInfo);
|
||||
loadImplicitDirectoryImports(typeEnv, doc);
|
||||
|
||||
// implicit imports:
|
||||
// a qmldir file in the same directory gets automatically imported at the highest version
|
||||
loadImplicitLibraryImports(typeEnv, doc->path());
|
||||
|
||||
// explicit imports, whether directories, files or libraries
|
||||
foreach (const ImportInfo &info, doc->bind()->imports()) {
|
||||
@@ -305,23 +287,7 @@ ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
loadQmldirComponents(import, version, libraryInfo, libraryPath);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -369,3 +335,104 @@ void Link::warning(const Document::Ptr &doc, const AST::SourceLocation &loc, con
|
||||
if (doc->fileName() == d->doc->fileName())
|
||||
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/qmljsinterpreter.h>
|
||||
#include <qmljs/parser/qmljsastfwd_p.h>
|
||||
#include <languageutils/componentversion.h>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
@@ -73,6 +74,14 @@ private:
|
||||
Interpreter::ObjectValue *importNonFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo);
|
||||
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 warning(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user