diff options
Diffstat (limited to 'src/assistant/help/qhelpsearchenginecore.cpp')
-rw-r--r-- | src/assistant/help/qhelpsearchenginecore.cpp | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/src/assistant/help/qhelpsearchenginecore.cpp b/src/assistant/help/qhelpsearchenginecore.cpp new file mode 100644 index 000000000..a8b032b3d --- /dev/null +++ b/src/assistant/help/qhelpsearchenginecore.cpp @@ -0,0 +1,255 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qhelpsearchenginecore.h" +#include "qhelpenginecore.h" + +#include "qhelpsearchindexreader_p.h" +#include "qhelpsearchindexwriter_p.h" + +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qpointer.h> +#include <QtCore/qtimer.h> + +QT_BEGIN_NAMESPACE + +using namespace fulltextsearch; + +class QHelpSearchEngineCorePrivate +{ +public: + QString indexFilesFolder() const + { + QString indexFilesFolder = QLatin1String(".fulltextsearch"); + if (m_helpEngine && !m_helpEngine->collectionFile().isEmpty()) { + const QFileInfo fi(m_helpEngine->collectionFile()); + indexFilesFolder = fi.absolutePath() + QDir::separator() + QLatin1Char('.') + + fi.fileName().left(fi.fileName().lastIndexOf(QLatin1String(".qhc"))); + } + return indexFilesFolder; + } + + void updateIndex(bool reindex) + { + if (m_helpEngine.isNull()) + return; + + if (!QFile::exists(QFileInfo(m_helpEngine->collectionFile()).path())) + return; + + if (!m_indexWriter) { + m_indexWriter.reset(new QHelpSearchIndexWriter); + + QObject::connect(m_indexWriter.get(), &QHelpSearchIndexWriter::indexingStarted, + q, &QHelpSearchEngineCore::indexingStarted); + QObject::connect(m_indexWriter.get(), &QHelpSearchIndexWriter::indexingFinished, + q, &QHelpSearchEngineCore::indexingFinished); + } + + m_indexWriter->cancelIndexing(); + m_indexWriter->updateIndex(m_helpEngine->collectionFile(), indexFilesFolder(), reindex); + } + + void search(const QString &searchInput) + { + if (m_helpEngine.isNull()) + return; + + if (!QFile::exists(QFileInfo(m_helpEngine->collectionFile()).path())) + return; + + if (!m_indexReader) { + m_indexReader.reset(new QHelpSearchIndexReader); + QObject::connect(m_indexReader.get(), &QHelpSearchIndexReader::searchingStarted, + q, &QHelpSearchEngineCore::searchingStarted); + QObject::connect(m_indexReader.get(), &QHelpSearchIndexReader::searchingFinished, + q, &QHelpSearchEngineCore::searchingFinished); + } + + m_searchInput = searchInput; + m_indexReader->cancelSearching(); + m_indexReader->search(m_helpEngine->collectionFile(), indexFilesFolder(), searchInput, + m_helpEngine->usesFilterEngine()); + } + + QHelpSearchEngineCore *q = nullptr; + + bool m_isIndexingScheduled = false; + + std::unique_ptr<QHelpSearchIndexReader> m_indexReader; + std::unique_ptr<QHelpSearchIndexWriter> m_indexWriter; + + QPointer<QHelpEngineCore> m_helpEngine; + QString m_searchInput; +}; + +/*! + \class QHelpSearchEngineCore + \since 6.8 + \inmodule QtHelp + \brief The QHelpSearchEngineCore class provides access to index and + search documentation. + + Before the search engine can be used, one has to instantiate at least a + QHelpEngineCore object that needs to be passed to the search engines constructor. + This is required as the search engine needs to be connected to the help + engines setupFinished() signal to know when it can start to index documentation. + + After starting the indexing process the signal indexingStarted() is emitted and + on the end of the indexing process the indexingFinished() is emitted. To stop + the indexing one can call cancelIndexing(). + + When the indexing process has finished, the search engine can be used to + search through the index for a given term using the search() function. When + the search input is passed to the search engine, the searchingStarted() + signal is emitted. When the search finishes, the searchingFinished() signal + is emitted. The search process can be stopped by calling cancelSearching(). + + If the search succeeds, searchingFinished() is called with the search result + count to fetch the search results from the search engine. Calling the + searchResults() function with a range returns a list of QHelpSearchResult + objects within the range. The results consist of the document title and URL, + as well as a snippet from the document that contains the best match for the + search input. +*/ + +/*! + \fn void QHelpSearchEngineCore::indexingStarted() + + This signal is emitted when indexing process is started. +*/ + +/*! + \fn void QHelpSearchEngineCore::indexingFinished() + + This signal is emitted when the indexing process is complete. +*/ + +/*! + \fn void QHelpSearchEngineCore::searchingStarted() + + This signal is emitted when the search process is started. +*/ + +/*! + \fn void QHelpSearchEngineCore::searchingFinished(int searchResultCount) + + This signal is emitted when the search process is complete. + The search result count is stored in \a searchResultCount. +*/ + +/*! + Constructs a new search engine with the given \a parent. The search engine + uses the given \a helpEngine to access the documentation that needs to be indexed. + The QHelpEngine's setupFinished() signal is automatically connected to the + QHelpSearchEngineCore's indexing function, so that new documentation will be indexed + after the signal is emitted. +*/ +QHelpSearchEngineCore::QHelpSearchEngineCore(QHelpEngineCore *helpEngine) + : d(new QHelpSearchEngineCorePrivate) +{ + d->q = this; + d->m_helpEngine = helpEngine; + connect(helpEngine, &QHelpEngineCore::setupFinished, + this, &QHelpSearchEngineCore::scheduleIndexDocumentation); +} + +/*! + Destructs the search engine. +*/ +QHelpSearchEngineCore::~QHelpSearchEngineCore() +{ + delete d; +} + +/*! + Returns the number of results the search engine found. +*/ +int QHelpSearchEngineCore::searchResultCount() const +{ + return d->m_indexReader ? d->m_indexReader->searchResultCount() : 0;; +} + +/*! + Returns a list of search results within the range from the index + specified by \a start to the index specified by \a end. +*/ +QList<QHelpSearchResult> QHelpSearchEngineCore::searchResults(int start, int end) const +{ + return d->m_indexReader ? d->m_indexReader->searchResults(start, end) + : QList<QHelpSearchResult>(); +} + +/*! + Returns the phrase that was last searched for. +*/ +QString QHelpSearchEngineCore::searchInput() const +{ + return d->m_searchInput; +} + +/*! + Forces the search engine to reindex all documentation files. +*/ +void QHelpSearchEngineCore::reindexDocumentation() +{ + d->updateIndex(true); +} + +/*! + Stops the indexing process. +*/ +void QHelpSearchEngineCore::cancelIndexing() +{ + if (d->m_indexWriter) + d->m_indexWriter->cancelIndexing(); +} + +/*! + Stops the search process. +*/ +void QHelpSearchEngineCore::cancelSearching() +{ + if (d->m_indexReader) + d->m_indexReader->cancelSearching(); +} + +/*! + Starts the search process using the given search phrase \a searchInput. + + The phrase may consist of several words. By default, the search engine returns + the list of documents that contain all the specified words. + The phrase may contain any combination of the logical operators AND, OR, and + NOT. The operator must be written in all capital letters, otherwise it will + be considered a part of the search phrase. + + If double quotation marks are used to group the words, + the search engine will search for an exact match of the quoted phrase. + + For more information about the text query syntax, + see \l {https://sqlite.org/fts5.html#full_text_query_syntax} + {SQLite FTS5 Extension}. +*/ +void QHelpSearchEngineCore::search(const QString &searchInput) +{ + d->search(searchInput); +} + +/*! + \internal +*/ +void QHelpSearchEngineCore::scheduleIndexDocumentation() +{ + if (d->m_isIndexingScheduled) + return; + + d->m_isIndexingScheduled = true; + QTimer::singleShot(0, this, [this] { + d->m_isIndexingScheduled = false; + d->updateIndex(false); + }); +} + +QT_END_NAMESPACE |