File indexing completed on 2024-04-28 05:49:12

0001 /*
0002     SPDX-FileCopyrightText: 2011-21 Kåre Särs <kare.sars@iki.fi>
0003     SPDX-FileCopyrightText: 2021 Christoph Cullmann <cullmann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #pragma once
0009 
0010 // Qt
0011 #include <QMutex>
0012 #include <QMutexLocker>
0013 #include <QObject>
0014 #include <QRegularExpression>
0015 #include <QRunnable>
0016 #include <QStringList>
0017 
0018 // std
0019 #include <atomic>
0020 
0021 // locals
0022 #include "MatchModel.h"
0023 
0024 class QString;
0025 class QUrl;
0026 class QFile;
0027 
0028 /**
0029  * Thread-safe worklist to feed the SearchDiskFiles runnables.
0030  */
0031 class SearchDiskFilesWorkList
0032 {
0033 public:
0034     /**
0035      * Default constructor => nothing to be done
0036      */
0037     SearchDiskFilesWorkList() = default;
0038 
0039     /**
0040      * Any workers running?
0041      * @return any worker running?
0042      */
0043     bool isRunning()
0044     {
0045         QMutexLocker lock(&m_mutex);
0046         return m_currentRunningRunnables > 0;
0047     }
0048 
0049     /**
0050      * Search canceled?
0051      * @return canceled?
0052      */
0053     bool isCanceled()
0054     {
0055         return m_canceled;
0056     }
0057 
0058     /**
0059      * Init the search, shall only be done if not running.
0060      * @param files files to search
0061      * @param numberOfWorkers number of workers we will spawn
0062      */
0063     void init(const QStringList &files, int numberOfWorkers)
0064     {
0065         /**
0066          * ensure sane initial state: last search is done!
0067          * should hold even if canceled, the last markOnRunnableAsDone clears all fields!
0068          */
0069         QMutexLocker lock(&m_mutex);
0070         Q_ASSERT(m_currentRunningRunnables == 0);
0071         Q_ASSERT(m_filesToSearch.isEmpty());
0072         Q_ASSERT(m_filesToSearchIndex == 0);
0073 
0074         /**
0075          * we shall not be called without any work!
0076          */
0077         Q_ASSERT(!files.isEmpty());
0078         Q_ASSERT(numberOfWorkers > 0);
0079 
0080         /**
0081          * init work
0082          */
0083         m_currentRunningRunnables = numberOfWorkers;
0084         m_filesToSearch = files;
0085         m_filesToSearchIndex = 0;
0086         m_canceled = false;
0087     }
0088 
0089     /**
0090      * Get one file to search if still some there.
0091      * Will return empty string if no further work (or canceled)
0092      * @return file to search next or empty string
0093      */
0094     QString nextFileToSearch()
0095     {
0096         QMutexLocker lock(&m_mutex);
0097         if (m_filesToSearchIndex >= m_filesToSearch.size()) {
0098             return QString();
0099         }
0100 
0101         // else return file, shall not be empty and advance one file
0102         const auto file = m_filesToSearch.at(m_filesToSearchIndex);
0103         Q_ASSERT(!file.isEmpty());
0104         ++m_filesToSearchIndex;
0105         return file;
0106     }
0107 
0108     /**
0109      * Mark one runnable as done.
0110      */
0111     void markOnRunnableAsDone()
0112     {
0113         QMutexLocker lock(&m_mutex);
0114         Q_ASSERT(m_currentRunningRunnables > 0);
0115         --m_currentRunningRunnables;
0116 
0117         // if we are done, cleanup
0118         if (m_currentRunningRunnables == 0) {
0119             m_filesToSearch.clear();
0120             m_filesToSearchIndex = 0;
0121         }
0122     }
0123 
0124     /**
0125      * Cancel the work.
0126      */
0127     void cancel()
0128     {
0129         QMutexLocker lock(&m_mutex);
0130         m_canceled = true;
0131         m_filesToSearch.clear();
0132         m_filesToSearchIndex = 0;
0133     }
0134 
0135 private:
0136     /**
0137      * non-recursive mutex to lock internals, only locked a very short time
0138      */
0139     QMutex m_mutex;
0140 
0141     /**
0142      * current number of still active runnables, if == 0 => nothing running
0143      */
0144     int m_currentRunningRunnables{0}; // guarded by m_mutex
0145 
0146     /**
0147      * worklist => files to search in on the disk
0148      */
0149     QStringList m_filesToSearch; // guarded by m_mutex
0150 
0151     /**
0152      * current index into the worklist => next file to search
0153      * we don't do modify the stringlist, we just move the index
0154      */
0155     int m_filesToSearchIndex{0}; // guarded by m_mutex
0156 
0157     /**
0158      * was the search canceled?
0159      */
0160     std::atomic_bool m_canceled{false};
0161 };
0162 
0163 class SearchDiskFiles : public QObject, public QRunnable
0164 {
0165     Q_OBJECT
0166 
0167 public:
0168     SearchDiskFiles(SearchDiskFilesWorkList &worklist, const QRegularExpression &regexp, const bool includeBinaryFiles);
0169 
0170     void run() override;
0171 
0172 Q_SIGNALS:
0173     void matchesFound(const QUrl &url, const QList<KateSearchMatch> &searchMatches, KTextEditor::Document *doc = nullptr);
0174 
0175 private:
0176     QList<KateSearchMatch> searchSingleLineRegExp(QFile &file);
0177     QList<KateSearchMatch> searchMultiLineRegExp(QFile &file);
0178 
0179 private:
0180     SearchDiskFilesWorkList &m_worklist;
0181     const QRegularExpression m_regExp;
0182     bool m_includeBinaryFiles = false;
0183 };