"""WOLF RPG Editor inline control-code helpers for WolfDawn JSON. WolfDawn ``strings-inject`` requires each ``text`` line to carry the same backslash control codes as its ``source`` (only the human-readable words may change). Models often break codes (e.g. ``\\^`` becomes ``\\ ^``). These helpers protect codes during translation and repair them before inject. """ from __future__ import annotations import re from typing import Any from util import speakers as wolf_speakers # Inline codes WolfDawn compares during inject (see strings-inject guard output). # Order matters: longer bracketed forms before single-letter fallbacks. WOLF_INLINE_CODE_RE = re.compile( r"\\(?:" r"r\[[^\]]*\]|" # ruby \r[kanji,kana] r"c(?:self)?\[[^\]]*\]|" # \c[...] / \cself[...] r"[A-Za-z]+\[[^\]]*\]|" # \font[0], \cdb[21:78:0], \wE[1], ... r"\^|" # \^ (wait / beat — often mangled to "\ ^") r"[ @<>\-]|" # other single-char escapes Wolf uses r"[A-Za-z]" # \n, \c, \f, ... r")" ) # Placeholder transport (same convention as util.translation.protect_script_codes). _PLACEHOLDER_PREFIX = "__WOLF_CODE_" def _tokenize(rest: str) -> list[tuple[str, str]]: """Split *rest* into ``('lit', ...)`` and ``('code', ...)`` tokens.""" if not rest: return [] out: list[tuple[str, str]] = [] last = 0 for m in WOLF_INLINE_CODE_RE.finditer(rest): if m.start() > last: out.append(("lit", rest[last : m.start()])) out.append(("code", m.group(0))) last = m.end() if last < len(rest): out.append(("lit", rest[last:])) return out def _fix_spurious_spaces_in_codes(source: str, text: str) -> str: """Undo common model errors like ``\\ ^`` when *source* has ``\\^``.""" for m in WOLF_INLINE_CODE_RE.finditer(source): code = m.group(0) # Single-char escapes: \^ \c etc. Models often insert a space after \\. if len(code) == 2 and code[1] not in (" ", "\t"): spaced = f"{code[0]} {code[1]}" if spaced in text: text = text.replace(spaced, code, 1) return text def rebuild_text_preserving_source_codes(source: str, text: str) -> str: """Return *text* with *source*'s inline code tokens and translated literals. Keeps any leading ``@