forked from qt-creator/qt-creator
Core: Show context of filter matches
When inspecting logs often enough the relevant information is next to the line with the unique expression that's easy to match. The `grep` tool solves this problem by providing various `--*context` options which configure how much context to show for each match. This change tries to replicate this feature. Task-number: QTCREATORBUG-30167 Change-Id: I6432870c0b958df8c5dc616009aea4ca54973245 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -19385,6 +19385,16 @@ Wenn die Systemzeiger für das Verändern der Größe von Ansichten nicht korrek
|
||||
<source>Open file "%1" with:</source>
|
||||
<translation>Öffne Datei "%1" mit:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show {} &preceding lines</source>
|
||||
<extracomment>The optional placeholder "{}" is replaced by a spin box for selecting a number.</extracomment>
|
||||
<translation>Zeige {} &vorausgehende Zeilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show {} &subsequent lines</source>
|
||||
<extracomment>The optional placeholder "{}" is replaced by a spin box for selecting a number.</extracomment>
|
||||
<translation>Zeige {} &nachfolgende Zeilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Maximize</source>
|
||||
<translation>Maximieren</translation>
|
||||
@@ -21807,6 +21817,14 @@ Doppelklicken Sie einen Eintrag um ihn zu ändern.</translation>
|
||||
<source>Show Non-matching Lines</source>
|
||||
<translation>Zeige nicht übereinstimmende Zeilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show {# lines} &before</source>
|
||||
<translation>{# Zeilen} &vorab zeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show {# lines} &after</source>
|
||||
<translation>{# Zeilen} &nachfolgend zeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Filter output...</source>
|
||||
<translation>Ausgabe filtern...</translation>
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -33,6 +33,8 @@ public:
|
||||
void goToPrev() override;
|
||||
bool canNavigate() const override;
|
||||
|
||||
bool hasFilterContext() const override;
|
||||
|
||||
private:
|
||||
void updateFilter() override;
|
||||
|
||||
|
@@ -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<NumericOption> 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<NumericOption> 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<Utils::Id> 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;
|
||||
|
@@ -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<unsigned int, QPair<int, int>> 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<int> 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();
|
||||
|
||||
|
@@ -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<qsizetype(const QString &text)>;
|
||||
using TextMatchingFunction = std::function<bool(const QString &text)>;
|
||||
TextMatchingFunction makeMatchingFunction() const;
|
||||
|
||||
Internal::OutputWindowPrivate *d = nullptr;
|
||||
|
@@ -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:
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -62,6 +62,8 @@ public:
|
||||
void goToPrev() final;
|
||||
bool canNavigate() const final;
|
||||
|
||||
bool hasFilterContext() const final;
|
||||
|
||||
void createNewOutputWindow(SerialControl *rc);
|
||||
|
||||
bool closeTabs(CloseTabMode mode);
|
||||
|
Reference in New Issue
Block a user