diff options
author | Zufu Liu <unknown> | 2023-12-12 09:45:46 +1100 |
---|---|---|
committer | Zufu Liu <unknown> | 2023-12-12 09:45:46 +1100 |
commit | 50f053ff0f1d1a2b45a20e65f25a21a2a10c1bc4 (patch) | |
tree | 6394af33566c632c73cf5f035abf5d176f61e946 | |
parent | 1b83a38def5c210c39421593b0a73c23495bcd54 (diff) | |
download | scintilla-mirror-50f053ff0f1d1a2b45a20e65f25a21a2a10c1bc4.tar.gz |
Bug [#2405]. Fix regular expression assertion (^, $, \b. \B) failures when using
SCFIND_CXX11REGEX.
-rw-r--r-- | doc/ScintillaHistory.html | 4 | ||||
-rw-r--r-- | src/Document.cxx | 61 | ||||
-rw-r--r-- | test/unit/testDocument.cxx | 37 |
3 files changed, 69 insertions, 33 deletions
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 38cd4e3fa..dc2a8415e 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -614,6 +614,10 @@ <a href="https://sourceforge.net/p/scintilla/bugs/2413/">Bug #2413</a>. </li> <li> + Fix regular expression assertion (^, $, \b. \B) failures when using SCFIND_CXX11REGEX. + <a href="https://sourceforge.net/p/scintilla/bugs/2405/">Bug #2405</a>. + </li> + <li> With a document that does not have the SC_DOCUMENTOPTION_TEXT_LARGE option set, allocating more than 2G (calling SCI_ALLOCATE or similar) will now fail with SC_STATUS_FAILURE. </li> diff --git a/src/Document.cxx b/src/Document.cxx index 16595eafa..84eea1394 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -2843,8 +2843,8 @@ public: lineRangeEnd = doc->SciLineFromPosition(endPos); lineRangeBreak = lineRangeEnd + increment; } - Range LineRange(Sci::Line line) const { - Range range(doc->LineStart(line), doc->LineEnd(line)); + Range LineRange(Sci::Line line, Sci::Position lineStartPos, Sci::Position lineEndPos) const noexcept { + Range range(lineStartPos, lineEndPos); if (increment == 1) { if (line == lineRangeStart) range.start = startPos; @@ -3082,12 +3082,24 @@ public: #endif -std::regex_constants::match_flag_type MatchFlags(const Document *doc, Sci::Position startPos, Sci::Position endPos) noexcept { +std::regex_constants::match_flag_type MatchFlags(const Document *doc, Sci::Position startPos, Sci::Position endPos, Sci::Position lineStartPos, Sci::Position lineEndPos) noexcept { std::regex_constants::match_flag_type flagsMatch = std::regex_constants::match_default; - if (!doc->IsLineStartPosition(startPos)) + if (startPos != lineStartPos) { +#ifdef _LIBCPP_VERSION flagsMatch |= std::regex_constants::match_not_bol; - if (!doc->IsLineEndPosition(endPos)) + if (!doc->IsWordStartAt(startPos)) { + flagsMatch |= std::regex_constants::match_not_bow; + } +#else + flagsMatch |= std::regex_constants::match_prev_avail; +#endif + } + if (endPos != lineEndPos) { flagsMatch |= std::regex_constants::match_not_eol; + if (!doc->IsWordEndAt(endPos)) { + flagsMatch |= std::regex_constants::match_not_eow; + } + } return flagsMatch; } @@ -3101,39 +3113,33 @@ bool MatchOnLines(const Document *doc, const Regex ®exp, const RESearchRange // has not been implemented by compiler runtimes with MSVC always in multiline // mode and libc++ and libstdc++ always in single-line mode. // If multiline regex worked well then the line by line iteration could be removed - // for the forwards case and replaced with the following 4 lines: + // for the forwards case and replaced with the following: #ifdef REGEX_MULTILINE + const Sci::Position lineStartPos = doc->LineStart(resr.lineRangeStart); + const Sci::Position lineEndPos = doc->LineEnd(resr.lineRangeEnd); Iterator itStart(doc, resr.startPos); Iterator itEnd(doc, resr.endPos); - const std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, resr.startPos, resr.endPos); + const std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, resr.startPos, resr.endPos, lineStartPos, lineEndPos); const bool matched = std::regex_search(itStart, itEnd, match, regexp, flagsMatch); #else // Line by line. bool matched = false; for (Sci::Line line = resr.lineRangeStart; line != resr.lineRangeBreak; line += resr.increment) { - const Range lineRange = resr.LineRange(line); + const Sci::Position lineStartPos = doc->LineStart(line); + const Sci::Position lineEndPos = doc->LineEnd(line); + const Range lineRange = resr.LineRange(line, lineStartPos, lineEndPos); Iterator itStart(doc, lineRange.start); Iterator itEnd(doc, lineRange.end); - std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, lineRange.start, lineRange.end); - matched = std::regex_search(itStart, itEnd, match, regexp, flagsMatch); - // Check for the last match on this line. - if (matched) { - if (resr.increment == -1) { - while (matched) { - Iterator itNext(doc, match[0].second.PosRoundUp()); - flagsMatch = MatchFlags(doc, itNext.Pos(), lineRange.end); - std::match_results<Iterator> matchNext; - matched = std::regex_search(itNext, itEnd, matchNext, regexp, flagsMatch); - if (matched) { - if (match[0].first == match[0].second) { - // Empty match means failure so exit - return false; - } - match = matchNext; - } - } - matched = true; + const std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, lineRange.start, lineRange.end, lineStartPos, lineEndPos); + std::regex_iterator<Iterator> it(itStart, itEnd, regexp, flagsMatch); + for (const std::regex_iterator<Iterator> last; it != last; ++it) { + match = *it; + matched = true; + if (resr.increment > 0) { + break; } + } + if (matched) { break; } } @@ -3171,7 +3177,6 @@ Sci::Position Cxx11RegexFindText(const Document *doc, Sci::Position minPos, Sci: std::wregex regexp; regexp.assign(ws, flagsRe); matched = MatchOnLines<UTF8Iterator>(doc, regexp, resr, search); - } else { std::regex regexp; regexp.assign(s, flagsRe); diff --git a/test/unit/testDocument.cxx b/test/unit/testDocument.cxx index ca95bda66..52af45167 100644 --- a/test/unit/testDocument.cxx +++ b/test/unit/testDocument.cxx @@ -555,6 +555,10 @@ TEST_CASE("Document") { REQUIRE(match == Match(0)); match = doc.FindString(1, docLength, findingBOL, reCxx11); REQUIRE(match == Match(10)); + match = doc.FindString(docLength, 0, findingBOL, reCxx11); + REQUIRE(match == Match(10)); + match = doc.FindString(docLength - 1, 0, findingBOL, reCxx11); + REQUIRE(match == Match(10)); #endif constexpr std::string_view findingEOL = "$"; @@ -567,11 +571,15 @@ TEST_CASE("Document") { match = doc.FindString(docLength - 1, 0, findingEOL, rePosix); REQUIRE(match == Match(8)); - #ifndef NO_CXX11_REGEX + #if !defined(NO_CXX11_REGEX) && !defined(_LIBCPP_VERSION) match = doc.FindString(0, docLength, findingEOL, reCxx11); REQUIRE(match == Match(8)); match = doc.FindString(1, docLength, findingEOL, reCxx11); REQUIRE(match == Match(8)); + match = doc.FindString(docLength, 0, findingEOL, reCxx11); + REQUIRE(match == Match(18)); + match = doc.FindString(docLength - 1, 0, findingEOL, reCxx11); + REQUIRE(match == Match(8)); #endif constexpr std::string_view findingBOW = "\\<"; @@ -605,13 +613,32 @@ TEST_CASE("Document") { match = doc.FindString(0, docLength, findingWB, reCxx11); REQUIRE(match == Match(0)); match = doc.FindString(1, docLength, findingWB, reCxx11); - REQUIRE(match == Match(1)); + REQUIRE(match == Match(2)); + match = doc.FindString(docLength, 0, findingWB, reCxx11); + #ifdef _LIBCPP_VERSION + REQUIRE(match == Match(16)); + #else + REQUIRE(match == Match(18)); + #endif + match = doc.FindString(docLength - 1, 0, findingWB, reCxx11); + REQUIRE(match == Match(16)); constexpr std::string_view findingNWB = "\\B"; match = doc.FindString(0, docLength, findingNWB, reCxx11); REQUIRE(match == Match(1)); match = doc.FindString(1, docLength, findingNWB, reCxx11); - REQUIRE(match == Match(4)); + REQUIRE(match == Match(1)); + #ifdef _LIBCPP_VERSION + match = doc.FindString(docLength, 0, findingNWB, reCxx11); + REQUIRE(match == Match(18)); + match = doc.FindString(docLength - 1, 0, findingNWB, reCxx11); + REQUIRE(match == Match(14)); + #else + match = doc.FindString(docLength, 0, findingNWB, reCxx11); + REQUIRE(match == Match(17)); + match = doc.FindString(docLength - 1, 0, findingNWB, reCxx11); + REQUIRE(match == Match(17)); + #endif #endif } @@ -637,7 +664,7 @@ TEST_CASE("Document") { match = doc.FindString(0, docLength, "\\b[a-z]", reCxx11); REQUIRE(match == Match(0, 1)); match = doc.FindString(1, docLength, "\\b[a-z]", reCxx11); - REQUIRE(match == Match(1, 1)); // Should be (3,1) + REQUIRE(match == Match(3, 1)); match = doc.FindString(0, docLength, "[a-z]\\b", reCxx11); REQUIRE(match == Match(1, 1)); match = doc.FindString(2, docLength, "[a-z]\\b", reCxx11); @@ -646,7 +673,7 @@ TEST_CASE("Document") { match = doc.FindString(0, docLength, "\\B[a-z]", reCxx11); REQUIRE(match == Match(1, 1)); match = doc.FindString(1, docLength, "\\B[a-z]", reCxx11); - REQUIRE(match == Match(4, 1)); // Should be (1,1) + REQUIRE(match == Match(1, 1)); match = doc.FindString(0, docLength, "[a-z]\\B", reCxx11); REQUIRE(match == Match(0, 1)); match = doc.FindString(2, docLength, "[a-z]\\B", reCxx11); |