mechanicaldiff/bin/mechanicaldiff.py

106 lines
2.7 KiB
Python
Executable file

#!/usr/bin/env python3
import sys
def should_include_change(change_lines: list[str], search: str, replace: str) -> bool:
removed_lines = []
added_lines = []
phase = "minus"
for line in change_lines:
if line.startswith("-"):
if phase != "minus":
raise ValueError("Non-consecutive '-' and '+' lines in change block.")
removed_lines.append(line[1:])
continue
if line.startswith("+"):
phase = "plus"
added_lines.append(line[1:])
continue
raise ValueError("Unexpected non-change line in change block.")
return True
def should_include_hunk(hunk_text: str, search: str, replace: str) -> bool:
lines = hunk_text.splitlines()
if not lines:
return True
change = []
for line in lines[1:]:
if line.startswith(("+", "-")):
change.append(line)
continue
if change:
if not should_include_change(change, search, replace):
return False
change = []
if change:
if not should_include_change(change, search, replace):
return False
return True
def main() -> None:
if len(sys.argv) != 3:
raise SystemExit("Usage: mechanicaldiff <search> <replace>")
search = sys.argv[1]
replace = sys.argv[2]
lines = sys.stdin.read().splitlines(keepends=True)
preamble_lines = []
sections = []
current = None
for line in lines:
if line.startswith("diff --git "):
if current is not None:
sections.append(current)
current = {"header": [line], "hunks": []}
continue
if current is None:
preamble_lines.append(line)
continue
if line.startswith("@@ "):
current["hunks"].append([line])
else:
if current["hunks"]:
current["hunks"][-1].append(line)
else:
current["header"].append(line)
if current is not None:
sections.append(current)
output_lines = []
output_lines.extend(preamble_lines)
for section in sections:
if not section["hunks"]:
output_lines.extend(section["header"])
continue
kept_hunks = []
for hunk_lines in section["hunks"]:
hunk_text = "".join(hunk_lines)
if should_include_hunk(hunk_text, search, replace):
kept_hunks.append(hunk_lines)
if not kept_hunks:
continue
output_lines.extend(section["header"])
for hunk_lines in kept_hunks:
output_lines.extend(hunk_lines)
sys.stdout.write("".join(output_lines))
if __name__ == "__main__":
main()