Commit cf15e14e authored by Christian Kamm's avatar Christian Kamm
Browse files

QmlJS: Add tooltips to imports.

Task-number: QTCREATORBUG-4814
parent 04c5425d
......@@ -365,7 +365,7 @@ void Document::extractPragmas(QString *source)
LibraryInfo::LibraryInfo()
: _valid(false)
, _dumpStatus(DumpNotStartedOrRunning)
, _dumpStatus(NoTypeInfo)
{
}
......@@ -373,7 +373,7 @@ LibraryInfo::LibraryInfo(const QmlDirParser &parser)
: _valid(true)
, _components(parser.components())
, _plugins(parser.plugins())
, _dumpStatus(DumpNotStartedOrRunning)
, _dumpStatus(NoTypeInfo)
{
}
......
......@@ -124,10 +124,12 @@ private:
class QMLJS_EXPORT LibraryInfo
{
public:
enum DumpStatus {
DumpNotStartedOrRunning,
enum PluginTypeInfoStatus {
NoTypeInfo,
DumpDone,
DumpError
DumpError,
TypeInfoFileDone,
TypeInfoFileError
};
private:
......@@ -137,7 +139,7 @@ private:
typedef QList<LanguageUtils::FakeMetaObject::ConstPtr> FakeMetaObjectList;
FakeMetaObjectList _metaObjects;
DumpStatus _dumpStatus;
PluginTypeInfoStatus _dumpStatus;
QString _dumpError;
public:
......@@ -160,13 +162,13 @@ public:
bool isValid() const
{ return _valid; }
DumpStatus dumpStatus() const
PluginTypeInfoStatus pluginTypeInfoStatus() const
{ return _dumpStatus; }
QString dumpError() const
QString pluginTypeInfoError() const
{ return _dumpError; }
void setDumpStatus(DumpStatus dumped, const QString &error = QString())
void setPluginTypeInfoStatus(PluginTypeInfoStatus dumped, const QString &error = QString())
{ _dumpStatus = dumped; _dumpError = error; }
};
......
......@@ -3374,6 +3374,10 @@ UiImport *ImportInfo::ast() const
return _ast;
}
TypeEnvironment::Import::Import()
: object(0)
{}
TypeEnvironment::TypeEnvironment(Engine *engine)
: ObjectValue(engine)
{
......@@ -3434,12 +3438,9 @@ void TypeEnvironment::processMembers(MemberProcessor *processor) const
}
}
void TypeEnvironment::addImport(const ObjectValue *import, const ImportInfo &info)
void TypeEnvironment::addImport(const Import &import)
{
Import i;
i.object = import;
i.info = info;
_imports.append(i);
_imports.append(import);
}
ImportInfo TypeEnvironment::importInfo(const QString &name, const Context *context) const
......@@ -3473,6 +3474,11 @@ ImportInfo TypeEnvironment::importInfo(const QString &name, const Context *conte
return ImportInfo();
}
QList<TypeEnvironment::Import> TypeEnvironment::imports() const
{
return _imports;
}
#ifdef QT_DEBUG
class MemberDumper: public MemberProcessor
......
......@@ -1020,16 +1020,18 @@ private:
class QMLJS_EXPORT TypeEnvironment: public ObjectValue
{
public:
class Import {
public:
const ObjectValue *object;
Import();
// const!
ObjectValue *object;
ImportInfo info;
// uri imports: path to library, else empty
QString libraryPath;
};
// holds imports in the order they appeared,
// lookup order is back to front
QList<Import> _imports;
public:
TypeEnvironment(Engine *engine);
......@@ -1038,12 +1040,19 @@ public:
bool examinePrototypes = true) const;
virtual void processMembers(MemberProcessor *processor) const;
void addImport(const ObjectValue *import, const ImportInfo &info);
void addImport(const Import &import);
ImportInfo importInfo(const QString &name, const Context *context) const;
QList<Import> imports() const;
#ifdef QT_DEBUG
void dump() const;
#endif
private:
// holds imports in the order they appeared,
// lookup order is back to front
QList<Import> _imports;
};
} } // namespace QmlJS::Interpreter
......
......@@ -89,7 +89,7 @@ public:
Interpreter::Context *context;
QStringList importPaths;
QHash<ImportCacheKey, Interpreter::ObjectValue *> importCache;
QHash<ImportCacheKey, TypeEnvironment::Import> importCache;
Document::Ptr doc;
QList<DiagnosticMessage> *diagnosticMessages;
......@@ -192,9 +192,9 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc)
// explicit imports, whether directories, files or libraries
foreach (const ImportInfo &info, doc->bind()->imports()) {
ObjectValue *import = d->importCache.value(ImportCacheKey(info));
TypeEnvironment::Import import = d->importCache.value(ImportCacheKey(info));
if (!import) {
if (!import.object) {
switch (info.type()) {
case ImportInfo::FileImport:
case ImportInfo::DirectoryImport:
......@@ -206,11 +206,10 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc)
default:
break;
}
if (import)
if (import.object)
d->importCache.insert(ImportCacheKey(info), import);
}
if (import)
typeEnv->addImport(import, info);
typeEnv->addImport(import);
}
}
......@@ -222,30 +221,33 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc)
import "http://www.ovi.com/" as Ovi
*/
ObjectValue *Link::importFileOrDirectory(Document::Ptr doc, const ImportInfo &importInfo)
TypeEnvironment::Import Link::importFileOrDirectory(Document::Ptr doc, const ImportInfo &importInfo)
{
Q_D(Link);
ObjectValue *import = 0;
TypeEnvironment::Import import;
import.info = importInfo;
import.object = 0;
const QString &path = importInfo.name();
if (importInfo.type() == ImportInfo::DirectoryImport
|| importInfo.type() == ImportInfo::ImplicitDirectoryImport) {
import = new ObjectValue(engine());
import.object = new ObjectValue(engine());
importLibrary(doc, import, path, importInfo);
importLibrary(doc, path, &import);
const QList<Document::Ptr> &documentsInDirectory = d->snapshot.documentsInDirectory(path);
foreach (Document::Ptr importedDoc, documentsInDirectory) {
if (importedDoc->bind()->rootObjectValue()) {
const QString targetName = importedDoc->componentName();
import->setProperty(targetName, importedDoc->bind()->rootObjectValue());
import.object->setProperty(targetName, importedDoc->bind()->rootObjectValue());
}
}
} else if (importInfo.type() == ImportInfo::FileImport) {
Document::Ptr importedDoc = d->snapshot.document(path);
if (importedDoc)
import = importedDoc->bind()->rootObjectValue();
import.object = importedDoc->bind()->rootObjectValue();
}
return import;
......@@ -256,11 +258,14 @@ ObjectValue *Link::importFileOrDirectory(Document::Ptr doc, const ImportInfo &im
import Qt 4.6 as Xxx
(import com.nokia.qt is the same as the ones above)
*/
ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo)
TypeEnvironment::Import Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo)
{
Q_D(Link);
ObjectValue *import = new ObjectValue(engine());
TypeEnvironment::Import import;
import.info = importInfo;
import.object = new ObjectValue(engine());
const QString packageName = Bind::toString(importInfo.ast()->importUri, '.');
const ComponentVersion version = importInfo.version();
......@@ -273,7 +278,7 @@ ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo
libraryPath += QDir::separator();
libraryPath += packagePath;
if (importLibrary(doc, import, libraryPath, importInfo, importPath)) {
if (importLibrary(doc, libraryPath, &import, importPath)) {
importFound = true;
break;
}
......@@ -284,7 +289,7 @@ ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo
importFound = true;
foreach (QmlObjectValue *object,
engine()->cppQmlTypes().typesForImport(packageName, version)) {
import->setProperty(object->className(), object);
import.object->setProperty(object->className(), object);
}
}
......@@ -297,17 +302,21 @@ ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo
return import;
}
bool Link::importLibrary(Document::Ptr doc, Interpreter::ObjectValue *import,
bool Link::importLibrary(Document::Ptr doc,
const QString &libraryPath,
const Interpreter::ImportInfo &importInfo,
TypeEnvironment::Import *import,
const QString &importPath)
{
Q_D(Link);
const ImportInfo &importInfo = import->info;
const LibraryInfo libraryInfo = d->snapshot.libraryInfo(libraryPath);
if (!libraryInfo.isValid())
return false;
import->libraryPath = libraryPath;
const ComponentVersion version = importInfo.version();
const UiImport *ast = importInfo.ast();
SourceLocation errorLoc;
......@@ -315,7 +324,7 @@ bool Link::importLibrary(Document::Ptr doc, Interpreter::ObjectValue *import,
errorLoc = locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation());
if (!libraryInfo.plugins().isEmpty()) {
if (libraryInfo.dumpStatus() == LibraryInfo::DumpNotStartedOrRunning) {
if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::NoTypeInfo) {
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
if (modelManager) {
if (importInfo.type() == ImportInfo::LibraryImport) {
......@@ -335,7 +344,8 @@ bool Link::importLibrary(Document::Ptr doc, Interpreter::ObjectValue *import,
warning(doc, errorLoc,
tr("Library contains C++ plugins, type dump is in progress."));
}
} else if (libraryInfo.dumpStatus() == LibraryInfo::DumpError) {
} else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpError
|| libraryInfo.pluginTypeInfoStatus() == LibraryInfo::TypeInfoFileError) {
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
// Only underline import if package/version isn't described in .qmltypes anyway
......@@ -344,7 +354,7 @@ bool Link::importLibrary(Document::Ptr doc, Interpreter::ObjectValue *import,
const QString packageName = importInfo.name().replace(QDir::separator(), QLatin1Char('.'));
if (!builtinPackages.value(packageName).contains(importInfo.version())) {
if (errorLoc.isValid()) {
error(doc, errorLoc, libraryInfo.dumpError());
error(doc, errorLoc, libraryInfo.pluginTypeInfoError());
}
}
} else {
......@@ -352,13 +362,13 @@ bool Link::importLibrary(Document::Ptr doc, Interpreter::ObjectValue *import,
engine()->cppQmlTypes().load(engine(), libraryInfo.metaObjects());
foreach (QmlObjectValue *object, loadedObjects) {
if (object->packageName().isEmpty()) {
import->setProperty(object->className(), object);
import->object->setProperty(object->className(), object);
}
}
}
}
loadQmldirComponents(import, version, libraryInfo, libraryPath);
loadQmldirComponents(import->object, version, libraryInfo, libraryPath);
return true;
}
......@@ -431,14 +441,15 @@ void Link::loadImplicitDirectoryImports(TypeEnvironment *typeEnv, Document::Ptr
ImportInfo implcitDirectoryImportInfo(
ImportInfo::ImplicitDirectoryImport, doc->path());
ObjectValue *directoryImport = d->importCache.value(ImportCacheKey(implcitDirectoryImportInfo));
if (!directoryImport) {
TypeEnvironment::Import directoryImport = d->importCache.value(ImportCacheKey(implcitDirectoryImportInfo));
if (!directoryImport.object) {
directoryImport = importFileOrDirectory(doc, implcitDirectoryImportInfo);
if (directoryImport)
if (directoryImport.object)
d->importCache.insert(ImportCacheKey(implcitDirectoryImportInfo), directoryImport);
}
if (directoryImport)
typeEnv->addImport(directoryImport, implcitDirectoryImportInfo);
if (directoryImport.object) {
typeEnv->addImport(directoryImport);
}
}
void Link::loadImplicitDefaultImports(TypeEnvironment *typeEnv)
......@@ -448,15 +459,16 @@ void Link::loadImplicitDefaultImports(TypeEnvironment *typeEnv)
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());
TypeEnvironment::Import import = d->importCache.value(ImportCacheKey(info));
if (!import.object) {
import.info = info;
import.object = new ObjectValue(engine());
foreach (QmlObjectValue *object,
engine()->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
import->setProperty(object->className(), object);
import.object->setProperty(object->className(), object);
}
d->importCache.insert(ImportCacheKey(info), import);
}
typeEnv->addImport(import, info);
typeEnv->addImport(import);
}
}
......@@ -74,14 +74,17 @@ private:
void linkImports();
void populateImportedTypes(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc);
Interpreter::ObjectValue *importFileOrDirectory(Document::Ptr doc, const Interpreter::ImportInfo &importInfo);
Interpreter::ObjectValue *importNonFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo);
Interpreter::TypeEnvironment::Import importFileOrDirectory(
Document::Ptr doc,
const Interpreter::ImportInfo &importInfo);
Interpreter::TypeEnvironment::Import importNonFile(
Document::Ptr doc,
const Interpreter::ImportInfo &importInfo);
void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace);
bool importLibrary(Document::Ptr doc,
Interpreter::ObjectValue *import,
const QString &libraryPath,
const Interpreter::ImportInfo &importInfo,
Interpreter::TypeEnvironment::Import *import,
const QString &importPath = QString());
void loadQmldirComponents(Interpreter::ObjectValue *import,
LanguageUtils::ComponentVersion version,
......
......@@ -120,16 +120,19 @@ void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos)
return;
QList<AST::Node *> astPath = semanticInfo.astPath(pos);
if (astPath.isEmpty())
return;
const Document::Ptr qmlDocument = semanticInfo.document;
LookupContext::Ptr lookupContext = semanticInfo.lookupContext(astPath);
AST::Node *node = semanticInfo.nodeUnderCursor(pos);
if (astPath.isEmpty()) {
if (AST::UiImport *import = AST::cast<AST::UiImport *>(node))
handleImport(lookupContext, import);
return;
}
if (matchColorItem(lookupContext, qmlDocument, astPath, pos))
return;
AST::Node *node = semanticInfo.nodeUnderCursor(pos);
handleOrdinaryMatch(lookupContext, node);
TextEditor::HelpItem helpItem = qmlHelpItem(lookupContext, node);
......@@ -214,6 +217,34 @@ void HoverHandler::handleOrdinaryMatch(const LookupContext::Ptr &lookupContext,
}
}
void HoverHandler::handleImport(const LookupContext::Ptr &lookupContext, AST::UiImport *node)
{
const Interpreter::TypeEnvironment *typeEnv = lookupContext->context()->typeEnvironment(lookupContext->document().data());
if (!typeEnv)
return;
foreach (const Interpreter::TypeEnvironment::Import &import, typeEnv->imports()) {
if (import.info.ast() == node) {
if (import.info.type() == Interpreter::ImportInfo::LibraryImport
&& !import.libraryPath.isEmpty()) {
QString msg = tr("Library at %1").arg(import.libraryPath);
const LibraryInfo &libraryInfo = lookupContext->snapshot().libraryInfo(import.libraryPath);
if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpDone) {
msg += QLatin1Char('\n');
msg += tr("Dumped plugins successfully.");
} else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::TypeInfoFileDone) {
msg += QLatin1Char('\n');
msg += tr("Read typeinfo files successfully.");
}
setToolTip(msg);
} else {
setToolTip(import.info.name());
}
break;
}
}
}
void HoverHandler::reset()
{
m_colorTip = QColor();
......
......@@ -78,6 +78,8 @@ private:
unsigned pos);
void handleOrdinaryMatch(const QmlJS::LookupContext::Ptr &lookupContext,
QmlJS::AST::Node *node);
void handleImport(const QmlJS::LookupContext::Ptr &lookupContext,
QmlJS::AST::UiImport *node);
void prettyPrintTooltip(const QmlJS::Interpreter::Value *value,
const QmlJS::Interpreter::Context *context);
......
......@@ -82,7 +82,7 @@ void PluginDumper::onLoadPluginTypes(const QString &libraryPath, const QString &
return;
const Snapshot snapshot = m_modelManager->snapshot();
const LibraryInfo libraryInfo = snapshot.libraryInfo(canonicalLibraryPath);
if (libraryInfo.dumpStatus() != LibraryInfo::DumpNotStartedOrRunning)
if (libraryInfo.pluginTypeInfoStatus() != LibraryInfo::NoTypeInfo)
return;
// avoid inserting the same plugin twice
......@@ -174,14 +174,14 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
Core::MessageManager *messageManager = Core::MessageManager::instance();
const QString errorMessages = process->readAllStandardError();
messageManager->printToOutputPane(qmldumpErrorMessage(libraryPath, errorMessages));
libraryInfo.setDumpStatus(LibraryInfo::DumpError, qmldumpFailedMessage(errorMessages));
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(errorMessages));
}
const QByteArray output = process->readAllStandardOutput();
QString error;
QList<FakeMetaObject::ConstPtr> objectsList = parseHelper(output, &error);
if (exitCode == 0 && !error.isEmpty()) {
libraryInfo.setDumpStatus(LibraryInfo::DumpError, tr("Type dump of C++ plugin failed. Parse error:\n'%1'").arg(error));
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, tr("Type dump of C++ plugin failed. Parse error:\n'%1'").arg(error));
}
if (exitCode == 0 && error.isEmpty()) {
......@@ -189,7 +189,7 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
// ### disabled code path for running qmldump to get Qt's builtins
// if (libraryPath.isEmpty())
// Interpreter::CppQmlTypesLoader::builtinObjects.append(objectsList);
libraryInfo.setDumpStatus(LibraryInfo::DumpDone);
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
}
if (!libraryPath.isEmpty())
......@@ -212,7 +212,7 @@ void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
if (!libraryPath.isEmpty()) {
const Snapshot snapshot = m_modelManager->snapshot();
LibraryInfo libraryInfo = snapshot.libraryInfo(libraryPath);
libraryInfo.setDumpStatus(LibraryInfo::DumpError, qmldumpFailedMessage(errorMessages));
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(errorMessages));
m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
}
}
......@@ -238,7 +238,7 @@ void PluginDumper::dump(const Plugin &plugin)
const QString &path = plugin.predumpedQmlTypesFilePath();
Utils::FileReader reader;
if (!reader.fetch(path, QFile::Text)) {
libraryInfo.setDumpStatus(LibraryInfo::DumpError, reader.errorString());
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, reader.errorString());
m_modelManager->updateLibraryInfo(plugin.qmldirPath, libraryInfo);
return;
}
......@@ -248,9 +248,9 @@ void PluginDumper::dump(const Plugin &plugin)
if (error.isEmpty()) {
libraryInfo.setMetaObjects(objectsList);
libraryInfo.setDumpStatus(LibraryInfo::DumpDone);
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
} else {
libraryInfo.setDumpStatus(LibraryInfo::DumpError,
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError,
tr("Failed to parse '%1'.\nError: %2").arg(path, error));
}
m_modelManager->updateLibraryInfo(plugin.qmldirPath, libraryInfo);
......@@ -269,7 +269,7 @@ void PluginDumper::dump(const Plugin &plugin)
if (!libraryInfo.isValid())
return;
libraryInfo.setDumpStatus(LibraryInfo::DumpError,
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
tr("Could not locate the helper application for dumping type information from C++ plugins.\n"
"Please build the debugging helpers on the Qt version options page."));
m_modelManager->updateLibraryInfo(plugin.qmldirPath, libraryInfo);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment