Utils: fix Text::Range length and remove mid

Those functions are based on the assumption that the passed text starts
at the begin position, which was good enough for search results, but if
used in other parts of the codebase it might give unwanted results.
Calculate the length of the range now as expected and subtract the
beginning lines.
In order to still got the correct results for the text result texts
modify the result range to always start at the first line before
calculating the length of the range.
Also add tests for the modified functionality

Change-Id: I7ccd75b642dda6dd4f738877cbe3543d46c03652
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
David Schulz
2023-05-11 09:34:53 +02:00
parent 70609cdec6
commit 1a98dda5c4
8 changed files with 101 additions and 15 deletions

View File

@@ -58,22 +58,30 @@ Position Position::fromPositionInDocument(const QTextDocument *document, int pos
int Range::length(const QString &text) const int Range::length(const QString &text) const
{ {
if (end.line < begin.line)
return -1;
if (begin.line == end.line) if (begin.line == end.line)
return end.column - begin.column; return end.column - begin.column;
const int lineCount = end.line - begin.line; int index = 0;
int index = text.indexOf(QChar::LineFeed);
int currentLine = 1; int currentLine = 1;
while (index > 0 && currentLine < lineCount) { while (currentLine < begin.line) {
++index;
index = text.indexOf(QChar::LineFeed, index); index = text.indexOf(QChar::LineFeed, index);
if (index < 0)
return -1;
++index;
++currentLine; ++currentLine;
} }
const int beginIndex = index + begin.column;
if (index < 0) while (currentLine < end.line) {
return 0; index = text.indexOf(QChar::LineFeed, index);
if (index < 0)
return index - begin.column + end.column; return -1;
++index;
++currentLine;
}
return index + end.column - beginIndex;
} }
bool Range::operator==(const Range &other) const bool Range::operator==(const Range &other) const

View File

@@ -37,7 +37,6 @@ public:
class QTCREATOR_UTILS_EXPORT Range class QTCREATOR_UTILS_EXPORT Range
{ {
public: public:
QString mid(const QString &text) const { return text.mid(begin.column, length(text)); }
int length(const QString &text) const; int length(const QString &text) const;
Position begin; Position begin;
@@ -97,3 +96,4 @@ QTCREATOR_UTILS_EXPORT QString utf16LineTextInUtf8Buffer(const QByteArray &utf8B
} // Utils } // Utils
Q_DECLARE_METATYPE(Utils::Text::Position) Q_DECLARE_METATYPE(Utils::Text::Position)
Q_DECLARE_METATYPE(Utils::Text::Range)

View File

@@ -322,9 +322,13 @@ QVariant SearchResultTreeModel::data(const SearchResultTreeItem *row, int role)
case ItemDataRoles::ResultBeginColumnNumberRole: case ItemDataRoles::ResultBeginColumnNumberRole:
result = row->item.mainRange().begin.column; result = row->item.mainRange().begin.column;
break; break;
case ItemDataRoles::SearchTermLengthRole: case ItemDataRoles::SearchTermLengthRole:{
result = row->item.mainRange().length(row->item.lineText()); Text::Range range = row->item.mainRange();
range.end.line -= range.begin.line - 1;
range.begin.line = 1;
result = range.length(row->item.lineText());
break; break;
}
case ItemDataRoles::ContainingFunctionNameRole: case ItemDataRoles::ContainingFunctionNameRole:
result = row->item.containingFunctionName().value_or(QString{}); result = row->item.containingFunctionName().value_or(QString{});
break; break;

View File

@@ -486,9 +486,13 @@ FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems
if (item.userData().canConvert<QStringList>() && !item.userData().toStringList().isEmpty()) { if (item.userData().canConvert<QStringList>() && !item.userData().toStringList().isEmpty()) {
replacement = Utils::expandRegExpReplacement(text, item.userData().toStringList()); replacement = Utils::expandRegExpReplacement(text, item.userData().toStringList());
} else if (preserveCase) { } else if (preserveCase) {
const QString originalText = (item.mainRange().length(item.lineText()) == 0) Text::Range range = item.mainRange();
? item.lineText() range.end.line -= range.begin.line - 1;
: item.mainRange().mid(item.lineText()); range.begin.line = 1;
QString originalText = item.lineText();
const int rangeLength = range.length(item.lineText());
if (rangeLength > 0)
originalText = originalText.mid(range.begin.column, rangeLength);
replacement = Utils::matchCaseReplacement(originalText, text); replacement = Utils::matchCaseReplacement(originalText, text);
} else { } else {
replacement = text; replacement = text;

View File

@@ -17,4 +17,5 @@ add_subdirectory(stringutils)
add_subdirectory(tasktree) add_subdirectory(tasktree)
add_subdirectory(templateengine) add_subdirectory(templateengine)
add_subdirectory(treemodel) add_subdirectory(treemodel)
add_subdirectory(text)
add_subdirectory(unixdevicefileaccess) add_subdirectory(unixdevicefileaccess)

View File

@@ -0,0 +1,4 @@
add_qtc_test(tst_utils_text
DEPENDS Utils
SOURCES tst_text.cpp
)

View File

@@ -0,0 +1,7 @@
import qbs
QtcAutotest {
name: "Utils::Text autotest"
Depends { name: "Utils" }
files: "tst_text.cpp"
}

View File

@@ -0,0 +1,58 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <utils/textutils.h>
#include <QtTest>
using namespace Utils::Text;
class tst_Text : public QObject
{
Q_OBJECT
private slots:
void testRangeLength_data();
void testRangeLength();
};
void tst_Text::testRangeLength_data()
{
QTest::addColumn<QString>("text");
QTest::addColumn<Range>("range");
QTest::addColumn<int>("length");
QTest::newRow("empty range") << QString() << Range{{1, 0}, {1, 0}} << 0;
QTest::newRow("range on same line") << QString() << Range{{1, 0}, {1, 1}} << 1;
QTest::newRow("range spanning lines") << QString("foo\nbar") << Range{{1, 0}, {2, 0}} << 4;
QTest::newRow("range spanning lines and column offset")
<< QString("foo\nbar") << Range{{1, 1}, {2, 1}} << 4;
QTest::newRow("range spanning lines and begin column offset")
<< QString("foo\nbar") << Range{{1, 1}, {2, 0}} << 3;
QTest::newRow("range spanning lines and end column offset")
<< QString("foo\nbar") << Range{{1, 0}, {2, 1}} << 5;
QTest::newRow("hyper range") << QString("foo\nbar\nfoobar") << Range{{2, 1}, {3, 1}} << 4;
QTest::newRow("flipped range") << QString() << Range{{2, 0}, {1, 0}} << -1;
QTest::newRow("out of range") << QString() << Range{{1, 0}, {2, 0}} << -1;
}
void tst_Text::testRangeLength()
{
QFETCH(QString, text);
QFETCH(Range, range);
QFETCH(int, length);
QCOMPARE(range.length(text), length);
}
QTEST_GUILESS_MAIN(tst_Text)
#include "tst_text.moc"