Commit cb759994 authored by Kåre Särs's avatar Kåre Särs
Browse files

S&R: Fix Look-ahead & behind highlighting & replace

Strip the look-ahead and look-behind from the regular-expression that
checks if a string has been modified after search and returns the
possible captures. We strip the look-ahead/behind because we only
match against the previously found range.

BUG: 449545
parent c857e08f
......@@ -260,6 +260,32 @@ void MatchModel::updateMatchRanges(const QVector<KTextEditor::MovingRange *> &ra
dataChanged(index(0, 0, rootFileIndex), index(matches.count() - 1, 0, rootFileIndex));
}
QRegularExpressionMatch MatchModel::rangeTextMatches(const QString &rangeText, const QRegularExpression &regExp)
{
// special handling for lookahead and lookbehind
QRegularExpression tmpReg = regExp;
QString pattern = tmpReg.pattern();
// NOTE: Negative look-ahead/behind i snot a problem as they are not part of the range
static QRegularExpression lookaheadRegex(QStringLiteral(".*(\\(\\?=[^\\)]+\\))"));
static QRegularExpression lookbehindRegex(QStringLiteral("(\\(\\?<=[^\\)]+\\)).*"));
// Remove possible lookahead as we do not have the tail to compare with
auto lookMatch = lookaheadRegex.match(pattern);
if (lookMatch.hasMatch()) {
pattern.remove(lookMatch.captured(1));
tmpReg.setPattern(pattern);
}
// Remove possible lookbehind as we do not have the prefix
lookMatch = lookbehindRegex.match(pattern);
if (lookMatch.hasMatch()) {
pattern.remove(lookMatch.captured(1));
tmpReg.setPattern(pattern);
}
return tmpReg.match(rangeText);
}
/** This function is used to replace a match */
bool MatchModel::replaceMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString)
{
......@@ -283,7 +309,7 @@ bool MatchModel::replaceMatch(KTextEditor::Document *doc, const QModelIndex &mat
// Check that the text has not been modified and still matches + get captures for the replace
QString matchLines = doc->text(matchItem->range);
QRegularExpressionMatch match = regExp.match(matchLines);
QRegularExpressionMatch match = rangeTextMatches(matchLines, regExp);
if (match.capturedStart() != 0) {
qDebug() << matchLines << "Does not match" << regExp.pattern();
return false;
......
......@@ -187,6 +187,12 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
/** This function is used to verify that the range has not been edited since the search and returns the match object with the needed captures.
* @param rangeText is the QString for the range.
* @param regExp is the regular-expression to check.
* @return the match object that has captures on success or none on failure to match the previously found range.*/
static QRegularExpressionMatch rangeTextMatches(const QString &rangeText, const QRegularExpression &regExp);
private Q_SLOTS:
void doReplaceNextMatch();
......
In this file we check that regular expression lookahead and lookbehind works as expected.
The following three lines are test data
1) Foo 123ABC123
2) Foo ABC
3) Foo BARABCDEF
4) Foo 123ABCDEF
Run the test cases by entering the following regexes (one at a time).
The lines in the result should be highlighted:
Lookahead
regex: "ABC(?=\d+)" result: 1
regex: "ABC(?=\D+)" result: 3, 4
regex: "ABC(?!\d+)" result: 2, 3, 4
Negative lookahead
regex: "ABC(?!\S+)" result: 2
regex: "ABC(?!\d+)" result: 2, 3, 4
Lookbehind
regex: "(?<=\d)ABC" result: 1, 4
regex: "(?<=\S)ABC" result: 1, 3, 4
Negative lookbehind
regex: "(?<!\d)ABC" result: 2, 3
regex: "(?<!\S)ABC" result: 2
regex: "(?<!\D)ABC" result: 1, 4
Lookbehind + lookahead
regex: "(?<=\S)ABC(?=\S+)" result: 1, 3, 4
regex: "(?<=\d)ABC(?=\D+)" result: 4
regex: "(?<=\D)ABC(?=\D+)" result: 3
Next we check that the captures do not get offset by look-ahead or look-behind by
checking the next regexp and replacement on the line after them. The replace should
just replace '_' with '-':
regex: (?<=\d)(ABC)_(DEF)_(GHI)(?=\d+)
replace: \1-\2-\3
123ABC_DEF_GHI123
Next we check that the captures do not get offset by NEGATIVE look-ahead or
look-behind by checking the next regexp and replacement on the line after them.
The replace should just replace '_' with '-':
regex: (?<!\d)(ABC)_(DEF)_(GHI)(?!\d+)
replace: \1-\2-\3
aaaABC_DEF_GHIaaa
Foo Baar
Foo Baar
In this file we check that regular expression for multi-line regular
expressions can use ^ bot beginning of line and $ for end of line.
The following regexp should match the first the last and next "Foo Baar"
blocks, but not the next to last or third to last:
regex: "^Foo Baar\nFoo Baar$"
Foo Baar
Foo Baar
Foo Baar
Foo Baar
Foo Baar
Foo Baar Baz
NOTE: do not insert a newline at the end
Foo Baar
Foo Baar
......@@ -1535,15 +1535,8 @@ void KatePluginSearchView::addRangeAndMark(KTextEditor::Document *doc,
// Check that the match still matches
if (m_curResults) {
if (!isReplaced) {
// special handling for "(?=\\n)" in multi-line search
QRegularExpression tmpReg = m_curResults->regExp;
if (m_curResults->regExp.pattern().endsWith(QLatin1String("(?=\\n)"))) {
QString newPatern = tmpReg.pattern();
newPatern.replace(QStringLiteral("(?=\\n)"), QStringLiteral("$"));
tmpReg.setPattern(newPatern);
}
// Check that the match still matches ;)
if (tmpReg.match(doc->text(match.range)).capturedStart() != 0) {
auto regMatch = MatchModel::rangeTextMatches(doc->text(match.range), m_curResults->regExp);
if (regMatch.capturedStart() != 0) {
// qDebug() << doc->text(range) << "Does not match" << m_curResults->regExp.pattern();
return;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment