summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhjk <hjk@qt.io>2021-01-18 16:30:38 +0100
committerhjk <hjk@qt.io>2021-01-20 09:45:03 +0000
commit79ade10c4afa5cf8db864f0ce4d446afa9e8f055 (patch)
tree0a76a17848674f04facdcc165938d85fd0b4f3aa
parent0ed99a954b0395c9f94264575fc1756b055fe6b4 (diff)
downloadqt-creator-79ade10c4afa5cf8db864f0ce4d446afa9e8f055.tar.gz
Debugger: Add an option to show simple values as text annotations
Change-Id: I726d8559d7e28abd776ce483d5f670be5af09412 Reviewed-by: André Hartmann <aha_1980@gmx.de> Reviewed-by: David Schulz <david.schulz@qt.io>
-rw-r--r--src/plugins/debugger/commonoptionspage.cpp20
-rw-r--r--src/plugins/debugger/debuggeractions.cpp9
-rw-r--r--src/plugins/debugger/debuggeractions.h1
-rw-r--r--src/plugins/debugger/debuggerengine.cpp1
-rw-r--r--src/plugins/debugger/debuggerinternalconstants.h1
-rw-r--r--src/plugins/debugger/sourceutils.cpp106
-rw-r--r--src/plugins/debugger/sourceutils.h4
-rw-r--r--src/plugins/debugger/watchdata.cpp26
-rw-r--r--src/plugins/debugger/watchdata.h1
-rw-r--r--src/plugins/debugger/watchhandler.cpp22
-rw-r--r--src/plugins/debugger/watchhandler.h2
11 files changed, 181 insertions, 12 deletions
diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp
index bb17ee5996..0169fca2c1 100644
--- a/src/plugins/debugger/commonoptionspage.cpp
+++ b/src/plugins/debugger/commonoptionspage.cpp
@@ -83,6 +83,9 @@ public:
auto checkBoxUseToolTipsInMainEditor = new QCheckBox(behaviorBox);
checkBoxUseToolTipsInMainEditor->setText(tr("Use tooltips in main editor while debugging"));
+ auto checkBoxUseAnnotationsInMainEditor = new QCheckBox(behaviorBox);
+ checkBoxUseAnnotationsInMainEditor->setText(tr("Use annotations in main editor while debugging"));
+
QString t = tr("Stopping and stepping in the debugger "
"will automatically open views associated with the current location.") + '\n';
auto checkBoxCloseSourceBuffersOnExit = new QCheckBox(behaviorBox);
@@ -146,13 +149,14 @@ public:
auto gridLayout = new QGridLayout(behaviorBox);
gridLayout->addWidget(checkBoxUseAlternatingRowColors, 0, 0, 1, 1);
- gridLayout->addWidget(checkBoxUseToolTipsInMainEditor, 1, 0, 1, 1);
- gridLayout->addWidget(checkBoxCloseSourceBuffersOnExit, 2, 0, 1, 1);
- gridLayout->addWidget(checkBoxCloseMemoryBuffersOnExit, 3, 0, 1, 1);
- gridLayout->addWidget(checkBoxBringToForegroundOnInterrrupt, 4, 0, 1, 1);
- gridLayout->addWidget(checkBoxBreakpointsFullPath, 5, 0, 1, 1);
- gridLayout->addWidget(checkBoxWarnOnReleaseBuilds, 6, 0, 1, 1);
- gridLayout->addLayout(horizontalLayout, 7, 0, 1, 2);
+ gridLayout->addWidget(checkBoxUseAnnotationsInMainEditor, 1, 0, 1, 1);
+ gridLayout->addWidget(checkBoxUseToolTipsInMainEditor, 2, 0, 1, 1);
+ gridLayout->addWidget(checkBoxCloseSourceBuffersOnExit, 3, 0, 1, 1);
+ gridLayout->addWidget(checkBoxCloseMemoryBuffersOnExit, 4, 0, 1, 1);
+ gridLayout->addWidget(checkBoxBringToForegroundOnInterrrupt, 5, 0, 1, 1);
+ gridLayout->addWidget(checkBoxBreakpointsFullPath, 6, 0, 1, 1);
+ gridLayout->addWidget(checkBoxWarnOnReleaseBuilds, 7, 0, 1, 1);
+ gridLayout->addLayout(horizontalLayout, 8, 0, 1, 2);
gridLayout->addWidget(checkBoxFontSizeFollowsEditor, 0, 1, 1, 1);
gridLayout->addWidget(checkBoxSwitchModeOnExit, 1, 1, 1, 1);
@@ -167,6 +171,8 @@ public:
m_group.insert(action(UseAlternatingRowColors),
checkBoxUseAlternatingRowColors);
+ m_group.insert(action(UseAnnotationsInMainEditor),
+ checkBoxUseAnnotationsInMainEditor);
m_group.insert(action(UseToolTipsInMainEditor),
checkBoxUseToolTipsInMainEditor);
m_group.insert(action(CloseSourceBuffersOnExit),
diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp
index 631bb88154..53e04570e7 100644
--- a/src/plugins/debugger/debuggeractions.cpp
+++ b/src/plugins/debugger/debuggeractions.cpp
@@ -498,6 +498,15 @@ DebuggerSettings::DebuggerSettings()
insertItem(IntelFlavor, item);
item = new SavedAction;
+ item->setSettingsKey(debugModeGroup, "UseAnnotations");
+ item->setText(tr("Use annotations in main editor when debugging"));
+ item->setToolTip(tr("<p>Checking this will show simple variable values "
+ "as annotations in the main editor during debugging."));
+ item->setCheckable(true);
+ item->setDefaultValue(true);
+ insertItem(UseAnnotationsInMainEditor, item);
+
+ item = new SavedAction;
item->setSettingsKey(debugModeGroup, "UseToolTips");
item->setText(tr("Use tooltips in main editor when debugging"));
item->setToolTip(tr("<p>Checking this will enable tooltips for variable "
diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h
index 30dbe7b9af..8137f4c400 100644
--- a/src/plugins/debugger/debuggeractions.h
+++ b/src/plugins/debugger/debuggeractions.h
@@ -98,6 +98,7 @@ enum DebuggerActionCode
ShowThreadNames,
UseToolTipsInMainEditor,
+ UseAnnotationsInMainEditor,
UseToolTipsInLocalsView,
UseToolTipsInBreakpointsView,
UseToolTipsInStackView,
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index c9cfd774cf..70e49c35e2 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -1113,6 +1113,7 @@ void DebuggerEngine::gotoLocation(const Location &loc)
}
d->m_breakHandler.setLocation(loc);
+ d->m_watchHandler.setLocation(loc);
}
void DebuggerEngine::gotoCurrentLocation()
diff --git a/src/plugins/debugger/debuggerinternalconstants.h b/src/plugins/debugger/debuggerinternalconstants.h
index 7137a9100c..908a02c5d8 100644
--- a/src/plugins/debugger/debuggerinternalconstants.h
+++ b/src/plugins/debugger/debuggerinternalconstants.h
@@ -68,6 +68,7 @@ const char TASK_CATEGORY_DEBUGGER_RUNTIME[] = "DebugRuntime";
const char TEXT_MARK_CATEGORY_BREAKPOINT[] = "Debugger.Mark.Breakpoint";
const char TEXT_MARK_CATEGORY_LOCATION[] = "Debugger.Mark.Location";
+const char TEXT_MARK_CATEGORY_VALUE[] = "Debugger.Mark.Value";
const char OPENED_BY_DEBUGGER[] = "OpenedByDebugger";
const char OPENED_WITH_DISASSEMBLY[] = "DisassemblerView";
diff --git a/src/plugins/debugger/sourceutils.cpp b/src/plugins/debugger/sourceutils.cpp
index a76317258c..fbb57679d6 100644
--- a/src/plugins/debugger/sourceutils.cpp
+++ b/src/plugins/debugger/sourceutils.cpp
@@ -31,15 +31,20 @@
#include "watchdata.h"
#include "watchutils.h"
-#include <texteditor/texteditor.h>
-#include <texteditor/textdocument.h>
-#include <cpptools/abstracteditorsupport.h>
-#include <cpptools/cppprojectfile.h>
-#include <cpptools/cppmodelmanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+
#include <cplusplus/CppDocument.h>
#include <cplusplus/ExpressionUnderCursor.h>
+#include <cplusplus/LookupItem.h>
#include <cplusplus/Overview.h>
+#include <cpptools/abstracteditorsupport.h>
+#include <cpptools/cppprojectfile.h>
+#include <cpptools/cppmodelmanager.h>
+
+#include <texteditor/texteditor.h>
+#include <texteditor/textdocument.h>
+
#include <utils/qtcassert.h>
#include <QDebug>
@@ -54,6 +59,7 @@ enum { debug = 0 };
using namespace CppTools;
using namespace CPlusPlus;
using namespace TextEditor;
+using namespace Utils;
namespace CPlusPlus {
@@ -343,5 +349,95 @@ ContextData getLocationContext(TextDocument *document, int lineNumber)
return data;
}
+//
+// Annotations
+//
+class DebuggerValueMark : public TextEditor::TextMark
+{
+public:
+ DebuggerValueMark(const FilePath &fileName, int lineNumber, const QString &value)
+ : TextMark(fileName, lineNumber, Constants::TEXT_MARK_CATEGORY_VALUE)
+ {
+ setPriority(TextEditor::TextMark::HighPriority);
+ setToolTipProvider([] { return QString(); });
+ setLineAnnotation(value);
+ }
+};
+
+static QList<DebuggerValueMark *> marks;
+
+// Stolen from CPlusPlus::Document::functionAt(...)
+static int firstRelevantLine(const Document::Ptr document, int line, int column)
+{
+ QTC_ASSERT(line > 0 && column > 0, return 0);
+ CPlusPlus::Symbol *symbol = document->lastVisibleSymbolAt(line, column);
+ if (!symbol)
+ return 0;
+
+ // Find the enclosing function scope (which might be several levels up,
+ // or we might be standing on it)
+ Scope *scope = symbol->asScope();
+ if (!scope)
+ scope = symbol->enclosingScope();
+
+ while (scope && !scope->isFunction() )
+ scope = scope->enclosingScope();
+
+ if (!scope)
+ return 0;
+
+ return scope->line();
+}
+
+static void setValueAnnotationsHelper(BaseTextEditor *textEditor,
+ const Location &loc,
+ QMap<QString, QString> values)
+{
+ TextEditorWidget *widget = textEditor->editorWidget();
+ TextDocument *textDocument = widget->textDocument();
+ const FilePath filePath = loc.fileName();
+ const Snapshot snapshot = CppModelManager::instance()->snapshot();
+ const Document::Ptr cppDocument = snapshot.document(filePath.toString());
+ if (!cppDocument) // For non-C++ documents.
+ return;
+
+ const int firstLine = firstRelevantLine(cppDocument, loc.lineNumber(), 1);
+ if (firstLine < 1)
+ return;
+
+ CPlusPlus::ExpressionUnderCursor expressionUnderCursor(cppDocument->languageFeatures());
+ QTextCursor tc = widget->textCursor();
+ for (int lineNumber = loc.lineNumber(); lineNumber >= firstLine; --lineNumber) {
+ const QTextBlock block = textDocument->document()->findBlockByNumber(lineNumber - 1);
+ tc.setPosition(block.position());
+ for (; !tc.atBlockEnd(); tc.movePosition(QTextCursor::NextCharacter)) {
+ const QString expression = expressionUnderCursor(tc);
+ if (expression.isEmpty())
+ continue;
+ const QString value = values.take(expression); // Show value one only once.
+ if (value.isEmpty())
+ continue;
+ const QString annotation = QString("%1: %2").arg(expression, value);
+ marks.append(new DebuggerValueMark(filePath, lineNumber, annotation));
+ }
+ }
+}
+
+void setValueAnnotations(const Location &loc, const QMap<QString, QString> &values)
+{
+ qDeleteAll(marks);
+ marks.clear();
+ if (values.isEmpty())
+ return;
+
+ const QList<Core::IEditor *> editors = Core::EditorManager::visibleEditors();
+ for (Core::IEditor *editor : editors) {
+ if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) {
+ if (textEditor->textDocument()->filePath() == loc.fileName())
+ setValueAnnotationsHelper(textEditor, loc, values);
+ }
+ }
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/sourceutils.h b/src/plugins/debugger/sourceutils.h
index a64c73295d..634fa66203 100644
--- a/src/plugins/debugger/sourceutils.h
+++ b/src/plugins/debugger/sourceutils.h
@@ -25,6 +25,7 @@
#pragma once
+#include <QMap>
#include <QString>
namespace TextEditor {
@@ -38,6 +39,7 @@ namespace Debugger {
namespace Internal {
class ContextData;
+class Location;
// Editor tooltip support
QString cppExpressionAt(TextEditor::TextEditorWidget *editorWidget, int pos,
@@ -54,5 +56,7 @@ QStringList getUninitializedVariables(const CPlusPlus::Snapshot &snapshot,
ContextData getLocationContext(TextEditor::TextDocument *document, int lineNumber);
+void setValueAnnotations(const Location &loc, const QMap<QString, QString> &values);
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp
index b8bcb522e0..26afbc7570 100644
--- a/src/plugins/debugger/watchdata.cpp
+++ b/src/plugins/debugger/watchdata.cpp
@@ -520,6 +520,7 @@ QString WatchItem::toToolTip() const
formatToolTipRow(str, tr("Static Object Size"), tr("%n bytes", nullptr, size));
formatToolTipRow(str, tr("Internal ID"), internalName());
formatToolTipRow(str, tr("Creation Time in ms"), QString::number(int(time * 1000)));
+ formatToolTipRow(str, tr("Source"), sourceExpression());
str << "</table></body></html>";
return res;
}
@@ -578,6 +579,31 @@ QString WatchItem::expression() const
return name;
}
+QString WatchItem::sourceExpression() const
+{
+ const WatchItem *p = parent();
+ if (!p)
+ return {}; // Root
+
+ const WatchItem *pp = p->parent();
+ if (!pp)
+ return {}; // local
+
+ const WatchItem *ppp = pp->parent();
+ if (!ppp)
+ return name; // local.x -> 'x'
+
+ // Enforce some arbitrary, but fixed limit to avoid excessive creation
+ // of very likely unused strings which are for convenience only.
+ if (arrayIndex >= 0 && arrayIndex <= 16)
+ return QString("%1[%2]").arg(p->sourceExpression()).arg(arrayIndex);
+
+ if (p->name == '*')
+ return QString("%1->%2").arg(pp->sourceExpression(), name);
+
+ return QString("%1.%2").arg(p->sourceExpression(), name);
+}
+
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h
index 15146d9616..0169d73bea 100644
--- a/src/plugins/debugger/watchdata.h
+++ b/src/plugins/debugger/watchdata.h
@@ -51,6 +51,7 @@ public:
bool isInspect() const;
QString expression() const;
+ QString sourceExpression() const;
QString realName() const;
QString internalName() const;
QString toToolTip() const;
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 3bf6444b8e..0068157296 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -38,6 +38,7 @@
#include "memoryagent.h"
#include "registerhandler.h"
#include "simplifytype.h"
+#include "sourceutils.h"
#include "watchdelegatewidgets.h"
#include "watchutils.h"
@@ -67,6 +68,7 @@
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
+#include <QMap>
#include <QMenu>
#include <QMimeData>
#include <QPainter>
@@ -473,6 +475,8 @@ public:
QHash<QString, TypeInfo> m_reportedTypeInfo;
QHash<QString, DisplayFormats> m_reportedTypeFormats; // Type name -> Dumper Formats
QHash<QString, QString> m_valueCache;
+
+ Location m_location;
};
WatchModel::WatchModel(WatchHandler *handler, DebuggerEngine *engine)
@@ -518,6 +522,8 @@ WatchModel::WatchModel(WatchHandler *handler, DebuggerEngine *engine)
m_engine, &DebuggerEngine::updateAll);
connect(action(ShowQObjectNames), &SavedAction::valueChanged,
m_engine, &DebuggerEngine::updateAll);
+ connect(action(UseAnnotationsInMainEditor), &SavedAction::valueChanged,
+ m_engine, &DebuggerEngine::updateAll);
connect(SessionManager::instance(), &SessionManager::sessionLoaded,
this, &loadSessionData);
@@ -2081,6 +2087,7 @@ void WatchHandler::cleanup()
theTemporaryWatchers.clear();
saveWatchers();
m_model->reinitialize();
+ Internal::setValueAnnotations(m_model->m_location, {});
emit m_model->updateFinished();
m_model->m_separatedView->hide();
}
@@ -2234,6 +2241,16 @@ void WatchHandler::notifyUpdateFinished()
}
});
+ QMap<QString, QString> values;
+ if (boolSetting(UseAnnotationsInMainEditor)) {
+ m_model->forAllItems([&values](WatchItem *item) {
+ const QString expr = item->sourceExpression();
+ if (!expr.isEmpty())
+ values[expr] = item->value;
+ });
+ }
+ Internal::setValueAnnotations(m_model->m_location, values);
+
m_model->m_contentsValid = true;
updateLocalsWindow();
m_model->reexpandItems();
@@ -2750,6 +2767,11 @@ void WatchHandler::recordTypeInfo(const GdbMi &typeInfo)
}
}
+void WatchHandler::setLocation(const Location &loc)
+{
+ m_model->m_location = loc;
+}
+
/////////////////////////////////////////////////////////////////////
//
// WatchDelegate
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index 6d94249eea..a0591bb521 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -119,6 +119,8 @@ public:
void reexpandItems();
void recordTypeInfo(const GdbMi &typeInfo);
+ void setLocation(const Location &loc);
+
private:
DebuggerEngine * const m_engine; // Not owned
WatchModel *m_model; // Owned.