17 #include <QDataStream>
18 #include <QRegularExpression>
19 #include <QRegularExpressionMatch>
20 #include <QStringList>
22 #include "../util/btassert.h"
39 [](
auto const *
const m) { return m->hasIndex(); }));
46 r.reserve(modules.size());
47 for (
auto const *
const m : modules)
59 QStringList tokenList;
62 bool tokenHasLetterOrNumber =
false;
63 bool tokenHasStar =
false;
64 auto const pushToken =
66 if (tokenHasLetterOrNumber
68 || (!tokenHasStar && !token.isEmpty()))
69 tokenList.append(token);
71 tokenHasLetterOrNumber =
false;
74 for (
int cnt = 0; cnt < queryString.size(); cnt++) {
75 auto const c = queryString[cnt];
76 if (c.isLetterOrNumber()) {
78 tokenHasLetterOrNumber =
true;
79 }
else if (c ==
'*') {
82 }
else if (c ==
'?') {
84 }
else if (c ==
'!' || c ==
'-' || c ==
'+') {
87 }
else if ((c ==
'|' || c ==
'&')
88 && cnt + 1 < queryString.size()
89 && queryString[cnt + 1] == c)
92 tokenList.append(QString(2, c));
101 for (
auto it = tokenList.begin(); it != tokenList.end(); ++it) {
108 || ((*it) == QStringLiteral(
"NOT"))
111 it = tokenList.erase(it);
112 if (it == tokenList.end())
114 it = tokenList.erase(it);
115 if (it == tokenList.end())
123 else if (((*it) == QStringLiteral(
"||"))
124 || ((*it) == QStringLiteral(
"OR"))
126 || ((*it) == QStringLiteral(
"AND"))
127 || ((*it) == QStringLiteral(
"&&")))
129 it = tokenList.erase(it);
130 if (it == tokenList.end())
139 else if ( (pos = (*it).indexOf(
'^') ) >= 0 ) {
140 (*it) = (*it).left(pos - 1);
144 else if ( (pos = (*it).indexOf(
'~') ) >= 0 ) {
145 (*it) = (*it).left(pos - 2) +
'*';
157 QString
const & searchedText,
158 bool plainSearchedText)
160 static Qt::CaseSensitivity
const cs = Qt::CaseInsensitive;
162 static auto const skipIndexToTagEnd =
163 [](
auto const & str,
auto i) {
164 static QRegularExpression
const re(
165 QStringLiteral(R
"PCRE(["'>])PCRE"));
167 i = str.indexOf(re, i);
171 auto const match = str.at(i);
172 if (match == QLatin1Char(
'>'))
176 i = str.indexOf(match, ++i);
183 auto const bodyIndex =
185 static QRegularExpression
const tagRe(
186 QStringLiteral(R
"PCRE(<body(>|\\s))PCRE"));
187 auto const i = content.indexOf(tagRe);
188 return (i < 0) ? 0 : skipIndexToTagEnd(content, i + 5);
193 auto ret = content.mid(bodyIndex);
195 if (!plainSearchedText) {
197 for (
auto const & newSearchText
198 : searchedText.split(
spaceRegexp, Qt::SkipEmptyParts))
201 int sstIndex = newSearchText.indexOf(QStringLiteral(
"strong:"));
207 sstIndex = sstIndex + 7;
214 while ((strongIndex =
215 ret.indexOf(QStringLiteral(
"lemma="), strongIndex, cs))
220 int const idx1 = ret.indexOf(
'"', strongIndex) + 1;
221 int const idx2 = ret.indexOf(
'"', idx1 + 1);
226 if (ret.mid(idx1, idx2 - idx1)
227 .contains(newSearchText.mid(sstIndex, -1)))
229 static auto const rep3 =
230 QStringLiteral(R
"HTML(class="highlightwords" )HTML");
234 ret = ret.insert(strongIndex, rep3);
235 strongIndex += rep3.length();
247 if (plainSearchedText) {
248 auto words = searchedText.split(
spaceRegexp, Qt::SkipEmptyParts);
249 for (
auto & word : words)
250 word = QRegularExpression::escape(word);
253 QString wordsRegexString;
255 QString wordRegexString;
256 auto const wordSize = word.size();
257 wordRegexString.reserve(wordSize + 3);
259 static QRegularExpression
const wildCardRegex(
260 QStringLiteral(R
"PCRE([*?])PCRE"));
265 wordRegexString.append(
266 QRegularExpression::escape(
268 wordRegexString.append(word.at(
fragmentEnd) == QLatin1Char(
'*')
269 ? QStringLiteral(R
"PCRE(\S*?)PCRE")
270 : QStringLiteral(R"PCRE(\S)PCRE"));
274 wordRegexString.append(
277 if (!wordsRegexString.isEmpty())
278 wordsRegexString.append(QLatin1Char(
'|'));
279 wordsRegexString.append(wordRegexString);
283 QStringLiteral(R
"PCRE(\b(%1)\b)PCRE").arg(wordsRegexString),
284 QRegularExpression::CaseInsensitiveOption);
287 QStringList r(content.left(bodyIndex));
294 for (QRegularExpressionMatch match;;) {
296 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
306 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
307 r << fragment.mid(searchStart);
309 r << fragment.mid(searchStart).toString();
314 if (
auto const noMatchSize = i - searchStart) {
315 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
316 r << fragment.mid(searchStart, noMatchSize);
318 r << fragment.mid(searchStart, noMatchSize).toString();
321 r << QStringLiteral(R
"HTML(<span class="highlightwords">)HTML")
323 << QStringLiteral(R"HTML(</span>)HTML");
324 searchStart = i + match.capturedLength();
337 return r.join(QString());
343 auto words = orig.split(
spaceRegexp, Qt::SkipEmptyParts);
344 static QRegularExpression
const escapeRe(
345 QLatin1String(R
"PCRE(([\\+\-\!\(\)\:\^\]\[\"\{\}\~\*\?\|\&]))PCRE"));
346 for (
auto & word : words)
347 word = QStringLiteral(R
"("%1")").arg(word.replace(escapeRe, "\\\\1"));
348 return words.join(searchType ==
OrType
349 ? QStringLiteral(
" OR ")
350 : QStringLiteral(
" AND "));
356 out << static_cast<qint8>(searchType);
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
QList< CSwordModuleInfo const * > BtConstModuleList
static CSwordBackend & instance() noexcept
void setFilterOptions(const FilterOptions &options)
QRegularExpression highlightRegex
QDataStream & operator<<(QDataStream &out, const CSwordModuleSearch::SearchType &searchType)
for(auto const &word :queryParser(searchedText))
decltype(ret.size()) fragmentSize
QDataStream & operator>>(QDataStream &in, CSwordModuleSearch::SearchType &searchType)
QStringList r(content.left(bodyIndex))
static QRegularExpression const spaceRegexp(spaceRegexpString)
QStringList queryParser(QString const &queryString)
static auto const spaceRegexpString(QStringLiteral(R"PCRE(\s+)PCRE"))
QString prepareSearchText(QString const &orig, SearchType const searchType)
QString highlightSearchedText(QString const &content, QString const &searchedText, bool plainSearchedText)
Results search(QString const &searchText, BtConstModuleList const &modules, sword::ListKey scope)
std::vector< ModuleSearchResult > Results