OSDN Git Service

QmlJS: Add tooltips to imports.
authorChristian Kamm <christian.d.kamm@nokia.com>
Thu, 12 May 2011 13:29:00 +0000 (15:29 +0200)
committerChristian Kamm <christian.d.kamm@nokia.com>
Thu, 12 May 2011 13:29:41 +0000 (15:29 +0200)
Task-number: QTCREATORBUG-4814

src/libs/qmljs/qmljsdocument.cpp
src/libs/qmljs/qmljsdocument.h
src/libs/qmljs/qmljsinterpreter.cpp
src/libs/qmljs/qmljsinterpreter.h
src/libs/qmljs/qmljslink.cpp
src/libs/qmljs/qmljslink.h
src/plugins/qmljseditor/qmljshoverhandler.cpp
src/plugins/qmljseditor/qmljshoverhandler.h
src/plugins/qmljstools/qmljsplugindumper.cpp

index 42e3534..6766d9f 100644 (file)
@@ -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)
 {
 }
 
index 680f44b..d987a9c 100644 (file)
@@ -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; }
 };
 
index bc00d68..547123b 100644 (file)
@@ -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
index b33e489..c54b095 100644 (file)
@@ -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
index f8a2533..dd47528 100644 (file)
@@ -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);
     }
 }
index b94ef86..338dd11 100644 (file)
@@ -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,
index a0a13b7..c7b07a3 100644 (file)
@@ -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();
index 1b86b77..1146ae3 100644 (file)
@@ -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);
index 9b2ef8b..6ad21f2 100644 (file)
@@ -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);