feat: replaced legacy editing commands with unified put and cut syntax

- Replaced legacy `SWAP`, `INS`, and `PASTE` commands with unified `PUT` and `CUT` hunks across parser, grammar, tokenizer, and test suites.
- Added support for named registers and span paste operations in clipboard and block execution logic.
- Implemented indentation repair and enhanced gap locator formatting for improved patch resilience.
- Updated documentation, system prompts, and session analysis scripts to reflect the new syntax and header shapes.
This commit is contained in:
can1357
2026-07-31 00:19:52 +02:00
parent 55b23864e2
commit 5ea583e413
46 changed files with 1767 additions and 1243 deletions
+70 -25
View File
@@ -569,43 +569,88 @@ def classify_edit_result(text: str, is_error: int | None = None) -> str:
_ANCHOR_BARE = re.compile(r"^[a-zA-Z]?[0-9]+[a-z]{2}$")
_LINE_LOCATOR = r"[1-9]\d*"
_RANGE_SEPARATOR = r"(?:\.=|-|\.|…|\s+)"
_RANGE_LOCATOR = rf"{_LINE_LOCATOR}(?:{_RANGE_SEPARATOR}{_LINE_LOCATOR})?"
_REGISTER = r"@[A-Za-z0-9_-]{1,64}"
# Current S3 headers. Match the whole row: prefix-only matching used to count
# recovered body text such as `replace this value` as an edit operation.
_HASHLINE_OP = re.compile(
r"^(SWAP\.BLK|SWAP|XCHG\.BLK|XCHG|DEL\.BLK|DEL|CUT\.BLK|CUT|"
r"PASTE\.BLK\.POST|PASTE\.PRE|PASTE\.POST|PASTE\.HEAD|PASTE\.TAIL|"
r"INS\.BLK\.POST|INS\.PRE|INS\.POST|INS\.HEAD|INS\.TAIL|REM|MV|"
r"replace_block|replace block|replace|delete_block|delete block|delete|"
r"insert_after_block|insert after block|insert before|insert after|insert head|insert tail)\b",
re.I,
rf"""
^\s*(?:
(?P<put>PUT)\s+
(?:
{_RANGE_LOCATOR}
|{_LINE_LOCATOR}\*
|[<>]\s*{_LINE_LOCATOR}\*?
|>\s*\$
)
(?:\s+{_REGISTER})?\s*:?
|(?P<cut>CUT)\s+(?:{_RANGE_LOCATOR}|{_LINE_LOCATOR}\*)(?:\s+{_REGISTER})?\s*:?
|(?P<mv>MV)\s+\S(?:.*\S)?\s*
|(?P<rem>REM)\s*
)$
""",
re.I | re.X,
)
# Retain syntactically shaped pre-S3 rows so stored sessions remain useful for
# historical reporting. These labels intentionally remain historical rather
# than being folded into PUT/CUT.
_HISTORICAL_HASHLINE_OP = re.compile(
rf"""
^\s*(?P<op>
SWAP\.BLK|SWAP|XCHG\.BLK|XCHG|DEL\.BLK|DEL|CUT\.BLK
|PASTE\.BLK\.POST|PASTE\.PRE|PASTE\.POST|PASTE\.HEAD|PASTE\.TAIL
|INS\.BLK\.POST|INS\.PRE|INS\.POST|INS\.HEAD|INS\.TAIL
|replace_block|replace\ block|replace
|delete_block|delete\ block|delete
|insert_after_block|insert\ after\ block|insert\ before|insert\ after
|insert\ head|insert\ tail
)
(?:
\s+(?:{_RANGE_LOCATOR}|{_LINE_LOCATOR}\.={_LINE_LOCATOR}|{_LINE_LOCATOR})
\s*:?
)?
\s*$
""",
re.I | re.X,
)
_HASHLINE_OP_ALIASES = {
"replace block": "SWAP.BLK",
"replace_block": "SWAP.BLK",
"replace": "SWAP",
"xchg.blk": "SWAP.BLK",
"xchg": "SWAP",
"delete block": "DEL.BLK",
"delete_block": "DEL.BLK",
"delete": "DEL",
"insert before": "INS.PRE",
"insert after": "INS.POST",
"insert head": "INS.HEAD",
"insert tail": "INS.TAIL",
"insert after block": "INS.BLK.POST",
"insert_after_block": "INS.BLK.POST",
"REPLACE BLOCK": "SWAP.BLK",
"REPLACE_BLOCK": "SWAP.BLK",
"REPLACE": "SWAP",
"XCHG.BLK": "SWAP.BLK",
"XCHG": "SWAP",
"DELETE BLOCK": "DEL.BLK",
"DELETE_BLOCK": "DEL.BLK",
"DELETE": "DEL",
"INSERT BEFORE": "INS.PRE",
"INSERT AFTER": "INS.POST",
"INSERT HEAD": "INS.HEAD",
"INSERT TAIL": "INS.TAIL",
"INSERT AFTER BLOCK": "INS.BLK.POST",
"INSERT_AFTER_BLOCK": "INS.BLK.POST",
}
def _hashline_ops(inp: str) -> list[str]:
"""Op kinds in a hashline edit `input` payload (skips `+` body rows)."""
"""Op kinds in a hashline edit `input`, excluding body/recovery rows."""
ops: list[str] = []
for line in inp.splitlines():
if not line or line[0] == "+":
stripped = line.lstrip()
if not stripped or stripped.startswith("+"):
continue
m = _HASHLINE_OP.match(line.lstrip())
if m:
op = m.group(1).lower()
match = _HASHLINE_OP.fullmatch(line)
if match:
ops.append(next(name.upper() for name, value in match.groupdict().items() if value))
continue
match = _HISTORICAL_HASHLINE_OP.fullmatch(line)
if match:
op = match.group("op").upper()
ops.append(_HASHLINE_OP_ALIASES.get(op, op))
return ops