From 6ec316b8ffdbb6143359f42ca589267849ad6f11 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen <erik.verbruggen@nokia.com> Date: Thu, 8 Oct 2009 13:48:39 +0200 Subject: [PATCH] Added help-tooltips for QML types. --- src/plugins/qmleditor/qmleditor.pro | 1 + .../qmleditor/qmlexpressionundercursor.h | 1 - src/plugins/qmleditor/qmlhoverhandler.cpp | 133 +++++++++++++++--- src/plugins/qmleditor/qmlhoverhandler.h | 10 ++ src/plugins/qmleditor/qmllookupcontext.cpp | 26 +++- src/plugins/qmleditor/qmllookupcontext.h | 1 + src/plugins/qmleditor/qmlsymbol.cpp | 1 + src/plugins/qmleditor/qmlsymbol.h | 4 +- 8 files changed, 154 insertions(+), 23 deletions(-) diff --git a/src/plugins/qmleditor/qmleditor.pro b/src/plugins/qmleditor/qmleditor.pro index 5f6a5a9642b..3dfa9367bc9 100644 --- a/src/plugins/qmleditor/qmleditor.pro +++ b/src/plugins/qmleditor/qmleditor.pro @@ -4,6 +4,7 @@ include(../../qtcreatorplugin.pri) include(qmleditor_dependencies.pri) include(parser/parser.pri) include(rewriter/rewriter.pri) +CONFIG += help DEFINES += QMLEDITOR_LIBRARY \ QT_CREATOR INCLUDEPATH += parser \ diff --git a/src/plugins/qmleditor/qmlexpressionundercursor.h b/src/plugins/qmleditor/qmlexpressionundercursor.h index 144edde2ced..28e8d87d64f 100644 --- a/src/plugins/qmleditor/qmlexpressionundercursor.h +++ b/src/plugins/qmleditor/qmlexpressionundercursor.h @@ -40,7 +40,6 @@ public: private: void parseExpression(const QTextBlock &block); - QmlJS::AST::ExpressionNode *tryExpression(const QString &text); QmlJS::AST::Statement *tryStatement(const QString &text); QmlJS::AST::UiObjectMember *tryBinding(const QString &text); diff --git a/src/plugins/qmleditor/qmlhoverhandler.cpp b/src/plugins/qmleditor/qmlhoverhandler.cpp index 890a4167fec..18eb5ab9df0 100644 --- a/src/plugins/qmleditor/qmlhoverhandler.cpp +++ b/src/plugins/qmleditor/qmlhoverhandler.cpp @@ -27,8 +27,12 @@ ** **************************************************************************/ -#include "qmlhoverhandler.h" #include "qmleditor.h" +#include "qmlexpressionundercursor.h" +#include "qmlhoverhandler.h" +#include "qmllookupcontext.h" +#include "qmlresolveexpression.h" +#include "qmlsymbol.h" #include <coreplugin/icore.h> #include <coreplugin/uniqueidmanager.h> @@ -38,7 +42,6 @@ #include <texteditor/basetexteditor.h> #include <debugger/debuggerconstants.h> -#include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QFileInfo> #include <QtCore/QSettings> @@ -47,13 +50,30 @@ #include <QtGui/QTextBlock> #include <QtHelp/QHelpEngineCore> -using namespace QmlEditor::Internal; using namespace Core; +using namespace QmlEditor; +using namespace QmlEditor::Internal; QmlHoverHandler::QmlHoverHandler(QObject *parent) : QObject(parent) + , m_helpEngineNeedsSetup(false) { + m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<QmlModelManagerInterface>(); + ICore *core = ICore::instance(); + QFileInfo fi(core->settings()->fileName()); + // FIXME shouldn't the help engine create the directory if it doesn't exist? + QDir directory(fi.absolutePath()+"/qtcreator"); + if (!directory.exists()) + directory.mkpath(directory.absolutePath()); + + m_helpEngine = new QHelpEngineCore(directory.absolutePath() + + QLatin1String("/helpcollection.qhc"), this); + //m_helpEngine->setAutoSaveFilter(false); + if (!m_helpEngine->setupData()) + qWarning() << "Could not initialize help engine:" << m_helpEngine->error(); + m_helpEngine->setCurrentFilter(tr("Unfiltered")); + m_helpEngineNeedsSetup = m_helpEngine->registeredDocumentations().count() == 0; // Listen for editor opened events in order to connect to tooltip/helpid requests connect(core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), @@ -78,26 +98,13 @@ void QmlHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint if (! editor) return; - ScriptEditor *ed = qobject_cast<ScriptEditor *>(editor->widget()); - ICore *core = ICore::instance(); const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_GDBDEBUGGER); if (core->hasContext(dbgcontext)) return; - m_toolTip.clear(); - - QTextCursor tc = ed->textCursor(); - tc.setPosition(pos); - const unsigned line = tc.block().blockNumber() + 1; - - foreach (const QmlJS::DiagnosticMessage &m, ed->diagnosticMessages()) { - if (m.loc.startLine == line) { - m_toolTip.append(m.message); - break; - } - } + updateHelpIdAndTooltip(editor, pos); if (m_toolTip.isEmpty()) QToolTip::hideText(); @@ -114,7 +121,97 @@ void QmlHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint } } -void QmlHoverHandler::updateContextHelpId(TextEditor::ITextEditor *, int) +void QmlHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos) +{ + updateHelpIdAndTooltip(editor, pos); +} + +static QString buildHelpId(QmlSymbol *symbol) { + if (!symbol) + return QString(); + + const QString idTemplate(QLatin1String("QML %1 Element Reference")); + + return idTemplate.arg(symbol->name()); } +void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos) +{ + m_helpId.clear(); + m_toolTip.clear(); + + if (!m_modelManager) + return; + + ScriptEditor *scriptEditor = qobject_cast<ScriptEditor *>(editor->widget()); + if (!scriptEditor) + return; + + const Snapshot documents = m_modelManager->snapshot(); + const QString fileName = editor->file()->fileName(); + QmlDocument::Ptr doc = documents.value(fileName); + if (!doc) + return; // nothing to do + + QTextCursor tc(scriptEditor->document()); + tc.setPosition(pos); + const unsigned lineNumber = tc.block().blockNumber() + 1; + + // We only want to show F1 if the tooltip matches the help id + bool showF1 = true; + + foreach (const QmlJS::DiagnosticMessage &m, scriptEditor->diagnosticMessages()) { + if (m.loc.startLine == lineNumber) { + m_toolTip = m.message; + showF1 = false; + break; + } + } + + if (m_helpId.isEmpty()) { + // Move to the end of a qualified name + bool stop = false; + while (!stop) { + const QChar ch = editor->characterAt(tc.position()); + if (ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.')) { + tc.setPosition(tc.position() + 1); + } else { + stop = true; + } + } + + // Fetch the expression's code + QmlExpressionUnderCursor expressionUnderCursor; + expressionUnderCursor(tc, doc); + + QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, m_modelManager->snapshot()); + QmlResolveExpression resolver(context); + QmlSymbol *resolvedSymbol = resolver.typeOf(expressionUnderCursor.expressionNode()); + + if (resolvedSymbol) { + m_helpId = buildHelpId(resolvedSymbol); + } + } + + if (m_helpEngineNeedsSetup && m_helpEngine->registeredDocumentations().count() > 0) { + m_helpEngine->setupData(); + m_helpEngineNeedsSetup = false; + } + + if (!m_toolTip.isEmpty()) + m_toolTip = Qt::escape(m_toolTip); + + if (!m_helpId.isEmpty() && !m_helpEngine->linksForIdentifier(m_helpId).isEmpty()) { + if (showF1) { + m_toolTip = QString(QLatin1String("<table><tr><td valign=middle><nobr>%1</td>" + "<td><img src=\":/cppeditor/images/f1.svg\"></td></tr></table>")) + .arg(m_toolTip); + } + editor->setContextHelpId(m_helpId); + } else if (!m_toolTip.isEmpty()) { + m_toolTip = QString(QLatin1String("<nobr>%1")).arg(m_toolTip); + } else if (!m_helpId.isEmpty()) { + m_toolTip = QString(QLatin1String("<nobr>No help available for \"%1\"")).arg(m_helpId); + } +} diff --git a/src/plugins/qmleditor/qmlhoverhandler.h b/src/plugins/qmleditor/qmlhoverhandler.h index 13cca4a57a0..b2050702e42 100644 --- a/src/plugins/qmleditor/qmlhoverhandler.h +++ b/src/plugins/qmleditor/qmlhoverhandler.h @@ -30,9 +30,12 @@ #ifndef QMLHOVERHANDLER_H #define QMLHOVERHANDLER_H +#include "qmlmodelmanagerinterface.h" + #include <QtCore/QObject> QT_BEGIN_NAMESPACE +class QHelpEngineCore; class QPoint; QT_END_NAMESPACE @@ -62,7 +65,14 @@ private slots: void editorOpened(Core::IEditor *editor); private: + void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos); + +private: + QmlModelManagerInterface *m_modelManager; + QHelpEngineCore *m_helpEngine; + QString m_helpId; QString m_toolTip; + bool m_helpEngineNeedsSetup; }; } // namespace Internal diff --git a/src/plugins/qmleditor/qmllookupcontext.cpp b/src/plugins/qmleditor/qmllookupcontext.cpp index abfca64db86..f846f19403c 100644 --- a/src/plugins/qmleditor/qmllookupcontext.cpp +++ b/src/plugins/qmleditor/qmllookupcontext.cpp @@ -1,3 +1,4 @@ +#include <QDebug> #include "qmljsast_p.h" #include "qmljsengine_p.h" @@ -70,7 +71,7 @@ QmlSymbol *QmlLookupContext::resolve(const QString &name) if (ids.contains(name)) return ids[name]; else - return 0; + return resolveType(name); } QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fileName) @@ -106,7 +107,28 @@ QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fil } } - return 0; + return resolveBuildinType(name); +} + +QmlSymbol *QmlLookupContext::resolveBuildinType(const QString &name) +{ + // FIXME: use a mete-type system here! + + if (name == "Rectangle") { + QmlBuildInSymbol *rectSymbol = new QmlBuildInSymbol(name); + rectSymbol->addMember(new QmlBuildInSymbol("x")); + rectSymbol->addMember(new QmlBuildInSymbol("y")); + rectSymbol->addMember(new QmlBuildInSymbol("height")); + rectSymbol->addMember(new QmlBuildInSymbol("width")); + return rectSymbol; + } else if (name == "Item") { + return new QmlBuildInSymbol(name); + } else if (name == "Text") { + QmlBuildInSymbol *textSymbol = new QmlBuildInSymbol(name); + return textSymbol; + } else { + return 0; + } } QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName) diff --git a/src/plugins/qmleditor/qmllookupcontext.h b/src/plugins/qmleditor/qmllookupcontext.h index d18e0ec5ec9..d1c64dc7112 100644 --- a/src/plugins/qmleditor/qmllookupcontext.h +++ b/src/plugins/qmleditor/qmllookupcontext.h @@ -32,6 +32,7 @@ public: private: QmlSymbol *resolveType(const QString &name, const QString &fileName); QmlSymbol *resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName); + QmlSymbol *resolveBuildinType(const QString &name); static QString toString(QmlJS::AST::UiQualifiedId *id); diff --git a/src/plugins/qmleditor/qmlsymbol.cpp b/src/plugins/qmleditor/qmlsymbol.cpp index 6eacae86450..afd0f7e719d 100644 --- a/src/plugins/qmleditor/qmlsymbol.cpp +++ b/src/plugins/qmleditor/qmlsymbol.cpp @@ -44,6 +44,7 @@ QmlBuildInSymbol::~QmlBuildInSymbol() QmlBuildInSymbol *QmlBuildInSymbol::asBuildInSymbol() { return this; } + QmlSymbolFromFile::QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node): _fileName(fileName), _node(node) diff --git a/src/plugins/qmleditor/qmlsymbol.h b/src/plugins/qmleditor/qmlsymbol.h index 8957db80c37..788341e104c 100644 --- a/src/plugins/qmleditor/qmlsymbol.h +++ b/src/plugins/qmleditor/qmlsymbol.h @@ -44,8 +44,8 @@ public: virtual const QString name() const { return _name; } - // TODO: -// virtual const List members(); + void addMember(QmlBuildInSymbol *symbol) + { _members.append(symbol); } private: QString _name; -- GitLab