Files
qt-creator/src/plugins/cppeditor/cpphoverhandler.cpp

332 lines
11 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
2010-03-05 11:25:49 +01:00
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Nokia Corporation (qt-info@nokia.com)
2008-12-02 12:01:29 +01:00
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
2009-08-14 09:30:56 +02:00
** contact the sales department at http://qt.nokia.com/contact.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 15:08:31 +01:00
2008-12-02 12:01:29 +01:00
#include "cpphoverhandler.h"
#include "cppeditor.h"
#include "cppplugin.h"
2008-12-02 12:01:29 +01:00
#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <extensionsystem/pluginmanager.h>
2008-12-02 12:01:29 +01:00
#include <texteditor/itexteditor.h>
2008-12-09 15:23:47 +01:00
#include <texteditor/basetexteditor.h>
2008-12-02 12:01:29 +01:00
#include <debugger/debuggerconstants.h>
#include <CoreTypes.h>
#include <FullySpecifiedType.h>
#include <Literals.h>
#include <Control.h>
2008-12-02 12:01:29 +01:00
#include <Names.h>
#include <Scope.h>
#include <Symbol.h>
#include <Symbols.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
2008-12-18 10:52:29 +01:00
#include <cplusplus/SimpleLexer.h>
2008-12-02 12:01:29 +01:00
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
2008-12-02 12:01:29 +01:00
#include <QtGui/QToolTip>
#include <QtGui/QTextCursor>
#include <QtGui/QTextBlock>
using namespace CppEditor::Internal;
2008-12-09 15:23:47 +01:00
using namespace CPlusPlus;
using namespace Core;
2008-12-02 12:01:29 +01:00
CppHoverHandler::CppHoverHandler(QObject *parent)
: QObject(parent)
2008-12-02 12:01:29 +01:00
{
m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
// Listen for editor opened events in order to connect to tooltip/helpid requests
connect(ICore::instance()->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
this, SLOT(editorOpened(Core::IEditor *)));
2008-12-02 12:01:29 +01:00
}
void CppHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos)
{
updateHelpIdAndTooltip(editor, pos);
}
void CppHoverHandler::editorOpened(IEditor *editor)
2008-12-02 12:01:29 +01:00
{
CPPEditorEditable *cppEditor = qobject_cast<CPPEditorEditable *>(editor);
if (!cppEditor)
return;
connect(cppEditor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*, QPoint, int)),
this, SLOT(showToolTip(TextEditor::ITextEditor*, QPoint, int)));
connect(cppEditor, SIGNAL(contextHelpIdRequested(TextEditor::ITextEditor*, int)),
this, SLOT(updateContextHelpId(TextEditor::ITextEditor*, int)));
}
2008-12-02 12:01:29 +01:00
void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos)
{
if (!editor)
2008-12-02 12:01:29 +01:00
return;
ICore *core = ICore::instance();
const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE);
if (core->hasContext(dbgcontext))
2008-12-02 12:01:29 +01:00
return;
updateHelpIdAndTooltip(editor, pos);
if (m_toolTip.isEmpty())
QToolTip::hideText();
else {
const QPoint pnt = point - QPoint(0,
#ifdef Q_WS_WIN
24
#else
16
#endif
);
QToolTip::showText(pnt, m_toolTip);
}
}
2010-06-04 09:59:38 +02:00
static QString buildHelpId(Symbol *symbol, const Name *declarationName)
2008-12-02 12:01:29 +01:00
{
Scope *scope = 0;
if (symbol) {
scope = symbol->scope();
2010-06-04 09:59:38 +02:00
declarationName = symbol->name();
2008-12-02 12:01:29 +01:00
}
2010-06-04 09:59:38 +02:00
if (! declarationName)
2009-02-11 10:01:36 +01:00
return QString();
2008-12-02 12:01:29 +01:00
Overview overview;
overview.setShowArgumentNames(false);
overview.setShowReturnTypes(false);
QStringList qualifiedNames;
2010-06-04 09:59:38 +02:00
qualifiedNames.prepend(overview.prettyName(declarationName));
2008-12-02 12:01:29 +01:00
for (; scope; scope = scope->enclosingScope()) {
Symbol *owner = scope->owner();
if (owner && owner->name() && ! scope->isEnumScope()) {
2009-12-01 12:46:15 +01:00
const Name *name = owner->name();
2009-12-01 11:33:13 +01:00
const Identifier *id = 0;
2009-12-01 12:46:15 +01:00
if (const NameId *nameId = name->asNameId())
2008-12-02 12:01:29 +01:00
id = nameId->identifier();
2009-12-01 12:46:15 +01:00
else if (const TemplateNameId *nameId = name->asTemplateNameId())
2008-12-02 12:01:29 +01:00
id = nameId->identifier();
2008-12-02 12:01:29 +01:00
if (id)
qualifiedNames.prepend(QString::fromLatin1(id->chars(), id->size()));
}
}
return qualifiedNames.join(QLatin1String("::"));
}
void CppHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos)
{
m_helpId.clear();
m_toolTip.clear();
if (!m_modelManager)
return;
2008-12-09 15:23:47 +01:00
TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
2008-12-02 12:01:29 +01:00
if (!edit)
return;
2010-05-14 09:52:53 +02:00
const Snapshot snapshot = m_modelManager->snapshot();
2008-12-18 10:52:29 +01:00
const QString fileName = editor->file()->fileName();
2010-05-14 09:52:53 +02:00
Document::Ptr doc = snapshot.document(fileName);
2009-06-12 13:55:08 +02:00
if (!doc)
2008-12-18 10:52:29 +01:00
return; // nothing to do
QString formatTooltip = edit->extraSelectionTooltip(pos);
2008-12-02 12:01:29 +01:00
QTextCursor tc(edit->document());
tc.setPosition(pos);
2008-12-18 10:52:29 +01:00
const unsigned lineNumber = tc.block().blockNumber() + 1;
2008-12-02 12:01:29 +01:00
2008-12-18 10:52:29 +01:00
// Find the last symbol up to the cursor position
int line = 0, column = 0;
editor->convertPosition(tc.position(), &line, &column);
Scope *scope = doc->scopeAt(line, column);
2008-12-18 10:52:29 +01:00
TypeOfExpression typeOfExpression;
2010-05-14 09:52:53 +02:00
typeOfExpression.init(doc, snapshot);
2008-12-18 10:52:29 +01:00
// We only want to show F1 if the tooltip matches the help id
2009-07-22 16:10:58 +02:00
bool showF1 = true;
foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) {
2008-12-18 10:52:29 +01:00
if (m.line() == lineNumber) {
m_toolTip = m.text();
2009-07-22 16:10:58 +02:00
showF1 = false;
2008-12-18 10:52:29 +01:00
break;
2008-12-02 12:01:29 +01:00
}
2008-12-18 10:52:29 +01:00
}
2008-12-02 12:01:29 +01:00
QMap<QString, QUrl> helpLinks;
2008-12-18 10:52:29 +01:00
if (m_toolTip.isEmpty()) {
foreach (const Document::Include &incl, doc->includes()) {
if (incl.line() == lineNumber) {
m_toolTip = QDir::toNativeSeparators(incl.fileName());
m_helpId = QFileInfo(incl.fileName()).fileName();
helpLinks = Core::HelpManager::instance()->linksForIdentifier(m_helpId);
2008-12-18 10:52:29 +01:00
break;
}
}
}
if (m_helpId.isEmpty()) {
2008-12-02 12:01:29 +01:00
// 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('_'))
tc.setPosition(tc.position() + 1);
else if (ch == QLatin1Char(':') && editor->characterAt(tc.position() + 1) == QLatin1Char(':')) {
tc.setPosition(tc.position() + 2);
} else {
stop = true;
}
}
// Fetch the expression's code
2010-06-29 17:47:59 +02:00
ExpressionUnderCursor expressionUnderCursor;
2008-12-02 12:01:29 +01:00
const QString expression = expressionUnderCursor(tc);
const QList<LookupItem> types = typeOfExpression(expression, scope);
2008-12-18 10:52:29 +01:00
2010-05-14 09:52:53 +02:00
if (!types.isEmpty()) {
Overview overview;
overview.setShowArgumentNames(true);
overview.setShowReturnTypes(true);
overview.setShowFullyQualifiedNamed(true);
const LookupItem result = types.first(); // ### TODO: select the best candidate.
FullySpecifiedType symbolTy = result.type(); // result of `type of expression'.
Symbol *declaration = result.declaration(); // lookup symbol
const Name *declarationName = declaration ? declaration->name() : 0;
if (declaration && declaration->scope()
&& declaration->scope()->isClassScope()) {
Class *enclosingClass = declaration->scope()->owner()->asClass();
2009-12-01 11:33:13 +01:00
if (const Identifier *id = enclosingClass->identifier()) {
2010-05-14 09:52:53 +02:00
if (id->isEqualTo(declaration->identifier()))
declaration = enclosingClass;
}
}
2010-05-14 09:52:53 +02:00
m_helpId = buildHelpId(declaration, declarationName);
if (m_toolTip.isEmpty()) {
2010-05-14 09:52:53 +02:00
Symbol *symbol = declaration;
2010-05-14 09:52:53 +02:00
if (declaration)
symbol = declaration;
2010-05-14 09:52:53 +02:00
if (symbol && symbol == declaration && symbol->isClass()) {
m_toolTip = m_helpId;
2010-05-14 09:52:53 +02:00
} else if (declaration && (declaration->isDeclaration() || declaration->isArgument())) {
m_toolTip = overview.prettyType(symbolTy, buildHelpId(declaration, declaration->name()));
2010-05-14 09:52:53 +02:00
} else if (symbolTy->isClassType() || symbolTy->isEnumType() ||
symbolTy->isForwardClassDeclarationType()) {
m_toolTip = m_helpId;
} else {
2010-05-14 09:52:53 +02:00
m_toolTip = overview.prettyType(symbolTy, m_helpId);
}
2008-12-02 12:01:29 +01:00
}
// Some docs don't contain the namespace in the documentation pages, for instance
// there is QtMobility::QContactManager but the help page is for QContactManager.
// To show their help anyway, try stripping scopes until we find something.
const QString startHelpId = m_helpId;
while (!m_helpId.isEmpty()) {
helpLinks = Core::HelpManager::instance()->linksForIdentifier(m_helpId);
if (!helpLinks.isEmpty())
break;
int coloncolonIndex = m_helpId.indexOf(QLatin1String("::"));
if (coloncolonIndex == -1) {
m_helpId = startHelpId;
break;
}
m_helpId.remove(0, coloncolonIndex + 2);
}
2008-12-02 12:01:29 +01:00
}
}
2008-12-18 10:52:29 +01:00
if (m_toolTip.isEmpty()) {
foreach (const Document::MacroUse &use, doc->macroUses()) {
if (use.contains(pos)) {
const Macro m = use.macro();
m_toolTip = m.toString();
m_helpId = m.name();
break;
}
}
}
if (!formatTooltip.isEmpty())
m_toolTip = formatTooltip;
2008-12-18 10:52:29 +01:00
if (!m_helpId.isEmpty() && !helpLinks.isEmpty()) {
2009-07-22 16:10:58 +02:00
if (showF1) {
// we need the original width without escape sequences
const int width = QFontMetrics(QToolTip::font()).width(m_toolTip);
m_toolTip = QString(QLatin1String("<table><tr><td valign=middle width=%2>%1</td>"
2010-03-05 14:30:08 +01:00
"<td><img src=\":/cppeditor/images/f1.png\"></td></tr></table>"))
.arg(Qt::escape(m_toolTip)).arg(width);
}
2008-12-02 12:01:29 +01:00
editor->setContextHelpId(m_helpId);
} else if (!m_toolTip.isEmpty() && Qt::mightBeRichText(m_toolTip)) {
m_toolTip = QString(QLatin1String("<nobr>%1</nobr>")).arg(Qt::escape(m_toolTip));
2008-12-02 12:01:29 +01:00
}
}