diff --git a/codex/wiki.py b/codex/wiki.py index 44577a6..f18c7af 100644 --- a/codex/wiki.py +++ b/codex/wiki.py @@ -284,6 +284,9 @@ def _run_grounding_guard( # --------------------------------------------------------------------------- +_INLINE_CODE_RE = re.compile(r"`[^`]+`") + + def _inject_cross_refs( markdown: str, all_concepts: list[Concept], @@ -292,8 +295,21 @@ def _inject_cross_refs( """Replace occurrences of other concept titles/aliases with ``[[slug]]`` links. Only exact case-insensitive whole-word matches outside of existing - ``[[…]]`` blocks or inline code are replaced. + ``[[…]]`` blocks or inline code spans are replaced. + Inline-code spans (`` `…` ``) are temporarily protected by null-byte + placeholders and restored after injection. """ + # Step 1: protect inline-code spans from replacement + placeholders: dict[str, str] = {} + + def _protect(m: re.Match[str]) -> str: + key = f"\x00{len(placeholders)}\x00" + placeholders[key] = m.group(0) + return key + + markdown = _INLINE_CODE_RE.sub(_protect, markdown) + + # Step 2: inject cross-refs on unprotected text for concept in all_concepts: if concept.slug == current_slug: continue @@ -304,6 +320,11 @@ def _inject_cross_refs( pattern = re.compile(rf"(? None: assert "[[circle-packing]]" in result +def test_inject_cross_refs_skips_inline_code() -> None: + """Concept titles inside backtick inline-code spans are NOT replaced.""" + concepts = [ + Concept( + slug="circle-packing", + title="circle-packing", + aliases=[], + ), + FIXTURE_CONCEPT, + ] + # The term "circle-packing" appears both bare (should be replaced) + # and inside backticks (should NOT be replaced). + text = "Use `circle-packing` for this. See also circle-packing theory." + result = _inject_cross_refs(text, concepts, current_slug="lobachevsky-function") + assert "`circle-packing`" in result # inline-code preserved + assert "[[circle-packing]]" in result # bare occurrence replaced + + +def test_inject_cross_refs_full_list_even_when_single_concept() -> None: + """Cross-ref injection uses the full concept list, not just the compiled concept.""" + concepts = [ + Concept(slug="circle-packing", title="Circle Packing", aliases=[]), + Concept(slug="discrete-conformal-map", title="Discrete Conformal Map", aliases=[]), + FIXTURE_CONCEPT, + ] + # Compiling lobachevsky-function only; text mentions the other two concepts + text = "Discrete Conformal Map is used with Circle Packing." + result = _inject_cross_refs(text, concepts, current_slug="lobachevsky-function") + assert "[[circle-packing]]" in result + assert "[[discrete-conformal-map]]" in result + + # --------------------------------------------------------------------------- # compile_concept (full mock) # ---------------------------------------------------------------------------