From 37f2a065852ff3a140edf6caab03ee08d9af3d85 Mon Sep 17 00:00:00 2001 From: liuzhangjian Date: Mon, 22 Jul 2024 18:00:26 +0800 Subject: [PATCH] fix: [search] Fixed an issue where advanced search could not find results as title Log: fix issue --- src/plugins/find/constants.h | 8 +- src/plugins/find/gui/advancedsearchwidget.cpp | 8 +- .../find/gui/searchresultitemdelegate.cpp | 8 +- src/plugins/find/gui/searchresultmodel.cpp | 4 +- .../worker/searchreplaceworker.cpp | 191 +++++++++++++----- .../worker/searchreplaceworker_p.h | 6 +- 6 files changed, 160 insertions(+), 65 deletions(-) diff --git a/src/plugins/find/constants.h b/src/plugins/find/constants.h index 4e9ae07ca..f196d748b 100644 --- a/src/plugins/find/constants.h +++ b/src/plugins/find/constants.h @@ -12,7 +12,7 @@ enum ResultRole { LineRole = Qt::UserRole + 1, ColumnRole, KeywordRole, - MatchedTextRole, + MatchedLengthRole, FilePathRole, ReplaceTextRole }; @@ -25,8 +25,8 @@ enum SearchScope { enum SearchFlag { SearchNoFlag = 0, - SearchCaseSensitive = 1, - SearchWholeWord = 1 << 1, + SearchCaseSensitively = 1, + SearchWholeWords = 1 << 1, SearchRegularExpression = 1 << 2 }; Q_DECLARE_FLAGS(SearchFlags, SearchFlag) @@ -42,7 +42,7 @@ struct FindItem int line = -1; int column = -1; QString keyword; - QString matchedText; + int matchedLength; QStringList capturedTexts; QString context; diff --git a/src/plugins/find/gui/advancedsearchwidget.cpp b/src/plugins/find/gui/advancedsearchwidget.cpp index 9107680e5..ea0e15a8f 100644 --- a/src/plugins/find/gui/advancedsearchwidget.cpp +++ b/src/plugins/find/gui/advancedsearchwidget.cpp @@ -295,8 +295,8 @@ SearchParams AdvancedSearchWidgetPrivate::searchParams() } params.scope = static_cast(scope); - params.flags |= caseBtn->isChecked() ? SearchCaseSensitive : SearchNoFlag; - params.flags |= wholeWordBtn->isChecked() ? SearchWholeWord : SearchNoFlag; + params.flags |= caseBtn->isChecked() ? SearchCaseSensitively : SearchNoFlag; + params.flags |= wholeWordBtn->isChecked() ? SearchWholeWords : SearchNoFlag; params.flags |= regexBtn->isChecked() ? SearchRegularExpression : SearchNoFlag; params.includeList = includeEdit->text().trimmed().split(",", QString::SkipEmptyParts); params.excludeList = excludeEdit->text().trimmed().split(",", QString::SkipEmptyParts); @@ -312,8 +312,8 @@ ReplaceParams AdvancedSearchWidgetPrivate::replaceParams(const QMapopenedFiles(); params.resultMap = resultMap; params.replaceText = replaceEdit->text(); - params.flags |= caseBtn->isChecked() ? SearchCaseSensitive : SearchNoFlag; - params.flags |= wholeWordBtn->isChecked() ? SearchWholeWord : SearchNoFlag; + params.flags |= caseBtn->isChecked() ? SearchCaseSensitively : SearchNoFlag; + params.flags |= wholeWordBtn->isChecked() ? SearchWholeWords : SearchNoFlag; params.flags |= regexBtn->isChecked() ? SearchRegularExpression : SearchNoFlag; return params; diff --git a/src/plugins/find/gui/searchresultitemdelegate.cpp b/src/plugins/find/gui/searchresultitemdelegate.cpp index 6689ec1b9..819acc1f9 100644 --- a/src/plugins/find/gui/searchresultitemdelegate.cpp +++ b/src/plugins/find/gui/searchresultitemdelegate.cpp @@ -293,7 +293,7 @@ void SearchResultItemDelegate::drawContextItem(QPainter *painter, const QStyleOp optionRect = drawOptionButton(painter, option, index); const auto &lineNumber = QString::number(index.data(LineRole).toInt()); - const auto &matchedText = index.data(MatchedTextRole).toString(); + const auto &matchedLength = index.data(MatchedLengthRole).toInt(); const auto &column = index.data(ColumnRole).toInt(); auto context = index.data(Qt::DisplayRole).toString(); const auto &replaceText = index.data(ReplaceTextRole).toString(); @@ -319,7 +319,7 @@ void SearchResultItemDelegate::drawContextItem(QPainter *painter, const QStyleOp if (optionRect.isValid()) textRect.setRight(optionRect.left() - Padding); if (!replaceText.isEmpty()) { - int replaceTextOffset = column + matchedText.length(); + int replaceTextOffset = column + matchedLength; context.insert(replaceTextOffset, replaceText); QColor matchedBackground; QColor replaceBackground; @@ -334,7 +334,7 @@ void SearchResultItemDelegate::drawContextItem(QPainter *painter, const QStyleOp replaceBackground.setNamedColor("#57965C"); replaceBackground.setAlpha(180); } - formats << createFormatRange(opt, column, matchedText.length(), {}, matchedBackground); + formats << createFormatRange(opt, column, matchedLength, {}, matchedBackground); formats << createFormatRange(opt, replaceTextOffset, replaceText.length(), {}, replaceBackground); } else { QColor background; @@ -345,7 +345,7 @@ void SearchResultItemDelegate::drawContextItem(QPainter *painter, const QStyleOp background.setNamedColor("#F2C55C"); background.setAlpha(220); } - formats << createFormatRange(opt, column, matchedText.length(), {}, background); + formats << createFormatRange(opt, column, matchedLength, {}, background); } drawDisplay(painter, option, textRect, context, formats); } diff --git a/src/plugins/find/gui/searchresultmodel.cpp b/src/plugins/find/gui/searchresultmodel.cpp index 5eef923cd..1903f690f 100644 --- a/src/plugins/find/gui/searchresultmodel.cpp +++ b/src/plugins/find/gui/searchresultmodel.cpp @@ -231,8 +231,8 @@ QVariant SearchResultModel::data(const FindItem &item, int role) const return item.column; case KeywordRole: return item.keyword; - case MatchedTextRole: - return item.matchedText; + case MatchedLengthRole: + return item.matchedLength; case ReplaceTextRole: if (!item.capturedTexts.isEmpty()) return Utils::expandRegExpReplacement(replaceText, item.capturedTexts); diff --git a/src/plugins/find/maincontroller/worker/searchreplaceworker.cpp b/src/plugins/find/maincontroller/worker/searchreplaceworker.cpp index 15fa24d5d..1776e55c8 100644 --- a/src/plugins/find/maincontroller/worker/searchreplaceworker.cpp +++ b/src/plugins/find/maincontroller/worker/searchreplaceworker.cpp @@ -40,7 +40,9 @@ void SearchReplaceWorkerPrivate::startNextJob() std::bind(&SearchReplaceWorker::handleReadSearchResult, q, job.keyword, job.flags)); connect(process.get(), qOverload(&QProcess::finished), q, &SearchReplaceWorker::processDone); - process->start(job.cmd); + process->setProgram(job.program); + process->setArguments(job.arguments); + process->start(); if (!job.channelData.isEmpty()) { process->write(job.channelData.toUtf8()); process->closeWriteChannel(); @@ -58,20 +60,17 @@ void SearchReplaceWorkerPrivate::createSearchJob(const SearchParams ¶ms) for (const auto &file : tmpParams.editFileList) { const auto &job = buildSearchJob({ file }, tmpParams.includeList, tmpParams.excludeList, tmpParams.keyword, tmpParams.flags, true); - if (!job.cmd.isEmpty()) - jobList << job; + jobList << job; } const auto &job = buildSearchJob(tmpParams.projectFileList, tmpParams.includeList, tmpParams.excludeList, tmpParams.keyword, tmpParams.flags, false); - if (!job.cmd.isEmpty()) - jobList << job; + jobList << job; } break; case CurrentFile: { const auto &job = buildSearchJob(params.editFileList, params.includeList, params.excludeList, params.keyword, params.flags, true); - if (!job.cmd.isEmpty()) - jobList << job; + jobList << job; } break; default: break; @@ -89,43 +88,157 @@ SearchReplaceWorkerPrivate::buildSearchJob(const QStringList &fileList, return {}; Job job; + job.program = "grep"; job.keyword = keyword; job.flags = flags; - QStringList cmd; - cmd << "grep -Hn"; - if (!flags.testFlag(SearchCaseSensitive)) - cmd << "-i"; + job.arguments << "-Hn"; + if (!flags.testFlag(SearchCaseSensitively)) + job.arguments << "-i"; - if (flags.testFlag(SearchWholeWord)) { - cmd << "-w"; + if (flags.testFlag(SearchWholeWords)) { + job.arguments << "-w"; } if (flags.testFlag(SearchRegularExpression)) { - cmd << "-P"; + job.arguments << "-P"; } else { - cmd << "-F"; + job.arguments << "-F"; } if (!includeList.isEmpty()) - cmd << "--include=" + includeList.join(" --include="); + job.arguments << "--include=" + includeList.join(" --include="); if (!excludeList.isEmpty()) - cmd << "--exclude=" + excludeList.join(" --exclude="); - cmd << "\"" + keyword + "\""; + job.arguments << "--exclude=" + excludeList.join(" --exclude="); + job.arguments << keyword; if (isOpenedFile) { if (!editSrv) editSrv = dpfGetService(EditorService); job.channelData = editSrv->fileText(fileList.first()); - cmd << "--label=" + fileList.first(); + job.arguments << "--label=" + fileList.first(); } else { - QString searchPath = fileList.join(' '); - cmd << searchPath; + job.arguments << fileList; } - job.cmd = cmd.join(' '); return job; } +void SearchReplaceWorkerPrivate::parseResultWithRegExp(const QString &fileName, const QString &keyword, + const QString &contents, int line, SearchFlags flags) +{ + const QString term = flags & SearchWholeWords + ? QString::fromLatin1("\\b%1\\b").arg(keyword) + : keyword; + const auto patternOptions = (flags & SearchCaseSensitively) + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption; + const QRegularExpression expression = QRegularExpression(term, patternOptions); + if (!expression.isValid()) + return; + + QRegularExpressionMatch match; + int lengthOfContents = contents.length(); + int pos = 0; + while ((match = expression.match(contents, pos)).hasMatch()) { + pos = match.capturedStart(); + FindItem findItem; + findItem.filePathName = fileName; + findItem.line = line; + findItem.keyword = keyword; + findItem.context = contents; + findItem.column = pos; + findItem.matchedLength = match.capturedLength(); + findItem.capturedTexts = match.capturedTexts(); + + { + QMutexLocker lk(&mutex); + searchResults.append(findItem); + } + ++resultCount; + if (match.capturedLength() == 0) + break; + pos += match.capturedLength(); + if (pos >= lengthOfContents) + break; + } +} + +void SearchReplaceWorkerPrivate::parseResultWithoutRegExp(const QString &fileName, const QString &keyword, + const QString &contents, int line, SearchFlags flags) +{ + const bool caseSensitive = (flags & SearchCaseSensitively); + const bool wholeWord = (flags & SearchWholeWords); + const QString keywordLower = keyword.toLower(); + const QString keywordUpper = keyword.toUpper(); + const int keywordMaxIndex = keyword.length() - 1; + const QChar *keywordData = keyword.constData(); + const QChar *keywordDataLower = keywordLower.constData(); + const QChar *keywordDataUpper = keywordUpper.constData(); + + const int contentsLength = contents.length(); + const QChar *contentsPtr = contents.constData(); + const QChar *contentsEnd = contentsPtr + contentsLength - 1; + for (const QChar *regionPtr = contentsPtr; regionPtr + keywordMaxIndex <= contentsEnd; ++regionPtr) { + const QChar *regionEnd = regionPtr + keywordMaxIndex; + if ((caseSensitive && *regionPtr == keywordData[0] + && *regionEnd == keywordData[keywordMaxIndex]) + || + // case insensitive + (!caseSensitive && (*regionPtr == keywordDataLower[0] || *regionPtr == keywordDataUpper[0]) + && (*regionEnd == keywordDataLower[keywordMaxIndex] + || *regionEnd == keywordDataUpper[keywordMaxIndex]))) { + bool equal = true; + + // whole word check + const QChar *beforeRegion = regionPtr - 1; + const QChar *afterRegion = regionEnd + 1; + if (wholeWord + && (((beforeRegion >= contentsPtr) + && (beforeRegion->isLetterOrNumber() + || ((*beforeRegion) == QLatin1Char('_')))) + || ((afterRegion <= contentsEnd) + && (afterRegion->isLetterOrNumber() + || ((*afterRegion) == QLatin1Char('_')))))) { + equal = false; + } else { + // check all chars + int regionIndex = 1; + for (const QChar *regionCursor = regionPtr + 1; + regionCursor < regionEnd; + ++regionCursor, ++regionIndex) { + if ( // case sensitive + (caseSensitive + && *regionCursor != keywordData[regionIndex]) + || + // case insensitive + (!caseSensitive + && *regionCursor != keywordDataLower[regionIndex] + && *regionCursor != keywordDataUpper[regionIndex])) { + equal = false; + break; + } + } + } + + if (equal) { + FindItem result; + result.filePathName = fileName; + result.line = line; + result.column = regionPtr - contentsPtr; + result.context = contents; + result.keyword = keyword; + result.matchedLength = keywordMaxIndex + 1; + + ++resultCount; + regionPtr += keywordMaxIndex; + + QMutexLocker lk(&mutex); + searchResults.append(result); + } + } + } +} + void SearchReplaceWorkerPrivate::processWorkingFiles(QStringList &baseFiles, QStringList &openedFiles) { for (int i = 0; i < openedFiles.size();) { @@ -170,8 +283,8 @@ void SearchReplaceWorkerPrivate::replaceLocalFile(const QString &fileName, const offset = 0; lastReplaceLine = realLine; - offset += newText.length() - item.matchedText.length(); - lines[realLine].replace(index, item.matchedText.length(), newText); + offset += newText.length() - item.matchedLength; + lines[realLine].replace(index, item.matchedLength, newText); } QTextStream out(&file); @@ -201,8 +314,8 @@ void SearchReplaceWorkerPrivate::replaceOpenedFile(const QString &fileName, cons offset = 0; lastReplaceLine = realLine; - offset += newText.length() - item.matchedText.length(); - editSrv->replaceRange(fileName, realLine, index, item.matchedText.length(), newText); + offset += newText.length() - item.matchedLength; + editSrv->replaceRange(fileName, realLine, index, item.matchedLength, newText); } } @@ -282,30 +395,8 @@ void SearchReplaceWorker::handleReadSearchResult(const QString &keyword, SearchF auto line = regMatch.captured(2).toInt(); auto context = regMatch.captured(3); - QString pattern = flags.testFlag(SearchWholeWord) - ? QString::fromLatin1("\\b%1\\b").arg(keyword) - : keyword; - - QRegularExpression regex(pattern, flags.testFlag(SearchCaseSensitive) ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); - QRegularExpressionMatchIterator it = regex.globalMatch(context); - while (it.hasNext()) { - QRegularExpressionMatch match = it.next(); - if (match.hasMatch()) { - FindItem findItem; - findItem.filePathName = name; - findItem.line = line; - findItem.keyword = keyword; - findItem.context = context; - findItem.column = match.capturedStart(); - findItem.matchedText = match.captured(); - if (flags.testFlag(SearchRegularExpression)) - findItem.capturedTexts = match.capturedTexts(); - - QMutexLocker lk(&d->mutex); - d->searchResults.append(findItem); - ++d->resultCount; - } - } + flags.testFlag(SearchRegularExpression) ? d->parseResultWithRegExp(name, keyword, context, line, flags) + : d->parseResultWithoutRegExp(name, keyword, context, line, flags); } } diff --git a/src/plugins/find/maincontroller/worker/searchreplaceworker_p.h b/src/plugins/find/maincontroller/worker/searchreplaceworker_p.h index 002404227..7b89b56a1 100644 --- a/src/plugins/find/maincontroller/worker/searchreplaceworker_p.h +++ b/src/plugins/find/maincontroller/worker/searchreplaceworker_p.h @@ -15,7 +15,8 @@ class SearchReplaceWorkerPrivate : public QObject public: struct Job { - QString cmd; + QString program; + QStringList arguments; QString channelData; QString keyword; SearchFlags flags; @@ -34,6 +35,9 @@ class SearchReplaceWorkerPrivate : public QObject SearchFlags flags, bool isOpenedFile); + void parseResultWithRegExp(const QString &fileName, const QString &keyword, const QString &contents, int line, SearchFlags flags); + void parseResultWithoutRegExp(const QString &fileName, const QString &keyword, const QString &contents, int line, SearchFlags flags); + void processWorkingFiles(QStringList &baseFiles, QStringList &openedFiles); void replaceLocalFile(const QString &fileName, const QString &replacement, const FindItemList &itemList); Q_INVOKABLE void replaceOpenedFile(const QString &fileName, const QString &replacement, const FindItemList &itemList);