diff options
author | Fawzi Mohamed <fawzi.mohamed@digia.com> | 2013-01-31 20:01:39 +0100 |
---|---|---|
committer | Fawzi Mohamed <fawzi.mohamed@digia.com> | 2013-02-12 11:36:50 +0100 |
commit | bd7dfeee921474975268abcea62fa7c90bd31953 (patch) | |
tree | 280e022a1048d87fdaa0c42d38e68f97f0b60728 /src | |
parent | c4f76435adf378033843d596c4f26dda2b1336ef (diff) | |
download | qt-creator-bd7dfeee921474975268abcea62fa7c90bd31953.tar.gz |
qmljs: make context help more robust
Try harder to get help for types even if the parsing is almost
completely broken.
Task-number: QTCREATORBUG-3169
Change-Id: I013a0aa45c654570c88ef77564bed43727fce5fb
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/qmljseditor/qmljshoverhandler.cpp | 203 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljshoverhandler.h | 9 |
2 files changed, 145 insertions, 67 deletions
diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index fff9076f2a..5910497e43 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -51,6 +51,7 @@ #include <QDir> #include <QList> +#include <QStringRef> using namespace Core; using namespace QmlJS; @@ -101,6 +102,79 @@ bool HoverHandler::acceptEditor(IEditor *editor) return false; } +static inline QString getModuleName(const ScopeChain &scopeChain, const Document::Ptr &qmlDocument, + const ObjectValue *value) +{ + if (!value) + return QString(); + + const CppComponentValue *qmlValue = value_cast<CppComponentValue>(value); + if (qmlValue) { + const QString moduleName = qmlValue->moduleName(); + const Imports *imports = scopeChain.context()->imports(qmlDocument.data()); + const ImportInfo importInfo = imports->info(qmlValue->className(), scopeChain.context().data()); + if (importInfo.isValid() && importInfo.type() == ImportInfo::LibraryImport) { + const int majorVersion = importInfo.version().majorVersion(); + const int minorVersion = importInfo.version().minorVersion(); + return moduleName + QString::number(majorVersion) + QLatin1Char('.') + + QString::number(minorVersion) ; + } + return QString(); + } else { + QString typeName = value->className(); + + const Imports *imports = scopeChain.context()->imports(qmlDocument.data()); + const ImportInfo importInfo = imports->info(typeName, scopeChain.context().data()); + if (importInfo.isValid() && importInfo.type() == ImportInfo::LibraryImport) { + const QString moduleName = importInfo.name(); + const int majorVersion = importInfo.version().majorVersion(); + const int minorVersion = importInfo.version().minorVersion(); + return moduleName + QString::number(majorVersion) + QLatin1Char('.') + + QString::number(minorVersion) ; + } else if (importInfo.isValid() && importInfo.type() == ImportInfo::DirectoryImport) { + const QString path = importInfo.path(); + const QDir dir(qmlDocument->path()); + QString relativeDir = dir.relativeFilePath(path); + const QString name = relativeDir.replace(QLatin1Char('/'), QLatin1Char('.')); + return name; + } + } + return QString(); +} + +bool HoverHandler::setQmlTypeHelp(const ScopeChain &scopeChain, const Document::Ptr &qmlDocument, + const ObjectValue *value, const QStringList &qName) +{ + QString moduleName = getModuleName(scopeChain, qmlDocument, value); + QString helpId; + do { + QStringList helpIdPieces(qName); + helpIdPieces.prepend(moduleName); + helpIdPieces.prepend(QLatin1String("QML")); + helpId = helpIdPieces.join(QLatin1Char('.')); + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + if (helpIdPieces.size() > 3) { + QString lm = helpIdPieces.value(2); + helpIdPieces.removeAt(2); + helpId = helpIdPieces.join(QLatin1Char('.')); + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + helpIdPieces.replace(1, lm); + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + } + helpIdPieces.removeAt(1); + helpId = helpIdPieces.join(QLatin1Char('.')); + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + return false; + } while (0); + setLastHelpItemIdentified(TextEditor::HelpItem(helpId, qName.join(QLatin1Char('.')), + TextEditor::HelpItem::QmlComponent)); + return true; +} + void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) { reset(); @@ -112,9 +186,6 @@ void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) if (!qmlEditor) return; - if (matchDiagnosticMessage(qmlEditor, pos)) - return; - const QmlJSTools::SemanticInfo &semanticInfo = qmlEditor->semanticInfo(); if (! semanticInfo.isValid() || qmlEditor->isSemanticInfoOutdated()) return; @@ -138,16 +209,44 @@ void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) import = AST::cast<AST::UiImport *>(astPath.at(astPath.size() - 2)); if (import) handleImport(scopeChain, import); + // maybe parsing failed badly, still try to identify types + quint32 i,j; + i = j = pos; + QString nameAtt; + for (;;) { + QChar c = qmlEditor->characterAt(j); + if (!c.isLetterOrNumber()) break; + nameAtt.append(c); + ++j; + } + QStringList qName; + while (i>0) { + --i; + QChar c = qmlEditor->characterAt(i); + if (c.isLetterOrNumber()) { + nameAtt.prepend(c); + } else if (c == QLatin1Char('.')) { + qName.append(nameAtt); + nameAtt.clear(); + } else { + qName.append(nameAtt); + break; + } + } + const ObjectValue *value = scopeChain.context()->lookupType(qmlDocument.data(), qName); + setQmlTypeHelp(scopeChain, qmlDocument, value, qName); + matchDiagnosticMessage(qmlEditor, pos); return; } + if (matchDiagnosticMessage(qmlEditor, pos)) + return; if (matchColorItem(scopeChain, qmlDocument, rangePath, pos)) return; handleOrdinaryMatch(scopeChain, node); - TextEditor::HelpItem helpItem = qmlHelpItem(scopeChain, qmlDocument, node); - if (!helpItem.helpId().isEmpty()) - setLastHelpItemIdentified(helpItem); + + setQmlHelpItem(scopeChain, qmlDocument, node); } bool HoverHandler::matchDiagnosticMessage(QmlJSEditor::QmlJSTextEditorWidget *qmlEditor, int pos) @@ -347,46 +446,9 @@ static const ObjectValue *isMember(const ScopeChain &scopeChain, return owningObject; } -static inline QString getModuleName(const ScopeChain &scopeChain, const Document::Ptr &qmlDocument, const ObjectValue *value) -{ - if (!value) - return QString(); - - const CppComponentValue *qmlValue = value_cast<CppComponentValue>(value); - if (qmlValue) { - const QString moduleName = qmlValue->moduleName(); - const Imports *imports = scopeChain.context()->imports(qmlDocument.data()); - const ImportInfo importInfo = imports->info(qmlValue->className(), scopeChain.context().data()); - if (importInfo.isValid() && importInfo.type() == ImportInfo::LibraryImport) { - const int majorVersion = importInfo.version().majorVersion(); - const int minorVersion = importInfo.version().minorVersion(); - return moduleName + QString::number(majorVersion) + QLatin1Char('.') + QString::number(minorVersion) ; - } - return QString(); - } else { - QString typeName = value->className(); - - const Imports *imports = scopeChain.context()->imports(qmlDocument.data()); - const ImportInfo importInfo = imports->info(typeName, scopeChain.context().data()); - if (importInfo.isValid() && importInfo.type() == ImportInfo::LibraryImport) { - const QString moduleName = importInfo.name(); - const int majorVersion = importInfo.version().majorVersion(); - const int minorVersion = importInfo.version().minorVersion(); - return moduleName + QString::number(majorVersion) + QLatin1Char('.') + QString::number(minorVersion) ; - } else if (importInfo.isValid() && importInfo.type() == ImportInfo::DirectoryImport) { - const QString path = importInfo.path(); - const QDir dir(qmlDocument->path()); - QString relativeDir = dir.relativeFilePath(path); - const QString name = relativeDir.replace(QLatin1Char('/'), QLatin1Char('.')); - return name; - } - } - return QString(); -} - -TextEditor::HelpItem HoverHandler::qmlHelpItem(const ScopeChain &scopeChain, - const Document::Ptr &qmlDocument, - AST::Node *node) const +bool HoverHandler::setQmlHelpItem(const ScopeChain &scopeChain, + const Document::Ptr &qmlDocument, + AST::Node *node) { QString name; QString moduleName; @@ -395,13 +457,8 @@ TextEditor::HelpItem HoverHandler::qmlHelpItem(const ScopeChain &scopeChain, if (!name.isEmpty() && name.at(0).isUpper()) { AST::UiQualifiedId *qualifiedId = AST::cast<AST::UiQualifiedId *>(node); const ObjectValue *value = scopeChain.context()->lookupType(qmlDocument.data(), qualifiedId); - moduleName = getModuleName(scopeChain, qmlDocument, value); - const QString maybeHelpId1(QLatin1String("QML.") + moduleName + QLatin1Char('.') + name); - if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId1).isEmpty()) - return TextEditor::HelpItem(maybeHelpId1, name, TextEditor::HelpItem::QmlComponent); - const QString maybeHelpId2(QLatin1String("QML.") + name); - if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId2).isEmpty()) - return TextEditor::HelpItem(maybeHelpId2, name, TextEditor::HelpItem::QmlComponent); + if (setQmlTypeHelp(scopeChain, qmlDocument, value, QStringList(qualifiedId->name.toString()))) + return true; } // otherwise, it's probably a property @@ -414,21 +471,39 @@ TextEditor::HelpItem HoverHandler::qmlHelpItem(const ScopeChain &scopeChain, const QString className = cur->className(); if (!className.isEmpty()) { moduleName = getModuleName(scopeChain, qmlDocument, cur); - const QString maybeHelpId1(QLatin1String("QML.") + moduleName + QLatin1Char('.') + className + QLatin1String("::") + name); - if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId1).isEmpty()) - return TextEditor::HelpItem(maybeHelpId1, name, TextEditor::HelpItem::QmlProperty); - const QString maybeHelpId2(QLatin1String("QML.") + className + QLatin1String("::") + name); - if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId2).isEmpty()) - return TextEditor::HelpItem(maybeHelpId2, name, TextEditor::HelpItem::QmlProperty); - const QString maybeHelpId3(className + QLatin1String("::") + name); - if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId3).isEmpty()) - return TextEditor::HelpItem(maybeHelpId3, name, TextEditor::HelpItem::QmlProperty); + QString helpId; + do { + helpId = QLatin1String("QML.") + moduleName + QLatin1Char('.') + className + + QLatin1String("::") + name; + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + helpId = QLatin1String("QML.") + className + QLatin1String("::") + name; + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + helpId = className + QLatin1String("::") + name; + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) + break; + helpId.clear(); + } while (0); + if (!helpId.isEmpty()) { + setLastHelpItemIdentified( + TextEditor::HelpItem(helpId, name, TextEditor::HelpItem::QmlProperty)); + return true; + } } if (cur == lastScope) break; } + } else { + // it might be a type, but the scope chain is broken (mismatched braces) + if (AST::UiQualifiedId *qualifiedId = AST::cast<AST::UiQualifiedId *>(node)) { + const ObjectValue *value = scopeChain.context()->lookupType(qmlDocument.data(), + qualifiedId); + if (setQmlTypeHelp(scopeChain, qmlDocument, value, + QStringList(qualifiedId->name.toString()))) + return true; + } } - - return TextEditor::HelpItem(); + return false; } diff --git a/src/plugins/qmljseditor/qmljshoverhandler.h b/src/plugins/qmljseditor/qmljshoverhandler.h index 3e19763ca7..057833ea91 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.h +++ b/src/plugins/qmljseditor/qmljshoverhandler.h @@ -52,6 +52,7 @@ class ScopeChain; class Context; typedef QSharedPointer<const Context> ContextPtr; class Value; +class ObjectValue; } namespace QmlJSEditor { @@ -85,9 +86,11 @@ private: void prettyPrintTooltip(const QmlJS::Value *value, const QmlJS::ContextPtr &context); - TextEditor::HelpItem qmlHelpItem(const QmlJS::ScopeChain &lookupContext, - const QmlJS::Document::Ptr &qmlDocument, - QmlJS::AST::Node *node) const; + bool setQmlTypeHelp(const QmlJS::ScopeChain &scopeChain, const QmlJS::Document::Ptr &qmlDocument, + const QmlJS::ObjectValue *value, const QStringList &qName); + bool setQmlHelpItem(const QmlJS::ScopeChain &lookupContext, + const QmlJS::Document::Ptr &qmlDocument, + QmlJS::AST::Node *node); QmlJS::ModelManagerInterface *m_modelManager; QColor m_colorTip; |