[utilities/kate] addons/katebuild-plugin: Add text search to the build output

Christoph Cullmann <[email protected]>
Newsgroups gmane.comp.kde.cvs,gmane.comp.kde.doc
Message-ID <[email protected]>
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
      */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.