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
      */

Reply via email to