Git commit ba68742e0e12e7f2bf26dc256b700b2565883c83 by Christoph Cullmann, on behalf of Javier Guerra. Committed on 12/03/2025 at 16:04. Pushed by cullmann into branch 'master'.
Add text search to the build output Adds a simple search facility to the Output pane of the Build plugin. GUI: Add text search to the build output M +85 -2 addons/katebuild-plugin/build.ui M +109 -0 addons/katebuild-plugin/plugin_katebuild.cpp M +9 -0 addons/katebuild-plugin/plugin_katebuild.h https://invent.kde.org/utilities/kate/-/commit/ba68742e0e12e7f2bf26dc256b700b2565883c83 diff --git a/addons/katebuild-plugin/build.ui b/addons/katebuild-plugin/build.ui index 47090a1149..53f578cf7f 100644 --- a/addons/katebuild-plugin/build.ui +++ b/addons/katebuild-plugin/build.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>407</width> + <width>480</width> <height>308</height> </rect> </property> @@ -90,8 +90,72 @@ </property> </spacer> </item> + <item> + <widget class="QPushButton" name="openSearchPanel"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-find"/> + </property> + <property name="iconSize"> + <size> + <width>22</width> + <height>22</height> + </size> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> </layout> </item> + <item> + <widget class="QWidget" name="search" native="true"> + <property name="visible"> + <bool>false</bool> + </property> + <layout class="QHBoxLayout"> + <property name="topMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLineEdit" name="searchPattern"/> + </item> + <item> + <widget class="QLabel" name="searchStatus"> + <property name="minimumSize"> + <size> + <width>150</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="searchPrev"> + <property name="icon"> + <iconset theme="go-previous"/> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="searchNext"> + <property name="icon"> + <iconset theme="go-next"/> + </property> + </widget> + </item> + </layout> + </widget> + </item> <item> <widget class="QTextBrowser" name="textBrowser"> <property name="frameShape"> @@ -109,5 +173,24 @@ </layout> </widget> <resources/> - <connections/> + <connections> + <connection> + <sender>openSearchPanel</sender> + <signal>toggled(bool)</signal> + <receiver>search</receiver> + <slot>setVisible(bool)</slot> + </connection> + <connection> + <sender>openSearchPanel</sender> + <signal>clicked()</signal> + <receiver>searchPattern</receiver> + <slot>clear()</slot> + </connection> + <connection> + <sender>openSearchPanel</sender> + <signal>clicked()</signal> + <receiver>searchPattern</receiver> + <slot>setFocus()</slot> + </connection> + </connections> </ui> diff --git a/addons/katebuild-plugin/plugin_katebuild.cpp b/addons/katebuild-plugin/plugin_katebuild.cpp index e3e27b66e7..658fe81c59 100644 --- a/addons/katebuild-plugin/plugin_katebuild.cpp +++ b/addons/katebuild-plugin/plugin_katebuild.cpp @@ -67,6 +67,10 @@ #include <kde_terminal_interface.h> #include <kparts/part.h> +#include <qpalette.h> +#include <qtextbrowser.h> +#include <qtextcursor.h> +#include <qtextedit.h> using namespace Qt::Literals::StringLiterals; @@ -454,6 +458,15 @@ KateBuildView::KateBuildView(KateBuildPlugin *plugin, KTextEditor::MainWindow *m connect(m_buildUi.buildAgainButton, &QPushButton::clicked, this, &KateBuildView::slotBuildPreviousTarget); connect(m_buildUi.cancelBuildButton, &QPushButton::clicked, this, &KateBuildView::slotStop); + connect(m_buildUi.searchPattern, &QLineEdit::editingFinished, this, &KateBuildView::slotSearchBuildOutput); + connect(m_buildUi.searchPattern, &QLineEdit::textChanged, this, &KateBuildView::slotSearchPatternChanged); + connect(m_buildUi.searchNext, &QToolButton::clicked, this, [this]() { + gotoNthFound(m_currentFound + 1); + }); + connect(m_buildUi.searchPrev, &QToolButton::clicked, this, [this]() { + gotoNthFound(m_currentFound - 1); + }); + connect(m_targetsUi->buildButton, &QToolButton::clicked, this, &KateBuildView::slotBuildSelectedTarget); connect(m_targetsUi->runButton, &QToolButton::clicked, this, &KateBuildView::slotBuildAndRunSelectedTarget); connect(m_targetsUi, &TargetsUi::enterPressed, this, &KateBuildView::slotBuildAndRunSelectedTarget); @@ -1696,6 +1709,102 @@ KateBuildView::OutputLine KateBuildView::processOutputLine(const QString &line) return {.category = category, .lineStr = line, .message = msg, .file = filename, .lineNr = line_n.toInt(), .column = col_n.toInt()}; } +/******************************************************************/ + +/** Build a list of search results */ +void KateBuildView::doSearchAll(QString text) +{ + auto buildOutput = m_buildUi.textBrowser; + + if (buildOutput->document()->isEmpty() || text.isEmpty()) { + return; + } + + m_searchFound.clear(); + + auto saveCursor = buildOutput->textCursor(); + auto cursor = saveCursor; + cursor.movePosition(QTextCursor::Start); + buildOutput->setTextCursor(cursor); + + while (buildOutput->find(text)) { + m_searchFound.append(buildOutput->textCursor()); + } + + buildOutput->setTextCursor(saveCursor); +} + +/** Change the "Base" color of a widget. */ +/** For QLineEdit this is the background color. */ +void setBaseColor(QWidget *w, const QColor &color) +{ + if (w == nullptr) { + return; + } + + auto palette = w->palette(); + palette.setColor(QPalette::Base, color); + w->setPalette(palette); +} + +/** Highlights the Nth search result. */ +void KateBuildView::gotoNthFound(qsizetype n) +{ + auto buildOutput = m_buildUi.textBrowser; + auto searchText = m_buildUi.searchPattern->text(); + + if (buildOutput->document()->isEmpty() || searchText.isEmpty()) { + m_buildUi.searchStatus->clear(); + return; + } + + if (m_searchFound.empty()) { + doSearchAll(searchText); + if (m_searchFound.empty()) { + m_buildUi.searchStatus->clear(); + setBaseColor(m_buildUi.searchPattern, Qt::red); + return; + } + n = 0; + } + + if (n < 0) + n = m_searchFound.size() - 1; + if (n >= m_searchFound.size()) + n = 0; + + m_buildUi.searchStatus->setText(QStringLiteral("%1/%2").arg(n + 1).arg(m_searchFound.size())); + buildOutput->setTextCursor(m_searchFound.at(n)); + m_currentFound = n; +} + +/** The user has entered a search pattern. */ +void KateBuildView::slotSearchBuildOutput() +{ + auto input = qobject_cast<QLineEdit *>(sender()); + if (input == nullptr) { + return; + } + + doSearchAll(input->text()); + setBaseColor(input, m_searchFound.size() > 0 ? Qt::white : Qt::red); + gotoNthFound(0); +} + +/** The user is changing the search pattern. */ +void KateBuildView::slotSearchPatternChanged() +{ + m_searchFound = {}; + m_currentFound = 0; + + auto cursor = m_buildUi.textBrowser->textCursor(); + cursor.clearSelection(); + m_buildUi.textBrowser->setTextCursor(cursor); + + setBaseColor(m_buildUi.searchPattern, Qt::white); + m_buildUi.searchStatus->clear(); +} + /******************************************************************/ void KateBuildView::slotPluginViewCreated(const QString &name, QObject *pluginView) { diff --git a/addons/katebuild-plugin/plugin_katebuild.h b/addons/katebuild-plugin/plugin_katebuild.h index e73e1ef195..26cd925bb1 100644 --- a/addons/katebuild-plugin/plugin_katebuild.h +++ b/addons/katebuild-plugin/plugin_katebuild.h @@ -39,6 +39,7 @@ #include <KConfigGroup> #include <KXMLGUIClient> +#include <qtextdocument.h> #include "diagnostics/diagnosticview.h" #include "targets.h" @@ -108,6 +109,9 @@ private Q_SLOTS: void slotRunAfterBuild(); void slotUpdateTextBrowser(); + void slotSearchBuildOutput(); + void slotSearchPatternChanged(); + void handleEsc(QEvent *e); /** @@ -127,6 +131,8 @@ private Q_SLOTS: protected: bool eventFilter(QObject *obj, QEvent *ev) override; + void doSearchAll(QString text); + void gotoNthFound(qsizetype n); private: struct OutputLine { @@ -217,6 +223,9 @@ private: bool m_addingProjTargets = false; QSet<QString> m_saveProjectTargetDirs; + QList<QTextCursor> m_searchFound; + qsizetype m_currentFound; + /** * current project plugin view, if any */