diff --git a/CHANGELOG.md b/CHANGELOG.md index f475695..47766d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Add --gdb-program and --gdb-arguments to command line to override settings from Seer's config. * Fixed a rare bug with blank lines from gdb causing a segv in GdbMonitor. * Fixed bug specifying path to "rr" debugger. +* Add option to reload source file if it changes. ## [2.2] - 2023-09-07 * Fixed infinite loop when starting with RR mode. diff --git a/src/SeerEditorManagerWidget.cpp b/src/SeerEditorManagerWidget.cpp index 437ba2d..e1b093e 100644 --- a/src/SeerEditorManagerWidget.cpp +++ b/src/SeerEditorManagerWidget.cpp @@ -805,6 +805,7 @@ SeerEditorWidgetSource* SeerEditorManagerWidget::createEditorWidgetTab (const QS QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::deleteBreakpoints, this, &SeerEditorManagerWidget::handleDeleteBreakpoints); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::enableBreakpoints, this, &SeerEditorManagerWidget::handleEnableBreakpoints); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::disableBreakpoints, this, &SeerEditorManagerWidget::handleDisableBreakpoints); + QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::refreshBreakpointsStackFrames, this, &SeerEditorManagerWidget::handleRefreshBreakpointsStackFrames); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::runToLine, this, &SeerEditorManagerWidget::handleRunToLine); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addVariableLoggerExpression, this, &SeerEditorManagerWidget::handleAddVariableLoggerExpression); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addVariableTrackerExpression, this, &SeerEditorManagerWidget::handleAddVariableTrackerExpression); @@ -863,6 +864,7 @@ SeerEditorWidgetSource* SeerEditorManagerWidget::createEditorWidgetTab (const QS QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::deleteBreakpoints, this, &SeerEditorManagerWidget::handleDeleteBreakpoints); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::enableBreakpoints, this, &SeerEditorManagerWidget::handleEnableBreakpoints); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::disableBreakpoints, this, &SeerEditorManagerWidget::handleDisableBreakpoints); + QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::refreshBreakpointsStackFrames, this, &SeerEditorManagerWidget::handleRefreshBreakpointsStackFrames); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::runToLine, this, &SeerEditorManagerWidget::handleRunToLine); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addVariableLoggerExpression, this, &SeerEditorManagerWidget::handleAddVariableLoggerExpression); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addVariableTrackerExpression, this, &SeerEditorManagerWidget::handleAddVariableTrackerExpression); @@ -962,6 +964,7 @@ SeerEditorWidgetAssembly* SeerEditorManagerWidget::createAssemblyWidgetTab () { QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::deleteBreakpoints, this, &SeerEditorManagerWidget::handleDeleteBreakpoints); QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::enableBreakpoints, this, &SeerEditorManagerWidget::handleEnableBreakpoints); QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::disableBreakpoints, this, &SeerEditorManagerWidget::handleDisableBreakpoints); + QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::refreshBreakpointsStackFrames, this, &SeerEditorManagerWidget::handleRefreshBreakpointsStackFrames); QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::runToAddress, this, &SeerEditorManagerWidget::handleRunToAddress); QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::requestAssembly, this, &SeerEditorManagerWidget::handleRequestAssembly); QObject::connect(assemblyWidget->assemblyArea(), &SeerEditorWidgetAssemblyArea::requestSourceAndAssembly, this, &SeerEditorManagerWidget::handleRequestSourceAndAssembly); @@ -1142,6 +1145,15 @@ void SeerEditorManagerWidget::handleDisableBreakpoints (QString breakpoints) { emit disableBreakpoints (breakpoints); } +void SeerEditorManagerWidget::handleRefreshBreakpointsStackFrames () { + + // Ask for the breakpoint list to be resent, in case files have breakpoints. + emit refreshBreakpointsList(); + + // Ask for the stackframe list to be resent, in case files have currently executing lines. + emit refreshStackFrames(); +} + void SeerEditorManagerWidget::handleRunToLine (QString fullname, int lineno) { //qDebug() << fullname << lineno; diff --git a/src/SeerEditorManagerWidget.h b/src/SeerEditorManagerWidget.h index f7a8f91..652403b 100644 --- a/src/SeerEditorManagerWidget.h +++ b/src/SeerEditorManagerWidget.h @@ -73,6 +73,7 @@ class SeerEditorManagerWidget : public QWidget, protected Ui::SeerEditorManagerW void handleDeleteBreakpoints (QString breakpoints); void handleEnableBreakpoints (QString breakpoints); void handleDisableBreakpoints (QString breakpoints); + void handleRefreshBreakpointsStackFrames (); void handleRunToLine (QString fullname, int lineno); void handleRunToAddress (QString address); void handleAddVariableLoggerExpression (QString expression); diff --git a/src/SeerEditorWidgetAssembly.h b/src/SeerEditorWidgetAssembly.h index 2881037..475a3ba 100644 --- a/src/SeerEditorWidgetAssembly.h +++ b/src/SeerEditorWidgetAssembly.h @@ -104,6 +104,7 @@ class SeerEditorWidgetAssemblyArea : public SeerPlainTextEdit { void deleteBreakpoints (QString breakpoints); void enableBreakpoints (QString breakpoints); void disableBreakpoints (QString breakpoints); + void refreshBreakpointsStackFrames (); void runToAddress (QString address); void addMemoryVisualize (QString expression); void addArrayVisualize (QString expression); diff --git a/src/SeerEditorWidgetSource.cpp b/src/SeerEditorWidgetSource.cpp index 1b2934b..768deb6 100644 --- a/src/SeerEditorWidgetSource.cpp +++ b/src/SeerEditorWidgetSource.cpp @@ -28,12 +28,14 @@ SeerEditorWidgetSource::SeerEditorWidgetSource(QWidget* parent) : QWidget(parent showSearchBar(false); // Hide the search bar. ctrl+F to show it again. showAlternateBar(false); // Hide the alternate bar. ctrl+O to show it again. + showReloadBar(false); // Hide the reload file bar. Automatically shown if file gets updated. setSearchMatchCase(true); // Search with case sensitivity. - _textSearchShortcut = new QShortcut(QKeySequence(tr("Ctrl+F")), this); - _textSearchNextShortcut = new QShortcut(QKeySequence(tr("Ctrl+G")), this); - _textSearchPrevShortcut = new QShortcut(QKeySequence(tr("Ctrl+Shift+G")), this); - _alternateDirShortcut = new QShortcut(QKeySequence(tr("Ctrl+O")), this); + _textSearchShortcut = new QShortcut(QKeySequence(tr("Ctrl+F")), this); + _textSearchNextShortcut = new QShortcut(QKeySequence(tr("Ctrl+G")), this); + _textSearchPrevShortcut = new QShortcut(QKeySequence(tr("Ctrl+Shift+G")), this); + _textSearchReloadShortcut = new QShortcut(QKeySequence(tr("Ctrl+R")), this); + _alternateDirShortcut = new QShortcut(QKeySequence(tr("Ctrl+O")), this); setKeySettings(SeerKeySettings::populate()); @@ -43,16 +45,21 @@ SeerEditorWidgetSource::SeerEditorWidgetSource(QWidget* parent) : QWidget(parent QObject::connect(searchDownToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleSearchDownToolButton); QObject::connect(searchUpToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleSearchUpToolButton); QObject::connect(searchLineNumberLineEdit, &QLineEdit::returnPressed, this, &SeerEditorWidgetSource::handleSearchLineNumberLineEdit); + QObject::connect(searchReloadToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleReloadToolButton); QObject::connect(searchCloseToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleSearchCloseToolButton); QObject::connect(alternateCloseToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleAlternateCloseToolButton); QObject::connect(alternateFileOpenToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleAlternateFileOpenToolButton); QObject::connect(alternateLineEdit, &QLineEdit::returnPressed, this, &SeerEditorWidgetSource::handleAlternateLineEdit); + QObject::connect(reloadToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleReloadToolButton); + QObject::connect(reloadCloseToolButton, &QToolButton::clicked, this, &SeerEditorWidgetSource::handleReloadCloseToolButton); QObject::connect(sourceWidget, &SeerEditorWidgetSourceArea::showSearchBar, this, &SeerEditorWidgetSource::showSearchBar); QObject::connect(sourceWidget, &SeerEditorWidgetSourceArea::showAlternateBar, this, &SeerEditorWidgetSource::showAlternateBar); + QObject::connect(sourceWidget, &SeerEditorWidgetSourceArea::showReloadBar, this, &SeerEditorWidgetSource::showReloadBar); QObject::connect(_textSearchShortcut, &QShortcut::activated, this, &SeerEditorWidgetSource::handleTextSearchShortcut); QObject::connect(_textSearchNextShortcut, &QShortcut::activated, this, &SeerEditorWidgetSource::handleSearchDownToolButton); QObject::connect(_textSearchPrevShortcut, &QShortcut::activated, this, &SeerEditorWidgetSource::handleSearchUpToolButton); + QObject::connect(_textSearchReloadShortcut, &QShortcut::activated, this, &SeerEditorWidgetSource::handleReloadToolButton); QObject::connect(_alternateDirShortcut, &QShortcut::activated, this, &SeerEditorWidgetSource::handleAlternateDirectoryShortcut); } @@ -131,6 +138,19 @@ void SeerEditorWidgetSource::showAlternateBar (bool flag) { } } +void SeerEditorWidgetSource::showReloadBar (bool flag) { + + reloadBarWidget->setVisible(flag); + reloadBarWidget->setStyleSheet("QWidget { background-color : #00AA00; color : #FFFF00; }"); + + if (flag) { + reloadFilenameLabel->setText("The file \"" + sourceArea()->file() + "\" has changed on disk."); + reloadToolButton->setFocus(Qt::MouseFocusReason); // If 'show', give the reload button the focus. + }else{ + reloadFilenameLabel->setText(""); + } +} + void SeerEditorWidgetSource::handleSearchLineNumberLineEdit () { QString str = searchLineNumberLineEdit->text(); @@ -253,3 +273,15 @@ void SeerEditorWidgetSource::handleAlternateDirectoryShortcut () { } } +void SeerEditorWidgetSource::handleReloadToolButton () { + + showReloadBar(false); + + sourceArea()->reload(); +} + +void SeerEditorWidgetSource::handleReloadCloseToolButton () { + + showReloadBar(false); +} + diff --git a/src/SeerEditorWidgetSource.h b/src/SeerEditorWidgetSource.h index d7e90c7..15c3df3 100644 --- a/src/SeerEditorWidgetSource.h +++ b/src/SeerEditorWidgetSource.h @@ -15,6 +15,8 @@ #include #include #include +#include + class SeerEditorWidgetSourceLineNumberArea; class SeerEditorWidgetSourceBreakPointArea; @@ -48,9 +50,10 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { bool isOpen () const; void open (const QString& fullname, const QString& file, const QString& alternateDirectory=""); void openText (const QString& text, const QString& file); + void close (); + void reload (); const QString& fullname () const; const QString& file () const; - void close (); void setAlternateDirectory (const QString& alternateDirectory); const QString& alternateDirectory () const; void setAlternateDirectories (const QStringList& alternateDirectories); @@ -99,6 +102,7 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { void deleteBreakpoints (QString breakpoints); void enableBreakpoints (QString breakpoints); void disableBreakpoints (QString breakpoints); + void refreshBreakpointsStackFrames (); void runToLine (QString fullname, int lineno); void addVariableLoggerExpression (QString expression); void addVariableTrackerExpression (QString expression); @@ -109,12 +113,13 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { void addStructVisualize (QString expression); void showSearchBar (bool flag); void showAlternateBar (bool flag); + void showReloadBar (bool flag); void highlighterSettingsChanged (); public slots: void handleText (const QString& text); void handleHighlighterSettingsChanged (); - + void handleWatchFileModified (const QString& path); protected: void resizeEvent (QResizeEvent* event); @@ -135,6 +140,7 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { QString _file; QString _alternateDirectory; QStringList _alternateDirectories; + QFileSystemWatcher* _fileWatcher; bool _enableLineNumberArea; bool _enableBreakPointArea; @@ -244,6 +250,7 @@ class SeerEditorWidgetSource : public QWidget, protected Ui::SeerEditorWidgetSou void showSearchBar (bool flag); void setSearchMatchCase (bool flag); void showAlternateBar (bool flag); + void showReloadBar (bool flag); private slots: void handleSearchLineNumberLineEdit (); @@ -256,6 +263,8 @@ class SeerEditorWidgetSource : public QWidget, protected Ui::SeerEditorWidgetSou void handleAlternateLineEdit (); void handleTextSearchShortcut (); void handleAlternateDirectoryShortcut (); + void handleReloadToolButton (); + void handleReloadCloseToolButton (); signals: void addAlternateDirectory (QString path); @@ -265,6 +274,7 @@ class SeerEditorWidgetSource : public QWidget, protected Ui::SeerEditorWidgetSou QShortcut* _textSearchShortcut; QShortcut* _textSearchNextShortcut; QShortcut* _textSearchPrevShortcut; + QShortcut* _textSearchReloadShortcut; QShortcut* _alternateDirShortcut; }; diff --git a/src/SeerEditorWidgetSource.ui b/src/SeerEditorWidgetSource.ui index 649204c..01e3f31 100644 --- a/src/SeerEditorWidgetSource.ui +++ b/src/SeerEditorWidgetSource.ui @@ -13,20 +13,8 @@ SeerEditorWidgetSourceForm - - - 0 - - - 0 - - - 0 - - - 0 - - + + @@ -119,7 +107,53 @@ - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + Reload source file. + + + Reload + + + + + + + Close the reload bar. + + + ... + + + + :/seer/resources/RelaxLightIcons/list-remove.svg:/seer/resources/RelaxLightIcons/list-remove.svg + + + + + + + @@ -129,7 +163,7 @@ - + @@ -228,6 +262,27 @@ + + + + Qt::Vertical + + + + + + + Reload source file. + + + + + + + :/seer/resources/RelaxLightIcons/view-refresh.svg:/seer/resources/RelaxLightIcons/view-refresh.svg + + + diff --git a/src/SeerEditorWidgetSourceAreas.cpp b/src/SeerEditorWidgetSourceAreas.cpp index 5056bb2..f342419 100644 --- a/src/SeerEditorWidgetSourceAreas.cpp +++ b/src/SeerEditorWidgetSourceAreas.cpp @@ -28,6 +28,7 @@ SeerEditorWidgetSourceArea::SeerEditorWidgetSourceArea(QWidget* parent) : SeerPlainTextEdit(parent) { + _fileWatcher = 0; _enableLineNumberArea = false; _enableBreakPointArea = false; _enableMiniMapArea = false; @@ -543,6 +544,11 @@ bool SeerEditorWidgetSourceArea::isOpen () const { void SeerEditorWidgetSourceArea::open (const QString& fullname, const QString& file, const QString& alternateDirectory) { + // Delete old file watcher, if any. + if (_fileWatcher) { + delete _fileWatcher; _fileWatcher = 0; + } + // Close the previous file, if any. if (isOpen()) { close(); @@ -616,6 +622,12 @@ void SeerEditorWidgetSourceArea::open (const QString& fullname, const QString& f // Put the contents in the editor. openText(text, _fullname); + + // Watch the file. + _fileWatcher = new QFileSystemWatcher(this); + _fileWatcher->addPath(filename); + + QObject::connect(_fileWatcher, &QFileSystemWatcher::fileChanged, this, &SeerEditorWidgetSourceArea::handleWatchFileModified); } void SeerEditorWidgetSourceArea::openText (const QString& text, const QString& file) { @@ -648,6 +660,54 @@ void SeerEditorWidgetSourceArea::openText (const QString& text, const QString& f } } +void SeerEditorWidgetSourceArea::reload () { + + // Open the same file again. + QString fullname = _fullname; + QString file = _file; + QString alternateDirectory = _alternateDirectory; + + if (fullname == "") return; + if (file == "") return; + + // Save old cursor and scroll position. + int blockno = textCursor().blockNumber(); + int posinblock = textCursor().positionInBlock(); + int vscrollpos = verticalScrollBar()->value(); + + // Reload file. + open(fullname, file, alternateDirectory); + + // Restore to old cursor and scroll position. + QTextCursor c = textCursor(); + + c.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor, 1); + c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, blockno); + c.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, posinblock); + + setTextCursor(c); + + ensureCursorVisible(); + + verticalScrollBar()->setValue(vscrollpos); + + // Refresh any breakpoints or current lines in the stackframes. + emit refreshBreakpointsStackFrames(); +} + +void SeerEditorWidgetSourceArea::close () { + + setDocumentTitle(""); + setPlainText(""); + + clearExpression(); + + // Delete old file watcher, if any. + if (_fileWatcher) { + delete _fileWatcher; _fileWatcher = 0; + } +} + const QString& SeerEditorWidgetSourceArea::fullname () const { return _fullname; } @@ -656,13 +716,6 @@ const QString& SeerEditorWidgetSourceArea::file () const { return _file; } -void SeerEditorWidgetSourceArea::close () { - setDocumentTitle(""); - setPlainText(""); - - clearExpression(); -} - void SeerEditorWidgetSourceArea::setAlternateDirectory (const QString& alternateDirectory) { _alternateDirectory = alternateDirectory; @@ -1681,6 +1734,13 @@ void SeerEditorWidgetSourceArea::handleHighlighterSettingsChanged () { // The new highlighter settings will be used. } +void SeerEditorWidgetSourceArea::handleWatchFileModified (const QString& path) { + + Q_UNUSED(path); + + emit showReloadBar(true); +} + // // LineNumber area. //