Commit 0e3aa47f authored by Friedemann Kleint's avatar Friedemann Kleint

Added ToolTip support for CDB.

Make debugger tooltip API more general, have the engines check the
correct file type, figure out the expression and context from the text
editor.

Put common functionality in watchutils.cpp. In the CDB engine, check
whether a tooltip expression is a known variable within the stack frame
context. If so, retrieve via symbol group or dumpers. Cache by function
and expression.
Reviewed-by: default avatarhjk <qtc-committer@nokia.com>
parent 11ccb571
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** 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
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/
#include "cppmodelmanagerinterface.h"
#include <cplusplus/Overview.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <CoreTypes.h>
#include <Names.h>
#include <Symbols.h>
#include <Scope.h>
namespace CppTools {
AbstractEditorSupport::AbstractEditorSupport(CppModelManagerInterface *modelmanager) :
m_modelmanager(modelmanager)
{
}
AbstractEditorSupport::~AbstractEditorSupport()
{
}
void AbstractEditorSupport::updateDocument()
{
m_modelmanager->updateSourceFiles(QStringList(fileName()));
}
QString AbstractEditorSupport::functionAt(const CppModelManagerInterface *modelManager,
const QString &fileName,
int line, int column)
{
const CPlusPlus::Snapshot snapshot = modelManager->snapshot();
const CPlusPlus::Document::Ptr document = snapshot.value(fileName);
if (!document)
return QString();
if (const CPlusPlus::Symbol *symbol = document->findSymbolAt(line, column))
if (const CPlusPlus::Scope *scope = symbol->scope())
if (const CPlusPlus::Scope *functionScope = scope->enclosingFunctionScope())
if (const CPlusPlus::Symbol *function = functionScope->owner()) {
const CPlusPlus::Overview o;
return o.prettyName(function->name());
}
return QString();
}
}
......@@ -97,14 +97,18 @@ public:
class CPPTOOLS_EXPORT AbstractEditorSupport
{
public:
AbstractEditorSupport(CppModelManagerInterface *modelmanager)
: m_modelmanager(modelmanager) {}
virtual ~AbstractEditorSupport() {}
explicit AbstractEditorSupport(CppModelManagerInterface *modelmanager);
virtual ~AbstractEditorSupport();
virtual QByteArray contents() const = 0;
virtual QString fileName() const = 0;
void updateDocument()
{ m_modelmanager->updateSourceFiles(QStringList() << fileName()); }
void updateDocument();
// TODO: find a better place for common utility functions
static QString functionAt(const CppModelManagerInterface *mm,
const QString &fileName,
int line, int column);
private:
CppModelManagerInterface *m_modelmanager;
......
......@@ -33,7 +33,8 @@ SOURCES += completionsettingspage.cpp \
cpptoolsplugin.cpp \
searchsymbols.cpp \
cppdoxygen.cpp \
cppfilesettingspage.cpp
cppfilesettingspage.cpp \
abstracteditorsupport.cpp
FORMS += completionsettingspage.ui \
cppfilesettingspage.ui
......
......@@ -52,6 +52,7 @@
#include <utils/qtcassert.h>
#include <utils/winutils.h>
#include <utils/consoleprocess.h>
#include <texteditor/itexteditor.h>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
......@@ -63,6 +64,7 @@
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
#include <QtGui/QApplication>
#include <QtGui/QToolTip>
#define DBGHELP_TRANSLATE_TCHAR
#include <inc/Dbghelp.h>
......@@ -380,6 +382,7 @@ void CdbDebugEnginePrivate::clearForRun()
m_breakEventMode = BreakEventHandle;
m_firstActivatedFrame = false;
cleanStackTrace();
m_editorToolTipCache.clear();
}
void CdbDebugEnginePrivate::cleanStackTrace()
......@@ -438,10 +441,66 @@ void CdbDebugEngine::shutdown()
exitDebugger();
}
void CdbDebugEngine::setToolTipExpression(const QPoint & pos, const QString & exp)
QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &function)
{
// Figure the editor tooltip. Ask the frame context of the
// function if it is a local variable it knows. If that is not
// the case, try to evaluate via debugger
QString errorMessage;
QString rc;
// Find the frame of the function if there is any
CdbStackFrameContext *frame = 0;
if (m_d->m_currentStackTrace && !function.isEmpty()) {
const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
if (frameIndex != -1)
frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
}
if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
return rc;
// No function/symbol context found, try to evaluate in current context.
// Do not append type as this will mostly be 'long long' for integers, etc.
QString type;
if (!evaluateExpression(exp, &rc, &type, &errorMessage))
return QString();
return rc;
}
void CdbDebugEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
typedef CdbDebugEnginePrivate::EditorToolTipCache EditorToolTipCache;
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << pos << exp;
qDebug() << Q_FUNC_INFO << '\n' << cursorPos;
// Need a stopped debuggee and a cpp file
if (!m_d->m_hDebuggeeProcess || m_d->isDebuggeeRunning())
return;
if (!isCppEditor(editor))
return;
// Determine expression and function
QString toolTip;
do {
int line;
int column;
QString function;
const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
if (function.isEmpty() || exp.isEmpty())
break;
// Check cache (key containing function) or try to figure out expression
QString cacheKey = function;
cacheKey += QLatin1Char('@');
cacheKey += exp;
const EditorToolTipCache::const_iterator cit = m_d->m_editorToolTipCache.constFind(cacheKey);
if (cit != m_d->m_editorToolTipCache.constEnd()) {
toolTip = cit.value();
} else {
toolTip = editorToolTip(exp, function);
if (!toolTip.isEmpty())
m_d->m_editorToolTipCache.insert(cacheKey, toolTip);
}
} while (false);
// Display
QToolTip::hideText();
if (!toolTip.isEmpty())
QToolTip::showText(mousePos, toolTip);
}
void CdbDebugEnginePrivate::clearDisplay()
......
......@@ -60,7 +60,7 @@ public:
QString *errorMessage);
virtual void shutdown();
virtual void setToolTipExpression(const QPoint &pos, const QString &exp);
virtual void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
virtual bool startDebugger();
virtual void exitDebugger();
virtual void detachDebugger();
......@@ -118,6 +118,7 @@ private:
bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
void evaluateWatcher(WatchData *wd);
void filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler *wh);
QString editorToolTip(const QString &exp, const QString &function);
CdbDebugEnginePrivate *m_d;
......
......@@ -39,6 +39,7 @@
#include <utils/consoleprocess.h>
#include <QtCore/QSharedPointer>
#include <QtCore/QMap>
namespace Debugger {
namespace Internal {
......@@ -94,7 +95,9 @@ struct CdbComInterfaces
};
struct CdbDebugEnginePrivate
{
{
typedef QMap<QString, QString> EditorToolTipCache;
enum HandleBreakEventMode { // Special modes for break event handler.
BreakEventHandle,
BreakEventIgnoreOnce,
......@@ -160,6 +163,8 @@ struct CdbDebugEnginePrivate
DebuggerManager *m_debuggerManager;
IDebuggerManagerAccessForEngines *m_debuggerManagerAccess;
CdbStackTraceContext *m_currentStackTrace;
EditorToolTipCache m_editorToolTipCache;
bool m_firstActivatedFrame;
DebuggerStartMode m_mode;
......
......@@ -186,5 +186,32 @@ CdbStackFrameContext::~CdbStackFrameContext()
delete m_symbolContext;
}
bool CdbStackFrameContext::editorToolTip(const QString &iname,
QString *value,
QString *errorMessage)
{
value->clear();
unsigned long index;
if (!m_symbolContext->lookupPrefix(iname, &index)) {
*errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
return false;
}
const WatchData wd = m_symbolContext->symbolAt(index);
// Check dumpers. Should actually be just one item.
if (m_useDumpers && m_dumper->state() != CdbDumperHelper::Disabled) {
QList<WatchData> result;
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, OwnerDumper, &result, errorMessage)) {
foreach (const WatchData &dwd, result) {
if (!value->isEmpty())
value->append(QLatin1Char('\n'));
value->append(dwd.toToolTip());
}
return true;
} // Dumped ok
} // has Dumpers
*value = wd.toToolTip();
return true;
}
} // namespace Internal
} // namespace Debugger
......@@ -51,10 +51,11 @@ class CdbStackFrameContext
public:
explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
CdbSymbolGroupContext *symbolContext);
~CdbStackFrameContext();
~CdbStackFrameContext();
bool assignValue(const QString &iname, const QString &value,
QString *newValue /* = 0 */, QString *errorMessage);
bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
......
......@@ -118,6 +118,27 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
return true;
}
int CdbStackTraceContext::indexOf(const QString &function) const
{
const QChar exclamationMark = QLatin1Char('!');
const int count = m_frames.size();
// Module contained ('module!foo'). Exact match
if (function.contains(exclamationMark)) {
for (int i = 0; i < count; i++)
if (m_frames.at(i).function == function)
return i;
return -1;
}
// No module, fuzzy match
QString pattern = exclamationMark + function;
for (int i = 0; i < count; i++)
if (m_frames.at(i).function.endsWith(pattern))
return i;
return -1;
}
CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
{
// Create a frame on demand
......
......@@ -69,6 +69,8 @@ public:
QList<StackFrame> frames() const { return m_frames; }
inline int frameCount() const { return m_frames.size(); }
// Search for function. Should ideally contain the module as 'module!foo'.
int indexOf(const QString &function) const;
// Top-Level instruction offset for disassembler
ULONG64 instructionOffset() const { return m_instructionOffset; }
......
......@@ -89,6 +89,9 @@ public:
template <class OutputIterator>
bool getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage);
WatchData symbolAt(unsigned long index) const;
bool lookupPrefix(const QString &prefix, unsigned long *index) const;
enum SymbolState { LeafSymbol, ExpandedSymbol, CollapsedSymbol };
SymbolState symbolState(unsigned long index) const;
SymbolState symbolState(const QString &prefix) const;
......@@ -116,10 +119,8 @@ private:
unsigned long *parentId,
QString *errorMessage);
bool expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage);
void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long start, unsigned long count);
WatchData symbolAt(unsigned long index) const;
QString symbolINameAt(unsigned long index) const;
bool lookupPrefix(const QString &prefix, unsigned long *index) const;
void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long start, unsigned long count);
QString symbolINameAt(unsigned long index) const;
int getDisplayableChildCount(unsigned long index) const;
inline DEBUG_SYMBOL_PARAMETERS *symbolParameters() { return &(*m_symbolParameters.begin()); }
......
......@@ -719,10 +719,10 @@ void DebuggerManager::attemptBreakpointSynchronization()
m_engine->attemptBreakpointSynchronization();
}
void DebuggerManager::setToolTipExpression(const QPoint &pos, const QString &exp)
void DebuggerManager::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
if (m_engine)
m_engine->setToolTipExpression(pos, exp);
m_engine->setToolTipExpression(mousePos, editor, cursorPos);
}
void DebuggerManager::updateWatchModel()
......
......@@ -50,6 +50,10 @@ namespace Core {
class IOptionsPage;
} // namespace Core
namespace TextEditor {
class ITextEditor;
}
namespace Debugger {
namespace Internal {
......@@ -379,7 +383,7 @@ private:
void toggleBreakpoint(const QString &fileName, int lineNumber);
void toggleBreakpointEnabled(const QString &fileName, int lineNumber);
BreakpointData *findBreakpoint(const QString &fileName, int lineNumber);
void setToolTipExpression(const QPoint &pos, const QString &exp0);
void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
DebuggerRunControl *m_runControl;
......
......@@ -948,28 +948,10 @@ void DebuggerPlugin::requestMark(TextEditor::ITextEditor *editor, int lineNumber
void DebuggerPlugin::showToolTip(TextEditor::ITextEditor *editor,
const QPoint &point, int pos)
{
if (!theDebuggerBoolSetting(UseToolTips))
if (!theDebuggerBoolSetting(UseToolTips) || m_manager->status() == DebuggerProcessNotReady)
return;
QPlainTextEdit *plaintext = qobject_cast<QPlainTextEdit*>(editor->widget());
if (!plaintext)
return;
QString expr = plaintext->textCursor().selectedText();
if (expr.isEmpty()) {
QTextCursor tc(plaintext->document());
tc.setPosition(pos);
const QChar ch = editor->characterAt(pos);
if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
tc.movePosition(QTextCursor::EndOfWord);
// Fetch the expression's code.
CPlusPlus::ExpressionUnderCursor expressionUnderCursor;
expr = expressionUnderCursor(tc);
}
//qDebug() << " TOOLTIP EXPR " << expr;
m_manager->setToolTipExpression(point, expr);
m_manager->setToolTipExpression(point, editor, pos);
}
void DebuggerPlugin::setSessionValue(const QString &name, const QVariant &value)
......
......@@ -50,6 +50,7 @@
#include "debuggerdialogs.h"
#include <utils/qtcassert.h>
#include <texteditor/itexteditor.h>
#include <coreplugin/icore.h>
#include <QtCore/QDebug>
......@@ -2605,11 +2606,10 @@ static QString m_toolTipExpression;
static QPoint m_toolTipPos;
static QMap<QString, WatchData> m_toolTipCache;
void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
void GdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
//qDebug() << "SET TOOLTIP EXP" << pos << exp0;
if (q->status() != DebuggerInferiorStopped) {
//qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
if (q->status() != DebuggerInferiorStopped || !isCppEditor(editor)) {
//qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED/Non Cpp editor";
return;
}
......@@ -2618,9 +2618,10 @@ void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
return;
}
m_toolTipPos = pos;
m_toolTipExpression = exp0;
QString exp = exp0;
m_toolTipPos = mousePos;
int line, column;
m_toolTipExpression = cppExpressionAt(editor, cursorPos, &line, &column);
QString exp = m_toolTipExpression;
/*
if (m_toolTip.isTypePending()) {
qDebug() << "suppressing duplicated tooltip creation";
......
......@@ -96,7 +96,7 @@ private:
void nextIExec();
void shutdown();
void setToolTipExpression(const QPoint &pos, const QString &exp);
void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
bool startDebugger();
void exitDebugger();
void detachDebugger();
......
......@@ -38,6 +38,10 @@ class QPoint;
class QString;
QT_END_NAMESPACE
namespace TextEditor {
class ITextEditor;
}
namespace Debugger {
namespace Internal {
......@@ -49,7 +53,7 @@ public:
IDebuggerEngine(QObject *parent = 0) : QObject(parent) {}
virtual void shutdown() = 0;
virtual void setToolTipExpression(const QPoint &pos, const QString &exp) = 0;
virtual void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos) = 0;
virtual bool startDebugger() = 0;
virtual void exitDebugger() = 0;
virtual void detachDebugger() {}
......
......@@ -43,6 +43,11 @@
#include <utils/qtcassert.h>
#include <qtscripteditor/qtscripteditorconstants.h>
#include <texteditor/itexteditor.h>
#include <coreplugin/ifile.h>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QDir>
......@@ -423,18 +428,24 @@ static WatchData m_toolTip;
static QPoint m_toolTipPos;
static QHash<QString, WatchData> m_toolTipCache;
void ScriptEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
void ScriptEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
Q_UNUSED(pos);
Q_UNUSED(exp0);
Q_UNUSED(mousePos);
Q_UNUSED(editor);
Q_UNUSED(cursorPos);
if (q->status() != DebuggerInferiorStopped) {
//SDEBUG("SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED");
return;
}
// Check mime type and get expression (borrowing some C++ - functions)
const QString javaScriptMimeType = QLatin1String(QtScriptEditor::Constants::C_QTSCRIPTEDITOR_MIMETYPE);
if (!editor->file() || editor->file()->mimeType() != javaScriptMimeType)
return;
//m_toolTipPos = pos;
QString exp = exp0;
int line;
int column;
QString exp = cppExpressionAt(editor, cursorPos, &line, &column);
/*
if (m_toolTipCache.contains(exp)) {
......
......@@ -75,7 +75,7 @@ private:
void nextIExec();
void shutdown();
void setToolTipExpression(const QPoint &pos, const QString &exp);
void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
bool startDebugger();
void exitDebugger();
......
......@@ -429,11 +429,13 @@ static WatchData m_toolTip;
static QPoint m_toolTipPos;
static QHash<QString, WatchData> m_toolTipCache;
void TcfEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
void TcfEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
Q_UNUSED(mousePos)
Q_UNUSED(editor)
Q_UNUSED(cursorPos)
}
//////////////////////////////////////////////////////////////////////
//
// Watch specific stuff
......
......@@ -85,7 +85,7 @@ private:
void nextIExec();
void shutdown();
void setToolTipExpression(const QPoint &pos, const QString &exp);
void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
bool startDebugger();
void exitDebugger();
......
......@@ -31,12 +31,27 @@
#include "watchhandler.h"
#include <utils/qtcassert.h>
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
#include <texteditor/itexteditor.h>
#include <texteditor/texteditorconstants.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <cpptools/cpptoolsconstants.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <extensionsystem/pluginmanager.h>
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QStringList>
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
#include <QtGui/QTextCursor>
#include <QtGui/QPlainTextEdit>
#include <string.h>
#include <ctype.h>
......@@ -365,6 +380,68 @@ QString decodeData(const QByteArray &ba, int encoding)
return QCoreApplication::translate("Debugger", "<Encoding error>");
}
// Editor tooltip support
bool isCppEditor(Core::IEditor *editor)
{
static QStringList cppMimeTypes;
if (cppMimeTypes.empty()) {
cppMimeTypes << QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)
<< QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)
<< QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)
<< QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
}
if (const Core::IFile *file = editor->file())
return cppMimeTypes.contains(file->mimeType());
return false;
}
// Find the function the cursor is in to use a scope.