diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts
index 1f1d52059cb..9c18f5e66c7 100644
--- a/share/qtcreator/translations/qtcreator_de.ts
+++ b/share/qtcreator/translations/qtcreator_de.ts
@@ -19385,6 +19385,16 @@ Wenn die Systemzeiger für das Verändern der Größe von Ansichten nicht korrek
Open file "%1" with:
Öffne Datei "%1" mit:
+
+ Show {} &preceding lines
+ The optional placeholder "{}" is replaced by a spin box for selecting a number.
+ Zeige {} &vorausgehende Zeilen
+
+
+ Show {} &subsequent lines
+ The optional placeholder "{}" is replaced by a spin box for selecting a number.
+ Zeige {} &nachfolgende Zeilen
+
Maximize
Maximieren
@@ -21807,6 +21817,14 @@ Doppelklicken Sie einen Eintrag um ihn zu ändern.
Show Non-matching Lines
Zeige nicht übereinstimmende Zeilen
+
+ Show {# lines} &before
+ {# Zeilen} &vorab zeigen
+
+
+ Show {# lines} &after
+ {# Zeilen} &nachfolgend zeigen
+
Filter output...
Ausgabe filtern...
diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp
index 894107d5d4b..7492448a026 100644
--- a/src/plugins/autotest/testresultspane.cpp
+++ b/src/plugins/autotest/testresultspane.cpp
@@ -404,7 +404,7 @@ void TestResultsPane::goToPrev()
void TestResultsPane::updateFilter()
{
m_textOutput->updateFilterProperties(filterText(), filterCaseSensitivity(), filterUsesRegexp(),
- filterIsInverted());
+ filterIsInverted(), beforeContext(), afterContext());
}
void TestResultsPane::onItemActivated(const QModelIndex &index)
diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h
index 76d196a382f..0618653b20f 100644
--- a/src/plugins/coreplugin/ioutputpane.h
+++ b/src/plugins/coreplugin/ioutputpane.h
@@ -43,6 +43,8 @@ public:
virtual void goToNext() = 0;
virtual void goToPrev() = 0;
+ virtual bool hasFilterContext() const;
+
void setFont(const QFont &font);
void setWheelZoomEnabled(bool enabled);
@@ -80,6 +82,8 @@ protected:
QString filterText() const;
bool filterUsesRegexp() const { return m_filterRegexp; }
bool filterIsInverted() const { return m_invertFilter; }
+ int beforeContext() const { return m_beforeContext; }
+ int afterContext() const { return m_afterContext; }
Qt::CaseSensitivity filterCaseSensitivity() const { return m_filterCaseSensitivity; }
void setFilteringEnabled(bool enable);
QWidget *filterWidget() const { return m_filterOutputLineEdit; }
@@ -96,15 +100,22 @@ private:
Utils::Id filterRegexpActionId() const;
Utils::Id filterCaseSensitivityActionId() const;
Utils::Id filterInvertedActionId() const;
+ Utils::Id filterBeforeActionId() const;
+ Utils::Id filterAfterActionId() const;
Utils::Id m_id;
QString m_displayName;
int m_priority = -1;
QToolButton *m_zoomInButton;
QToolButton *m_zoomOutButton;
+ QAction *m_filterActionRegexp = nullptr;
+ QAction *m_filterActionCaseSensitive = nullptr;
+ QAction *m_invertFilterAction = nullptr;
Utils::FancyLineEdit *m_filterOutputLineEdit = nullptr;
bool m_filterRegexp = false;
bool m_invertFilter = false;
+ int m_beforeContext = 0;
+ int m_afterContext = 0;
Qt::CaseSensitivity m_filterCaseSensitivity = Qt::CaseInsensitive;
};
diff --git a/src/plugins/coreplugin/messageoutputwindow.cpp b/src/plugins/coreplugin/messageoutputwindow.cpp
index 1cfe384ac00..b13a301efed 100644
--- a/src/plugins/coreplugin/messageoutputwindow.cpp
+++ b/src/plugins/coreplugin/messageoutputwindow.cpp
@@ -99,10 +99,15 @@ bool MessageOutputWindow::canNavigate() const
return false;
}
+bool MessageOutputWindow::hasFilterContext() const
+{
+ return true;
+}
+
void MessageOutputWindow::updateFilter()
{
m_widget->updateFilterProperties(filterText(), filterCaseSensitivity(), filterUsesRegexp(),
- filterIsInverted());
+ filterIsInverted(), beforeContext(), afterContext());
}
} // namespace Internal
diff --git a/src/plugins/coreplugin/messageoutputwindow.h b/src/plugins/coreplugin/messageoutputwindow.h
index ef0d93a97cf..eabc2adcff3 100644
--- a/src/plugins/coreplugin/messageoutputwindow.h
+++ b/src/plugins/coreplugin/messageoutputwindow.h
@@ -33,6 +33,8 @@ public:
void goToPrev() override;
bool canNavigate() const override;
+ bool hasFilterContext() const override;
+
private:
void updateFilter() override;
diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp
index 0b141df46e9..45054f6185c 100644
--- a/src/plugins/coreplugin/outputpanemanager.cpp
+++ b/src/plugins/coreplugin/outputpanemanager.cpp
@@ -161,6 +161,11 @@ void IOutputPane::visibilityChanged(bool /*visible*/)
{
}
+bool IOutputPane::hasFilterContext() const
+{
+ return false;
+}
+
void IOutputPane::setFont(const QFont &font)
{
emit fontChanged(font);
@@ -191,6 +196,32 @@ void IOutputPane::setupFilterUi(const Key &historyKey)
updateFilter();
});
+ ActionBuilder filterBeforeAction(this, filterBeforeActionId());
+ //: The placeholder "{}" is replaced by a spin box for selecting a number.
+ filterBeforeAction.setText(Tr::tr("Show {} &preceding lines"));
+ QAction *action = filterBeforeAction.contextAction();
+ NumericOption::set(action, NumericOption{0, 0, 9});
+ NumericOption::set(filterBeforeAction.commandAction(), NumericOption{0, 0, 9});
+ connect(action, &QAction::changed, this, [this, action] {
+ const std::optional option = NumericOption::get(action);
+ QTC_ASSERT(option, return);
+ m_beforeContext = option->currentValue;
+ updateFilter();
+ });
+
+ ActionBuilder filterAfterAction(this, filterAfterActionId());
+ //: The placeholder "{}" is replaced by a spin box for selecting a number.
+ filterAfterAction.setText(Tr::tr("Show {} &subsequent lines"));
+ action = filterAfterAction.contextAction();
+ NumericOption::set(action, NumericOption{0, 0, 9});
+ NumericOption::set(filterAfterAction.commandAction(), NumericOption{0, 0, 9});
+ connect(action, &QAction::changed, this, [this, action] {
+ const std::optional option = NumericOption::get(action);
+ QTC_ASSERT(option, return);
+ m_afterContext = option->currentValue;
+ updateFilter();
+ });
+
m_filterOutputLineEdit = new FancyLineEdit;
m_filterOutputLineEdit->setPlaceholderText(Tr::tr("Filter output..."));
m_filterOutputLineEdit->setButtonVisible(FancyLineEdit::Left, true);
@@ -252,8 +283,16 @@ void IOutputPane::updateFilter()
void IOutputPane::filterOutputButtonClicked()
{
- auto popup = new Core::OptionsPopup(m_filterOutputLineEdit,
- {filterRegexpActionId(), filterCaseSensitivityActionId(), filterInvertedActionId()});
+ QVector commands = {filterRegexpActionId(),
+ filterCaseSensitivityActionId(),
+ filterInvertedActionId()};
+
+ if (hasFilterContext()) {
+ commands.emplaceBack(filterBeforeActionId());
+ commands.emplaceBack(filterAfterActionId());
+ }
+
+ auto popup = new Core::OptionsPopup(m_filterOutputLineEdit, commands);
popup->show();
}
@@ -278,6 +317,16 @@ Id IOutputPane::filterInvertedActionId() const
return Id("OutputFilter.Invert").withSuffix(metaObject()->className());
}
+Id IOutputPane::filterBeforeActionId() const
+{
+ return Id("OutputFilter.BeforeContext").withSuffix(metaObject()->className());
+}
+
+Id IOutputPane::filterAfterActionId() const
+{
+ return Id("OutputFilter.AfterContext").withSuffix(metaObject()->className());
+}
+
void IOutputPane::setCaseSensitive(bool caseSensitive)
{
m_filterCaseSensitivity = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp
index c9b3ec86f32..e792bbcb7f9 100644
--- a/src/plugins/coreplugin/outputwindow.cpp
+++ b/src/plugins/coreplugin/outputwindow.cpp
@@ -70,6 +70,8 @@ public:
int lastFilteredBlockNumber = -1;
QPalette originalPalette;
OutputWindow::FilterModeFlags filterMode = OutputWindow::FilterModeFlag::Default;
+ int beforeContext = 0;
+ int afterContext = 0;
QTimer scrollTimer;
QElapsedTimer lastMessage;
QHash> taskPositions;
@@ -337,14 +339,19 @@ void OutputWindow::updateFilterProperties(
const QString &filterText,
Qt::CaseSensitivity caseSensitivity,
bool isRegexp,
- bool isInverted
+ bool isInverted,
+ int beforeContext,
+ int afterContext
)
{
FilterModeFlags flags;
flags.setFlag(FilterModeFlag::CaseSensitive, caseSensitivity == Qt::CaseSensitive)
.setFlag(FilterModeFlag::RegExp, isRegexp)
.setFlag(FilterModeFlag::Inverted, isInverted);
- if (d->filterMode == flags && d->filterText == filterText)
+ if (d->filterMode == flags
+ && d->filterText == filterText
+ && d->beforeContext == beforeContext
+ && d->afterContext == afterContext)
return;
d->lastFilteredBlockNumber = -1;
if (d->filterText != filterText) {
@@ -371,6 +378,8 @@ void OutputWindow::updateFilterProperties(
}
}
d->filterMode = flags;
+ d->beforeContext = beforeContext;
+ d->afterContext = afterContext;
filterNewContent();
}
@@ -409,13 +418,33 @@ void OutputWindow::filterNewContent()
QTC_ASSERT(findNextMatch, return);
const bool invert = d->filterMode.testFlag(FilterModeFlag::Inverted)
&& !d->filterText.isEmpty();
+ const int requiredBacklog = std::max(d->beforeContext, d->afterContext);
+ const int firstBlockIndex = d->lastFilteredBlockNumber - requiredBacklog;
- QTextBlock lastBlock = document()->findBlockByNumber(d->lastFilteredBlockNumber);
+ std::vector matchedBlocks;
+ QTextBlock lastBlock = document()->findBlockByNumber(firstBlockIndex);
if (!lastBlock.isValid())
lastBlock = document()->begin();
- for (; lastBlock != document()->end(); lastBlock = lastBlock.next())
- lastBlock.setVisible(findNextMatch(lastBlock.text()) != invert);
+ // Find matching text blocks for the current filter.
+ for (; lastBlock != document()->end(); lastBlock = lastBlock.next()) {
+ const bool isMatch = findNextMatch(lastBlock.text()) != invert;
+
+ if (isMatch)
+ matchedBlocks.emplace_back(lastBlock.blockNumber());
+
+ lastBlock.setVisible(isMatch);
+ }
+
+ // Reveal the context lines before and after the match.
+ if (!d->filterText.isEmpty()) {
+ for (int blockNumber : matchedBlocks) {
+ for (auto i = 1; i <= d->beforeContext; ++i)
+ document()->findBlockByNumber(blockNumber - i).setVisible(true);
+ for (auto i = 1; i <= d->afterContext; ++i)
+ document()->findBlockByNumber(blockNumber + i).setVisible(true);
+ }
+ }
d->lastFilteredBlockNumber = document()->lastBlock().blockNumber();
diff --git a/src/plugins/coreplugin/outputwindow.h b/src/plugins/coreplugin/outputwindow.h
index 19ab9a3c568..b6fc49001f3 100644
--- a/src/plugins/coreplugin/outputwindow.h
+++ b/src/plugins/coreplugin/outputwindow.h
@@ -65,7 +65,9 @@ public:
const QString &filterText,
Qt::CaseSensitivity caseSensitivity,
bool regexp,
- bool isInverted);
+ bool isInverted,
+ int beforeContext,
+ int afterContext);
void setOutputFileNameHint(const QString &fileName);
@@ -97,7 +99,7 @@ private:
void handleOutputChunk(const QString &output, Utils::OutputFormat format);
void updateAutoScroll();
- using TextMatchingFunction = std::function;
+ using TextMatchingFunction = std::function;
TextMatchingFunction makeMatchingFunction() const;
Internal::OutputWindowPrivate *d = nullptr;
diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp
index 3d489460247..8d8455f5aee 100644
--- a/src/plugins/projectexplorer/appoutputpane.cpp
+++ b/src/plugins/projectexplorer/appoutputpane.cpp
@@ -339,7 +339,8 @@ void AppOutputPane::updateFilter()
{
if (RunControlTab * const tab = currentTab()) {
tab->window->updateFilterProperties(filterText(), filterCaseSensitivity(),
- filterUsesRegexp(), filterIsInverted());
+ filterUsesRegexp(), filterIsInverted(),
+ beforeContext(), afterContext());
}
}
@@ -730,7 +731,8 @@ void AppOutputPane::tabChanged(int i)
RunControlTab * const controlTab = tabFor(m_tabWidget->widget(i));
if (i != -1 && controlTab) {
controlTab->window->updateFilterProperties(filterText(), filterCaseSensitivity(),
- filterUsesRegexp(), filterIsInverted());
+ filterUsesRegexp(), filterIsInverted(),
+ beforeContext(), afterContext());
enableButtons(controlTab->runControl);
} else {
enableDefaultButtons();
@@ -810,6 +812,11 @@ bool AppOutputPane::canNavigate() const
return false;
}
+bool AppOutputPane::hasFilterContext() const
+{
+ return true;
+}
+
class AppOutputSettingsWidget : public Core::IOptionsPageWidget
{
public:
diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h
index f7cbb653ff3..6438e6c2c1d 100644
--- a/src/plugins/projectexplorer/appoutputpane.h
+++ b/src/plugins/projectexplorer/appoutputpane.h
@@ -57,6 +57,8 @@ public:
void goToPrev() override;
bool canNavigate() const override;
+ bool hasFilterContext() const override;
+
void createNewOutputWindow(RunControl *rc);
void showTabFor(RunControl *rc);
void setBehaviorOnOutput(RunControl *rc, AppOutputPaneMode mode);
diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp
index c5255d127f5..eae2e2bc805 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.cpp
+++ b/src/plugins/projectexplorer/compileoutputwindow.cpp
@@ -203,6 +203,11 @@ bool CompileOutputWindow::canNavigate() const
return false;
}
+bool CompileOutputWindow::hasFilterContext() const
+{
+ return true;
+}
+
void CompileOutputWindow::registerPositionOf(const Task &task, int linkedOutputLines, int skipLines,
int offset)
{
@@ -227,7 +232,8 @@ Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const
void CompileOutputWindow::updateFilter()
{
m_outputWindow->updateFilterProperties(filterText(), filterCaseSensitivity(),
- filterUsesRegexp(), filterIsInverted());
+ filterUsesRegexp(), filterIsInverted(),
+ beforeContext(), afterContext());
}
// CompileOutputSettings
diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h
index 21cebcb2dba..cab706c9f52 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.h
+++ b/src/plugins/projectexplorer/compileoutputwindow.h
@@ -59,6 +59,8 @@ public:
void goToPrev() override;
bool canNavigate() const override;
+ bool hasFilterContext() const override;
+
void appendText(const QString &text, BuildStep::OutputFormat format);
void registerPositionOf(const Task &task, int linkedOutputLines, int skipLines, int offset = 0);
diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp
index c3214b64984..70ba2bbc972 100644
--- a/src/plugins/projectexplorer/projectwindow.cpp
+++ b/src/plugins/projectexplorer/projectwindow.cpp
@@ -206,7 +206,9 @@ void BuildSystemOutputWindow::updateFilter()
m_filterActionCaseSensitive.isChecked() ? Qt::CaseSensitive
: Qt::CaseInsensitive,
m_filterActionRegexp.isChecked(),
- m_invertFilterAction.isChecked());
+ m_invertFilterAction.isChecked(),
+ 0 /* before context */,
+ 0 /* after context */);
}
class VanishedTargetPanelItem : public TreeItem
diff --git a/src/plugins/serialterminal/serialoutputpane.cpp b/src/plugins/serialterminal/serialoutputpane.cpp
index 2f30bf75fcd..7c4ca50b47c 100644
--- a/src/plugins/serialterminal/serialoutputpane.cpp
+++ b/src/plugins/serialterminal/serialoutputpane.cpp
@@ -227,6 +227,11 @@ bool SerialOutputPane::canNavigate() const
return false;
}
+bool SerialOutputPane::hasFilterContext() const
+{
+ return true;
+}
+
void SerialOutputPane::appendMessage(SerialControl *rc, const QString &out, Utils::OutputFormat format)
{
const int index = indexOf(rc);
diff --git a/src/plugins/serialterminal/serialoutputpane.h b/src/plugins/serialterminal/serialoutputpane.h
index 1f970e2f18e..9a4097b3f5f 100644
--- a/src/plugins/serialterminal/serialoutputpane.h
+++ b/src/plugins/serialterminal/serialoutputpane.h
@@ -62,6 +62,8 @@ public:
void goToPrev() final;
bool canNavigate() const final;
+ bool hasFilterContext() const final;
+
void createNewOutputWindow(SerialControl *rc);
bool closeTabs(CloseTabMode mode);