feat: prose citations link to the pinned source

Notes, file notes and platform details carried file:line citations as plain text although profile_sync keeps each on the profile's pin. A citation now links when the profile reads one repository and the path is rooted or already used by a structured source_ref. Platform detail source_refs go through the same renderer as file cards.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 05:44:08 +02:00
1 parent c87610b921
commit 10a1f475b0
3 files changed
+94 -5

No files matched your search

+14 -4
View File
@@ -2793,7 +2793,7 @@ def _render_emulator_file(
for d in details:
lines.append(f"- {d}")
if fnote:
lines.append(f"- {_escape_tags(str(fnote))}")
lines.append(f"- {_escape_tags(link_prose_citations(profile, str(fnote)))}")
if not in_repo and f.get("unsourceable"):
# The badge alone sent readers after a dump that cannot exist, and the
# reason was only on the gaps page, which the card did not link.
@@ -2876,14 +2876,23 @@ def _render_platform_details(profile: dict) -> list[str]:
if isinstance(pv, dict):
lines.append(f" **{pk}:**")
for sk, sv in pv.items():
lines.append(f" - {sk}: {_plain_value(sv)}")
lines.append(f" - {sk}: {_detail_value(profile, sk, sv)}")
else:
lines.append(f" **{pk}:** {_plain_value(pv)}")
lines.append(f" **{pk}:** {_detail_value(profile, pk, pv)}")
lines.append("")
return lines
def _detail_value(profile: dict, key: object, value: object) -> str:
"""A platform detail, its citations linked at the profile's pin."""
if key == "source_ref" and isinstance(value, (str, dict)):
return _source_ref_markdown(profile, value)
if isinstance(value, str):
return _escape_tags(link_prose_citations(profile, value))
return _plain_value(value)
def _plain_value(value: object) -> str:
"""A scalar or a list of scalars as a reader writes it, not as Python does."""
if isinstance(value, bool):
@@ -3176,7 +3185,7 @@ def generate_emulator_page(
# Notes
if notes:
indented = _admonition_body(notes)
indented = _admonition_body(link_prose_citations(profile, notes))
lines.extend(['???+ note "Technical notes"', f" {indented}", ""])
if not files:
@@ -4859,6 +4868,7 @@ from sitewrite import ( # noqa: E402,F401
_sweep_generated,
)
from sitesources import ( # noqa: E402,F401
link_prose_citations,
revision_markdown,
_repo_pins,
_forge_sources,
+54 -1
View File
@@ -162,4 +162,57 @@ def _source_ref_markdown(profile: dict, value: str | dict | None) -> str:
if label:
text = f"**{label}:** {text}"
rendered_groups.append(text)
return "; ".join(rendered_groups)
return ", ".join(rendered_groups)
def link_prose_citations(profile: dict, text: str) -> str:
"""Prose file:line citations as pinned links, where the target is certain.
profile_sync keeps these citations on their pin, and the page printed
4,328 of them as plain text. A citation links only when the profile reads
one repository and the path is rooted (it holds a directory) or is one a
structured source_ref of the profile already uses: a bare name could be
anywhere in the tree, and a link to the wrong file is a false citation.
Text already in backticks or in a link is left alone.
"""
from profile_sync import _prose_runs
candidates = _forge_sources(profile)
if len(candidates) != 1 or ":" not in text:
return text
repo, pin = candidates[0]
structured = {
part.path
for entry in profile.get("files") or []
if isinstance(entry, dict)
for _label, refs in source_ref_values(entry.get("source_ref"))
for part in split_source_ref(refs)
}
out = []
for line in text.split("\n"):
guarded = _guarded_spans(line)
edits = []
for _n, path_span, spans, parts, _run, wrapped in _prose_runs(line):
path = parts[0].path
if wrapped or " " in path or not _PATH_RE.fullmatch(path):
continue
if "/" not in path and path not in structured:
continue
if any(lo <= path_span[0] < hi for lo, hi in guarded):
continue
for index, ((lo, hi), part) in enumerate(zip(spans, parts)):
start = path_span[0] if index == 0 else lo
url = _source_permalink(repo, pin, part.path, part.start, part.end)
edits.append((start, hi, f"[`{line[start:hi]}`]({url})"))
for start, end, link in sorted(edits, reverse=True):
line = line[:start] + link + line[end:]
out.append(line)
return "\n".join(out)
_PATH_RE = re.compile(r"^[A-Za-z0-9_.@/+~-]+$")
def _guarded_spans(line: str) -> list[tuple[int, int]]:
"""Code spans and links of a markdown line."""
return [m.span() for m in re.finditer(r"`[^`]*`|\[[^\]]*\]\([^)]*\)", line)]