refactor: split TextReplacer into HtmlTokens and StoredForms (M1)

- HtmlTokens (static, with the S45 docblock): SPLIT, RAW_OPEN, tokenize(),
  structure(), closes(), unterminated() moved unchanged; new RAW_ELEMENTS list.
- StoredForms: of() (was TextReplacer::forms), mentions(), unicodeEscaped() –
  the DB prefilter forms; TextReplacer::mentions() delegates, the ability's
  candidate query uses StoredForms::of().
- TextReplacer keeps problems(), apply() and the splice/safe() verification;
  safe() still runs the tokenizer and HtmlContext as two independent scans, in
  the same order.
HtmlTokensTest asserts RAW_ELEMENTS === HtmlContext::RAW and that RAW_OPEN opens
exactly those elements. TextReplacerTest and HtmlContextTest unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-26 18:14:49 +02:00
co-authored by Claude Opus 5.5
parent 570c1e6142
commit 3eeb7eee90
6 changed files with 239 additions and 161 deletions
@@ -12,6 +12,7 @@ use LIT\MCP\Abilities\ToolResult;
use LIT\MCP\Abilities\WriteFailed;
use LIT\MCP\Integrations\Divi\BlockTree\Block;
use LIT\MCP\Integrations\Divi\BlockTree\Serializer;
use LIT\MCP\Integrations\Divi\Compose\StoredForms;
use LIT\MCP\Integrations\Divi\Compose\TextReplacer;
use LIT\MCP\Integrations\WordPress\PostGuard;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
@@ -426,14 +427,14 @@ final class DiviReplaceTextAbility implements Ability
/**
* IDs of Divi entries whose content contains the search text in one of its stored forms
* (TextReplacer::forms()). The exact match happens in the tree; this is only a prefilter.
* (StoredForms::of()). The exact match happens in the tree; this is only a prefilter.
*
* @return list<int>
*/
private function candidates(string $postType, string $search, bool $caseSensitive, int $limit, int $offset): array
{
$db = $this->db();
$forms = TextReplacer::forms($search, $caseSensitive);
$forms = StoredForms::of($search, $caseSensitive);
$likes = array_map(
static fn (string $form): string => '%' . $db->esc_like($form) . '%',
$forms