TextEditor: speed up searching in huge documents

Avoid creating QTextCursors for checking whether a search result is
inside the find scope. QTextCursor::setPosition seems to layout the
block the cursor is positioned at, which is not for free and not needed
to verify whether a search result is inside the find scope.

Change-Id: Ia1658fbbaa89a61f862e0b97eaa5b059972e2311
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-10-12 10:05:38 +02:00
parent 0aded7311a
commit 483a340e73
5 changed files with 48 additions and 15 deletions

View File

@@ -64,6 +64,16 @@ Position Position::fromCursor(const QTextCursor &c)
return c.isNull() ? Position{} : Position{c.blockNumber() + 1, c.positionInBlock()};
}
int Position::toPositionInDocument(const QTextDocument *document) const
{
QTC_ASSERT(document, return -1);
const QTextBlock block = document->findBlockByNumber(line - 1);
if (block.isValid())
return block.position() + qMin(column, block.length() - 1);
return -1;
}
int Range::length(const QString &text) const
{
if (end.line < begin.line)

View File

@@ -33,6 +33,8 @@ public:
static Position fromFileName(QStringView fileName, int &postfixPos);
static Position fromPositionInDocument(const QTextDocument *document, int pos);
static Position fromCursor(const QTextCursor &cursor);
int toPositionInDocument(const QTextDocument *document) const;
};
class QTCREATOR_UTILS_EXPORT Range

View File

@@ -425,9 +425,17 @@ bool BaseTextFind::inScope(const QTextCursor &candidate) const
return false;
if (d->m_scope.isNull())
return true;
return Utils::anyOf(d->m_scope, [candidate](const QTextCursor &scope){
return candidate.selectionStart() >= scope.selectionStart()
&& candidate.selectionEnd() <= scope.selectionEnd();
return inScope(candidate.selectionStart(), candidate.selectionEnd());
}
bool BaseTextFind::inScope(int candidateStart, int candidateEnd) const
{
if (d->m_scope.isNull())
return true;
if (candidateStart > candidateEnd)
std::swap(candidateStart, candidateEnd);
return Utils::anyOf(d->m_scope, [&](const QTextCursor &scope) {
return candidateStart >= scope.selectionStart() && candidateEnd <= scope.selectionEnd();
});
}

View File

@@ -50,6 +50,7 @@ public:
using CursorProvider = std::function<Utils::MultiTextCursor ()>;
void setMultiTextCursorProvider(const CursorProvider &provider);
bool inScope(const QTextCursor &candidate) const;
bool inScope(int candidateStart, int candidateEnd) const;
static QRegularExpression regularExpression(const QString &txt, Utils::FindFlags flags);

View File

@@ -4031,10 +4031,7 @@ void TextEditorWidgetPrivate::highlightSearchResults(const QTextBlock &block, co
const int start = blockPosition + idx;
const int end = start + l;
QTextCursor result = cursor;
result.setPosition(start);
result.setPosition(end, QTextCursor::KeepAnchor);
if (!q->inFindScope(result))
if (!m_find->inScope(start, end))
continue;
// check if the result is inside the visibale area for long blocks
@@ -6884,9 +6881,16 @@ void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar()
Utils::onResultReady(m_searchFuture, this, [this](const SearchResultItems &resultList) {
QList<SearchResult> results;
for (const SearchResultItem &result : resultList) {
SearchResult searchResult;
if (q->inFindScope(selectRange(q->document(), result.mainRange(), &searchResult)))
results << searchResult;
int start = result.mainRange().begin.toPositionInDocument(m_document->document());
if (start < 0)
continue;
int end = result.mainRange().end.toPositionInDocument(m_document->document());
if (end < 0)
continue;
if (start > end)
std::swap(start, end);
if (m_find->inScope(start, end))
results << SearchResult{start, start - end};
}
m_searchResults << results;
addSearchResultsToScrollBar(results);
@@ -6924,12 +6928,20 @@ void TextEditorWidgetPrivate::addSearchResultsToScrollBar(const QVector<SearchRe
for (const SearchResult &result : results) {
const QTextBlock &block = q->document()->findBlock(result.start);
if (block.isValid() && block.isVisible()) {
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();
const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber();
for (int line = firstLine; line <= lastLine; ++line) {
if (q->lineWrapMode() == QPlainTextEdit::WidgetWidth) {
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();
const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber();
for (int line = firstLine; line <= lastLine; ++line) {
m_highlightScrollBarController->addHighlight(
{Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line,
Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority});
}
} else {
m_highlightScrollBarController->addHighlight(
{Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line,
Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority});
{Constants::SCROLL_BAR_SEARCH_RESULT,
block.blockNumber(),
Theme::TextEditor_SearchResult_ScrollBarColor,
Highlight::HighPriority});
}
}
}