Skip to content

Commit 6f09f64

Browse files
committed
feat: refactor parse_xml_content to parse_patch_blocks for improved patch handling
1 parent fd8ee35 commit 6f09f64

2 files changed

Lines changed: 29 additions & 13 deletions

File tree

codetide/agents/tide/utils.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
from typing import List, Union
22
import re
33

4-
def parse_xml_content(text: str, tag: str = "diff", multiple: bool = False) -> Union[str, List[str], None]:
4+
def parse_patch_blocks(text: str, multiple: bool = True) -> Union[str, List[str], None]:
55
"""
6-
Extract content between <tag>...</tag> markers.
7-
6+
Extract content between *** Begin Patch and *** End Patch markers (inclusive),
7+
ensuring that both markers are at zero indentation (start of line, no leading spaces).
8+
89
Args:
9-
text: Full input text.
10-
tag: The tag name to extract content from (default: 'diff').
11-
multiple: If True, return all matching blocks. If False, return only the first one.
12-
10+
text: Full input text containing one or more patch blocks.
11+
multiple: If True, return a list of all patch blocks. If False, return the first match.
12+
1313
Returns:
14-
The extracted content as a string (if one block), list of strings (if multiple),
15-
or None if no blocks found.
14+
A string (single patch), list of strings (multiple patches), or None if not found.
1615
"""
17-
pattern = fr"<{tag}>\s*(.*?)\s*</{tag}>"
18-
matches = re.findall(pattern, text, re.DOTALL)
16+
17+
pattern = r"(?m)^(\*\*\* Begin Patch[\s\S]*?^\*\*\* End Patch)$"
18+
matches = re.findall(pattern, text)
1919

2020
if not matches:
2121
return None
2222

23-
return matches if multiple else matches[0]
23+
return matches if multiple else matches[0]

codetide/mcp/tools/patch_code/__init__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,30 @@
44
from typing import Dict, Tuple, List, Callable
55
import pathlib
66
import re
7+
import os
78

89
BREAKLINE_TOKEN = "<n>"
910

11+
BREAKLINE_PER_FILE_TYPE = {
12+
".md": "\n",
13+
".py": r"\n"
14+
}
15+
1016
# --------------------------------------------------------------------------- #
1117
# User-facing API
1218
# --------------------------------------------------------------------------- #
1319
def text_to_patch(text: str, orig: Dict[str, str]) -> Tuple[Patch, int]:
1420
"""High-level function to parse patch text against original file content."""
1521
lines = text.splitlines()
22+
for i, line in enumerate(lines):
23+
if line.startswith(("@", "***")):
24+
continue
25+
26+
elif line.startswith("---") or not line.startswith(("+", "-", " ")):
27+
lines[i] = f" {line}"
28+
29+
# print(f"\n\n{lines[-2:]=}")
30+
1631
if not lines or not Parser._norm(lines[0]).startswith("*** Begin Patch"):
1732
raise DiffError("Invalid patch text - must start with '*** Begin Patch'.")
1833
if not Parser._norm(lines[-1]) == "*** End Patch":
@@ -146,8 +161,9 @@ def open_file(path: str) -> str:
146161
def write_file(path: str, content: str) -> None:
147162
target = pathlib.Path(path)
148163
target.parent.mkdir(parents=True, exist_ok=True)
164+
_, ext = os.path.splitext(target)
149165
with target.open("wt", encoding="utf-8", newline="\n") as fh:
150-
fh.write(content.replace(BREAKLINE_TOKEN, "\\n"))
166+
fh.write(content.replace(BREAKLINE_TOKEN, BREAKLINE_PER_FILE_TYPE.get(ext, r"\n")))
151167

152168

153169
def remove_file(path: str) -> None:

0 commit comments

Comments
 (0)