import { useEffect, useRef, useState } from "react"; import { Pcre2TraceSupervisor } from "../regex/execution/Pcre2TraceSupervisor"; import { WorkerRequestError } from "../regex/execution/WorkerSupervisor"; import { DEFAULT_REGEX_LIMITS } from "../regex/execution/request-limits"; import type { RegexEngineOptions, RegexFlavourId, } from "../regex/model/flavour"; import type { Pcre2TraceEvent, Pcre2TraceResult } from "../regex/model/trace"; import type { SourceRange } from "../regex/model/syntax"; const DEFAULT_TRACE_EVENTS = 5_000; const DEFAULT_TRACE_BYTES = 2 * 1024 * 1024; const MAXIMUM_RENDERED_TRACE_EVENTS = 2_000; type TraceState = | { readonly status: "idle"; readonly message: string } | { readonly status: "running"; readonly message: string } | { readonly status: "ready"; readonly message: string } | { readonly status: "timeout"; readonly message: string } | { readonly status: "error"; readonly message: string }; function movementLabel(event: Pcre2TraceEvent): string { switch (event.movement.classification) { case "first-event": return "first"; case "same-position": return "same position"; case "apparent-backtrack": return "apparent backtrack"; case "forward": return "forward"; } } export function Pcre2TracePanel({ active, flavour, pattern, flags, options, subject, timeoutMs, onClose, onSelectRanges, }: { readonly active: boolean; readonly flavour: RegexFlavourId; readonly pattern: string; readonly flags: readonly string[]; readonly options: RegexEngineOptions; readonly subject: string; readonly timeoutMs: number; readonly onClose: () => void; readonly onSelectRanges: ( patternRange: SourceRange, subjectRange: SourceRange, ) => void; }) { const supervisor = useRef(null); const requestRevision = useRef(0); const [maximumEvents, setMaximumEvents] = useState(DEFAULT_TRACE_EVENTS); const [maximumBytes, setMaximumBytes] = useState(DEFAULT_TRACE_BYTES); const [completed, setCompleted] = useState<{ readonly result: Pcre2TraceResult; readonly input: { readonly pattern: string; readonly subject: string; readonly flags: string; readonly options: string; }; }>(); const [state, setState] = useState({ status: "idle", message: "Run an isolated PCRE2 automatic-callout trace.", }); const traceEngine = () => { supervisor.current ??= new Pcre2TraceSupervisor(); return supervisor.current; }; useEffect(() => { return () => supervisor.current?.dispose(); }, []); useEffect(() => { if (active) return; supervisor.current?.cancel(); }, [active]); const limitsValid = Number.isSafeInteger(maximumEvents) && maximumEvents >= 1 && maximumEvents <= DEFAULT_REGEX_LIMITS.maximumTraceEvents && Number.isSafeInteger(maximumBytes) && maximumBytes >= 32 && maximumBytes <= DEFAULT_REGEX_LIMITS.maximumTraceBytes; const runTrace = async () => { if (flavour !== "pcre2" || !limitsValid) return; const revision = (requestRevision.current += 1); setCompleted(undefined); setState({ status: "running", message: "Collecting reported callouts in a dedicated worker…", }); try { const next = await traceEngine().trace( { flavour, pattern, flags, options, subject, maximumTraceEvents: maximumEvents, maximumTraceBytes: maximumBytes, }, timeoutMs, ); if (revision !== requestRevision.current) return; setCompleted({ result: next, input: { pattern, subject, flags: flags.join(""), options: JSON.stringify(options), }, }); const error = next.diagnostics.find( (diagnostic) => diagnostic.severity === "error", ); setState( next.accepted ? { status: "ready", message: next.truncated ? "Bounded trace complete; collection stopped at a configured cap." : "Bounded trace complete.", } : { status: "error", message: error?.message ?? "PCRE2 rejected the trace request.", }, ); } catch (error) { if (revision !== requestRevision.current) return; if (error instanceof WorkerRequestError && error.kind === "timeout") { setState({ status: "timeout", message: `${error.message}; the trace worker was terminated.`, }); } else if ( error instanceof WorkerRequestError && error.kind === "cancelled" ) { setState({ status: "idle", message: "Trace cancelled; its worker was terminated.", }); } else { setState({ status: "error", message: error instanceof Error ? error.message : "The PCRE2 trace worker failed.", }); } } }; const resultIsCurrent = completed?.input.pattern === pattern && completed.input.subject === subject && completed.input.flags === flags.join("") && completed.input.options === JSON.stringify(options); const visibleResult = resultIsCurrent ? completed.result : undefined; const visibleState = resultIsCurrent || state.status === "running" ? state : { status: "idle" as const, message: "Inputs changed; run a new isolated trace.", }; const renderedEvents = visibleResult?.events.slice(0, MAXIMUM_RENDERED_TRACE_EVENTS) ?? []; return (

PCRE2 10.47 · reported automatic callouts

Execution trace

This is an actual stream reported by PCRE2_AUTO_CALLOUT for one{" "} pcre2_match() invocation. Movement labels are derived only from adjacent reported subject positions; the stream is not a complete record of every internal engine action.

{visibleState.status === "running" ? ( ) : null}
{flavour !== "pcre2" ? (

Select PCRE2 to enable its automatic-callout trace.

) : null}
{visibleState.message} {visibleResult?.accepted ? ( {visibleResult.events.length.toLocaleString()} complete retained events of {visibleResult.totalEventCount.toLocaleString()} callback event(s) observed before return ·{" "} {visibleResult.traceBytes.toLocaleString()} serialized bytes · last complete event{" "} {visibleResult.lastCompleteEvent?.toLocaleString() ?? "none"} ·{" "} native status {visibleResult.nativeMatchStatus} ·{" "} {visibleResult.matched ? "matched" : "not matched or bounded stop"}{" "} · {visibleResult.elapsedMs.toFixed(2)} ms ) : null}
{visibleResult?.diagnostics.length ? (
    {visibleResult.diagnostics.map((diagnostic) => (
  • {diagnostic.severity} {diagnostic.message}
  • ))}
) : null} {visibleResult && visibleResult.events.length > renderedEvents.length ? (

Trace viewer limited. Showing the first{" "} {renderedEvents.length.toLocaleString()} of{" "} {visibleResult.events.length.toLocaleString()} complete retained events. The worker result remains bounded by both configured caps.

) : null} {renderedEvents.length > 0 ? (
{renderedEvents.map((event) => ( ))}
Event Callout Pattern Next item Subject Captures Mark Movement
{event.reported.calloutNumber} {event.reported.patternPosition.utf16} UTF-16 {event.reported.patternPosition.nativeByte} byte {pattern.slice( event.reported.nextPatternItem.startUtf16, event.reported.nextPatternItem.endUtf16, ) || "end"} {event.reported.nextPatternItem.startNativeByte}… {event.reported.nextPatternItem.endNativeByte} bytes {event.reported.subjectPosition.utf16} UTF-16 {event.reported.subjectPosition.nativeByte} byte top {event.reported.captureTop} · last{" "} {event.reported.captureLast} {event.reported.mark ? ( {event.reported.mark} ) : ( "—" )} {movementLabel(event)} {event.movement.subjectDeltaBytes > 0 ? "+" : ""} {event.movement.subjectDeltaBytes} bytes · derived
) : visibleResult?.accepted ? (

No callout event was retained.

) : null}
); }