Skip to content

Commit 4f01657

Browse files
committed
improved parse_patch blocks robustness
1 parent a577657 commit 4f01657

File tree

1 file changed

+72
-7
lines changed

1 file changed

+72
-7
lines changed

codetide/mcp/tools/patch_code/__init__.py

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ def parse_patch_blocks(text: str, multiple: bool = True) -> Union[str, List[str]
1212
"""
1313
Extract content between *** Begin Patch and *** End Patch markers (inclusive),
1414
ensuring that both markers are at zero indentation (start of line, no leading spaces).
15+
16+
If only one identifier is present:
17+
- If only "Begin Patch" exists: returns from Begin Patch to end of text
18+
- If only "End Patch" exists: returns from start of text to End Patch
1519
1620
Args:
1721
text: Full input text containing one or more patch blocks.
@@ -21,13 +25,72 @@ def parse_patch_blocks(text: str, multiple: bool = True) -> Union[str, List[str]
2125
A string (single patch), list of strings (multiple patches), or None if not found.
2226
"""
2327

24-
pattern = r"(?m)^(\*\*\* Begin Patch[\s\S]*?^\*\*\* End Patch)$"
25-
matches = re.findall(pattern, text)
26-
27-
if not matches:
28+
# First, try to find complete blocks (both Begin and End markers)
29+
complete_pattern = r"(?m)^(\*\*\* Begin Patch[\s\S]*?^\*\*\* End Patch)$"
30+
complete_matches = re.findall(complete_pattern, text)
31+
32+
# If we found complete matches, return them (preserving original behavior)
33+
if complete_matches:
34+
return complete_matches if multiple else complete_matches[0]
35+
36+
# If no complete matches, look for partial identifiers
37+
begin_pattern = r"(?m)^(\*\*\* Begin Patch).*$"
38+
end_pattern = r"(?m)^(\*\*\* End Patch).*$"
39+
40+
begin_matches = list(re.finditer(begin_pattern, text))
41+
end_matches = list(re.finditer(end_pattern, text))
42+
43+
partial_matches = []
44+
45+
# Handle cases with only Begin markers (from Begin to end of text)
46+
if begin_matches and not end_matches:
47+
for match in begin_matches:
48+
start_pos = match.start()
49+
partial_content = text[start_pos:]
50+
partial_matches.append(partial_content)
51+
52+
# Handle cases with only End markers (from start of text to End)
53+
elif end_matches and not begin_matches:
54+
for match in end_matches:
55+
end_pos = match.end()
56+
partial_content = text[:end_pos]
57+
partial_matches.append(partial_content)
58+
59+
# Handle mixed cases (some begins without ends, some ends without begins)
60+
elif begin_matches or end_matches:
61+
# Get all Begin positions
62+
begin_positions = [m.start() for m in begin_matches]
63+
end_positions = [m.end() for m in end_matches]
64+
65+
# For each Begin, try to find corresponding End
66+
for begin_pos in begin_positions:
67+
corresponding_end = None
68+
for end_pos in end_positions:
69+
if end_pos > begin_pos:
70+
corresponding_end = end_pos
71+
break
72+
73+
if corresponding_end:
74+
# Complete pair found
75+
partial_content = text[begin_pos:corresponding_end]
76+
else:
77+
# Begin without End - go to end of text
78+
partial_content = text[begin_pos:]
79+
80+
partial_matches.append(partial_content)
81+
82+
# Handle orphaned End markers (Ends that don't have corresponding Begins)
83+
for end_pos in end_positions:
84+
has_corresponding_begin = any(begin_pos < end_pos for begin_pos in begin_positions)
85+
if not has_corresponding_begin:
86+
# End without Begin - from start of text
87+
partial_content = text[:end_pos]
88+
partial_matches.append(partial_content)
89+
90+
if not partial_matches:
2891
return None
29-
30-
return matches if multiple else matches[0]
92+
93+
return partial_matches if multiple else partial_matches[0]
3194

3295
# --------------------------------------------------------------------------- #
3396
# User-facing API
@@ -144,7 +207,9 @@ def process_patch(
144207

145208
# Normalize line endings before processing
146209
patches_text = open_fn(patch_path)
147-
patches = parse_patch_blocks(patches_text) or [""]
210+
print(f"{patches_text=}")
211+
patches = parse_patch_blocks(patches_text)#or [""]
212+
print(f"{patches=}")
148213

149214
all_paths_needed = []
150215
for text in patches:

0 commit comments

Comments
 (0)