mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 16:15:33 +00:00
Merge pull request #1708 from ebarkhordar/fix/search-text-exclusive-end-line
fix(search): do not mark the following line as matched when a match ends at a line break
This commit is contained in:
3 files changed
+59
No files matched your search
@@ -10,6 +10,12 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
the `line_ending` setting is meant to be the single point of line-ending translation on write. The
|
||||
fallback now normalizes line endings to LF, consistently with the primary path.
|
||||
|
||||
* Tools:
|
||||
- Fix: `search_for_pattern` marked one line too many as matched whenever a match ended with a line
|
||||
break, because the match's exclusive end index was mapped to a line number directly and therefore
|
||||
resolved to the start of the following line. The line the match ends on is now determined correctly,
|
||||
which also keeps `context_lines_after` aligned.
|
||||
|
||||
* JetBrains:
|
||||
- Allow external files from dependencies (specified via references like "<ext:FileUtil.class|472e0a13>") to be
|
||||
- read via `ReadFileTool`
|
||||
|
||||
@@ -187,6 +187,10 @@ def search_text(
|
||||
# Find the line numbers for the start and end positions
|
||||
start_line_num = TextUtils.get_line_from_index(content, start_pos)
|
||||
end_line_num = TextUtils.get_line_from_index(content, end_pos)
|
||||
if end_line_num > start_line_num and TextUtils.get_line_col_from_index(content, end_pos)[1] == 0:
|
||||
# `end_pos` is exclusive, so if it is at the start of a line, the match ends with the
|
||||
# preceding line's newline and does not extend into the line that `end_pos` points to
|
||||
end_line_num -= 1
|
||||
|
||||
# Calculate the range of lines to include in the context
|
||||
context_start = max(0, start_line_num - context_lines_before)
|
||||
|
||||
@@ -103,6 +103,55 @@ class TestSearchText:
|
||||
assert "return a * c" in first_match.lines[1].line_content
|
||||
assert "elif b > a:" in first_match.lines[2].line_content
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("pattern", "expected_matched_lines"),
|
||||
[
|
||||
# matches ending with a newline: the trailing newline terminates the last matched line
|
||||
# and must not pull in the line that follows
|
||||
(r"alpha\n", [0]),
|
||||
(r"alpha\nbeta\n", [0, 1]),
|
||||
(r"gamma\n", [2]),
|
||||
# controls: matches ending mid-line were always correct
|
||||
(r"alpha", [0]),
|
||||
(r"beta", [1]),
|
||||
(r"alpha\nbeta", [0, 1]),
|
||||
],
|
||||
)
|
||||
def test_search_text_match_ending_at_line_break(self, pattern: str, expected_matched_lines: list[int]):
|
||||
"""The lines marked as matched are exactly the lines the matched text occupies."""
|
||||
content = "alpha\nbeta\ngamma\n"
|
||||
|
||||
matches = search_text(pattern, content=content)
|
||||
|
||||
assert len(matches) == 1
|
||||
matched_lines = [line.line_number for line in matches[0].lines if line.match_type == LineType.MATCH]
|
||||
assert matched_lines == expected_matched_lines
|
||||
assert matches[0].num_matched_lines == len(expected_matched_lines)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("pattern", "expected_matched_lines"),
|
||||
[
|
||||
(r"alpha\r\n", [0]),
|
||||
(r"alpha\r", [0]),
|
||||
(r"alpha\r\nbeta\r\n", [0, 1]),
|
||||
(r"alpha", [0]),
|
||||
],
|
||||
)
|
||||
def test_search_text_match_ending_at_line_break_crlf(self, pattern: str, expected_matched_lines: list[int]):
|
||||
"""As above for CRLF content, where the end index can fall inside the two-character line break.
|
||||
|
||||
The content is passed directly rather than read from a file, since the file read paths translate
|
||||
line endings and CR would not reach this function.
|
||||
"""
|
||||
content = "alpha\r\nbeta\r\ngamma\r\n"
|
||||
|
||||
matches = search_text(pattern, content=content)
|
||||
|
||||
assert len(matches) == 1
|
||||
matched_lines = [line.line_number for line in matches[0].lines if line.match_type == LineType.MATCH]
|
||||
assert matched_lines == expected_matched_lines
|
||||
assert matches[0].num_matched_lines == len(expected_matched_lines)
|
||||
|
||||
def test_search_text_with_multiline_match(self):
|
||||
"""Test searching with multiline pattern matching."""
|
||||
content = """
|
||||
|
||||
Reference in new issue
Block a user