diff --git a/src/libs/utils/htmldocextractor.cpp b/src/libs/utils/htmldocextractor.cpp index 41fd38c1ee85d49d2f59d8ffdb750416ea369a8a..faf16f144317d9035afdf9cde70b71dc8da9788a 100644 --- a/src/libs/utils/htmldocextractor.cpp +++ b/src/libs/utils/htmldocextractor.cpp @@ -137,11 +137,32 @@ QString HtmlDocExtractor::getFunctionDescription(const QString &html, return contents; } -QString HtmlDocExtractor::getQMLItemDescription(const QString &html, const QString &mark) const +QString HtmlDocExtractor::getQmlComponentDescription(const QString &html, const QString &mark) const { return getClassOrNamespaceDescription(html, mark); } +QString HtmlDocExtractor::getQmlPropertyDescription(const QString &html, const QString &mark) const +{ + QString startMark = QString("<a name=\"%1-prop\">").arg(mark); + int index = html.indexOf(startMark); + if (index == -1) { + startMark = QString("<a name=\"%1-signal\">").arg(mark); + index = html.indexOf(startMark); + } + if (index == -1) + return QString(); + + QString contents = html.mid(index + startMark.size()); + index = contents.indexOf(QLatin1String("<p>")); + if (index == -1) + return QString(); + contents = contents.mid(index); + processOutput(&contents); + + return contents; +} + QString HtmlDocExtractor::getClassOrNamespaceMemberDescription(const QString &html, const QString &startMark, const QString &endMark) const diff --git a/src/libs/utils/htmldocextractor.h b/src/libs/utils/htmldocextractor.h index 8efa69b25e33eab2170088bec30ceddf2bc5b4ab..7a18cdfabe53f8a4db5c724274befc0885547288 100644 --- a/src/libs/utils/htmldocextractor.h +++ b/src/libs/utils/htmldocextractor.h @@ -57,7 +57,8 @@ public: QString getFunctionDescription(const QString &html, const QString &mark, const bool mainOverload = true) const; - QString getQMLItemDescription(const QString &html, const QString &mark) const; + QString getQmlComponentDescription(const QString &html, const QString &mark) const; + QString getQmlPropertyDescription(const QString &html, const QString &mark) const; private: QString getClassOrNamespaceMemberDescription(const QString &html, diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index 510469c39778e9d2787e2d446be294314c958d4f..3cf4e837111f9c377fe3cdef5527c16d5a3cf293 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -109,25 +109,29 @@ void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) if (!qmlEditor) return; - if (!matchDiagnosticMessage(qmlEditor, pos)) { - const QmlJSEditor::SemanticInfo &semanticInfo = qmlEditor->semanticInfo(); - if (! semanticInfo.isValid() || semanticInfo.revision() != qmlEditor->editorRevision()) - return; - - QList<AST::Node *> astPath = semanticInfo.astPath(pos); - if (astPath.isEmpty()) - return; - - const Document::Ptr qmlDocument = semanticInfo.document; - LookupContext::Ptr lookupContext = semanticInfo.lookupContext(astPath); - - if (!matchColorItem(lookupContext, qmlDocument, astPath, pos)) { - handleOrdinaryMatch(lookupContext, semanticInfo.nodeUnderCursor(pos)); - const QString &helpId = qmlHelpId(toolTip()); - if (!helpId.isEmpty()) - setLastHelpItemIdentified(TextEditor::HelpItem(helpId, TextEditor::HelpItem::QML)); - } - } + if (matchDiagnosticMessage(qmlEditor, pos)) + return; + + const QmlJSEditor::SemanticInfo &semanticInfo = qmlEditor->semanticInfo(); + if (! semanticInfo.isValid() || semanticInfo.revision() != qmlEditor->editorRevision()) + return; + + QList<AST::Node *> astPath = semanticInfo.astPath(pos); + if (astPath.isEmpty()) + return; + + const Document::Ptr qmlDocument = semanticInfo.document; + LookupContext::Ptr lookupContext = semanticInfo.lookupContext(astPath); + + if (matchColorItem(lookupContext, qmlDocument, astPath, pos)) + return; + + AST::Node *node = semanticInfo.nodeUnderCursor(pos); + handleOrdinaryMatch(lookupContext, node); + + TextEditor::HelpItem helpItem = qmlHelpItem(lookupContext, node); + if (!helpItem.helpId().isEmpty()) + setLastHelpItemIdentified(helpItem); } bool HoverHandler::matchDiagnosticMessage(QmlJSEditor::QmlJSTextEditor *qmlEditor, int pos) @@ -236,19 +240,15 @@ void HoverHandler::prettyPrintTooltip(const QmlJS::Interpreter::Value *value, return; if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) { - bool found = false; Interpreter::PrototypeIterator iter(objectValue, context); while (iter.hasNext()) { const Interpreter::ObjectValue *prototype = iter.next(); const QString className = prototype->className(); if (! className.isEmpty()) { - found = !qmlHelpId(className).isEmpty(); - if (toolTip().isEmpty() || found) - setToolTip(className); - } - if (found) + setToolTip(className); break; + } } } else if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) { @@ -262,10 +262,74 @@ void HoverHandler::prettyPrintTooltip(const QmlJS::Interpreter::Value *value, } } -QString HoverHandler::qmlHelpId(const QString &itemName) const +// if node refers to a property, its name and defining object are returned - otherwise zero +static const Interpreter::ObjectValue *isMember(const LookupContext::Ptr &lookupContext, + AST::Node *node, QString *name) { - QString helpId(QLatin1String("QML.") + itemName); - if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) - return helpId; - return QString(); + const Interpreter::ObjectValue *owningObject = 0; + if (AST::IdentifierExpression *identExp = AST::cast<AST::IdentifierExpression *>(node)) { + if (!identExp->name) + return 0; + *name = identExp->name->asString(); + lookupContext->context()->lookup(*name, &owningObject); + } else if (AST::FieldMemberExpression *fme = AST::cast<AST::FieldMemberExpression *>(node)) { + if (!fme->base || !fme->name) + return 0; + *name = fme->name->asString(); + const Interpreter::Value *base = lookupContext->evaluate(fme->base); + if (!base) + return 0; + owningObject = base->asObjectValue(); + if (owningObject) + owningObject->lookupMember(*name, lookupContext->context(), &owningObject); + } else if (AST::UiQualifiedId *qid = AST::cast<AST::UiQualifiedId *>(node)) { + if (!qid->name) + return 0; + *name = qid->name->asString(); + const Interpreter::Value *value = lookupContext->context()->lookup(*name, &owningObject); + for (AST::UiQualifiedId *it = qid->next; it; it = it->next) { + if (!value) + return 0; + const Interpreter::ObjectValue *next = value->asObjectValue(); + if (!next || !it->name) + return 0; + *name = it->name->asString(); + value = next->lookupMember(*name, lookupContext->context(), &owningObject); + } + } + return owningObject; +} + +TextEditor::HelpItem HoverHandler::qmlHelpItem(const LookupContext::Ptr &lookupContext, + AST::Node *node) const +{ + QString name; + if (const Interpreter::ObjectValue *scope = isMember(lookupContext, node, &name)) { + // maybe it's a type? + if (!name.isEmpty() && name.at(0).isUpper()) { + const QString maybeHelpId(QLatin1String("QML.") + name); + if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId).isEmpty()) + return TextEditor::HelpItem(maybeHelpId, name, TextEditor::HelpItem::QmlComponent); + } + + // otherwise, it's probably a property + const Interpreter::ObjectValue *lastScope; + scope->lookupMember(name, lookupContext->context(), &lastScope); + Interpreter::PrototypeIterator iter(scope, lookupContext->context()); + while (iter.hasNext()) { + const Interpreter::ObjectValue *cur = iter.next(); + + const QString className = cur->className(); + if (!className.isEmpty()) { + const QString maybeHelpId(className + QLatin1String("::") + name); + if (!Core::HelpManager::instance()->linksForIdentifier(maybeHelpId).isEmpty()) + return TextEditor::HelpItem(maybeHelpId, name, TextEditor::HelpItem::QmlProperty); + } + + if (cur == lastScope) + break; + } + } + + return TextEditor::HelpItem(); } diff --git a/src/plugins/qmljseditor/qmljshoverhandler.h b/src/plugins/qmljseditor/qmljshoverhandler.h index ea59ccdb79f1f6ea6867ebca6b051bfe973d5eec..42af413532a770a7332f96d0da680a9a65974925 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.h +++ b/src/plugins/qmljseditor/qmljshoverhandler.h @@ -79,7 +79,8 @@ private: void prettyPrintTooltip(const QmlJS::Interpreter::Value *value, const QmlJS::Interpreter::Context *context); - QString qmlHelpId(const QString &itemName) const; + TextEditor::HelpItem qmlHelpItem(const QmlJS::LookupContext::Ptr &lookupContext, + QmlJS::AST::Node *node) const; QmlJS::ModelManagerInterface *m_modelManager; QColor m_colorTip; diff --git a/src/plugins/texteditor/helpitem.cpp b/src/plugins/texteditor/helpitem.cpp index 33badec1c019b823c798ffd6b9378365b1094142..b22e5e3bae725e4c449f7e0c8de41dc0cd1d4f14 100644 --- a/src/plugins/texteditor/helpitem.cpp +++ b/src/plugins/texteditor/helpitem.cpp @@ -108,8 +108,11 @@ QString HelpItem::extractContent(bool extended) const case Macro: contents = htmlExtractor.getMacroDescription(html, m_docMark); break; - case QML: - contents = htmlExtractor.getQMLItemDescription(html, m_docMark); + case QmlComponent: + contents = htmlExtractor.getQmlComponentDescription(html, m_docMark); + break; + case QmlProperty: + contents = htmlExtractor.getQmlPropertyDescription(html, m_docMark); break; default: diff --git a/src/plugins/texteditor/helpitem.h b/src/plugins/texteditor/helpitem.h index 89f8aa7be373109cbedb0658366eb2355728128c..601f9cc27bc6824050a2f36ec93d31e110d78c3c 100644 --- a/src/plugins/texteditor/helpitem.h +++ b/src/plugins/texteditor/helpitem.h @@ -46,7 +46,8 @@ public: Macro, Brief, Function, - QML, + QmlComponent, + QmlProperty, Unknown };