fix: track citations a source_ref part drops

Annotations and text after a located head were discarded before
anchoring, so a citation written there never moved with its pin:
zesarux cited two macros four lines off. The leftover text of every
source_ref is now scanned as prose, and the two citations are fixed.
This commit is contained in:
Abdessamad Derraz committed 2026-10-17 18:25:03 +02:00
1 parent f036bce930
commit 9992490854
3 files changed
+90 -2

No files matched your search

+2 -2
View File
@@ -202,7 +202,7 @@ files:
required: true
bundled: true
system: sinclair-zx-spectrum
source_ref: "src/cpu.c:3381-3383, src/utils.c:4100 (MACHINE_IS_SPECTRUM_128_P2 covers Pentagon, src/cpu.h:545)"
source_ref: "src/cpu.c:3381-3383, src/utils.c:4100 (MACHINE_IS_SPECTRUM_128_P2 covers Pentagon, src/cpu.h:549)"
- name: "se.rom"
sha1: "14321f2092b8af21b52b35c1518d9a753d6618d4"
@@ -346,7 +346,7 @@ files:
required: true
bundled: true
system: timex-tc2048
source_ref: "src/cpu.c:3418-3420, src/utils.c:4098 (TC2048 falls under MACHINE_IS_SPECTRUM_16_48, src/cpu.h:543)"
source_ref: "src/cpu.c:3418-3420, src/utils.c:4098 (TC2048 falls under MACHINE_IS_SPECTRUM_16_48, src/cpu.h:547)"
- name: "ts2068.rom"
sha1: "34cd8113bf75b65a4da6eea355f9d46ad635f7e4"
+51
View File
@@ -158,6 +158,48 @@ def split_source_ref(ref: str) -> list[RefPart]:
return parts
def ref_prose_text(ref: str) -> str:
"""The text of a source_ref its parts do not read, positions kept.
split_source_ref keeps the located head of each chunk and drops the
rest: annotations, and prose after the head. A citation written there
(`(MACHINE_IS_SPECTRUM_128_P2 covers Pentagon, src/cpu.h:545)`, or the
second half of `pce.cpp:477-483 + huc.cpp:284-304`) was never anchored.
Every character a part reads is blanked, so the prose scanner finds
the rest at the offsets it has in the scalar.
"""
masked = list(ref)
depth = 0
outside: list[int] = []
def settle() -> None:
text = "".join(ref[i] for i in outside)
head = text.strip().split(" ", 1)[0] if text.strip() else ""
located = bool(head) and bool(
_LOCATED_RE.match(head) or _BARE_RANGE_RE.match(head)
)
blank = outside
if located:
first = next(k for k, i in enumerate(outside) if not ref[i].isspace())
blank = outside[first:first + len(head)]
for i in blank:
masked[i] = " "
outside.clear()
for index, char in enumerate(ref):
if char == "(":
depth += 1
if depth == 0:
if char in ",;":
settle()
else:
outside.append(index)
if char == ")":
depth = max(0, depth - 1)
settle()
return "".join(masked)
def split_outside_parentheses(text: str) -> list[str]:
"""Split on commas and semicolons, ignoring those inside an annotation.
@@ -676,6 +718,15 @@ def collect_citations(document: dict, text: str | None = None) -> list[Citation]
field=field, kind="ref", ref=ref, entry=holder,
holder=holder, key=key, label=label,
))
if isinstance(value, str):
for line, path_span, spans, parts, run, wrapped in _prose_runs(
ref_prose_text(value), known
):
citations.append(Citation(
field=field, kind="prose", ref=run, holder=holder,
key=key, line=line, path_span=path_span, spans=spans,
parts=parts, wrapped=wrapped,
))
continue
for line, path_span, spans, parts, run, wrapped in _prose_runs(
value, known
+37
View File
@@ -3003,3 +3003,40 @@ class ProseCitationsUseTheTrackedForm(unittest.TestCase):
for match in self.UNTRACKED.finditer(line)
]
self.assertEqual(found, [])
class ACitationInsideASourceRefIsTracked(unittest.TestCase):
"""split_source_ref reads each chunk's located head and drops the rest.
zesarux cited src/cpu.h:545 inside an annotation, four lines off at its
pin, and pce-emu joined two citations with ` + `: neither was anchored."""
def citations(self, ref: str) -> list[str]:
from scripts.profile_sync import collect_citations
document = {"files": [{"name": "x.rom", "source_ref": ref}]}
return [(c.kind, c.ref) for c in collect_citations(document)]
def test_an_annotation_citation_is_read_as_prose(self):
found = self.citations(
"src/cpu.c:3381-3383, src/utils.c:4100 "
"(MACHINE_IS_SPECTRUM_128_P2 covers Pentagon, src/cpu.h:549)"
)
self.assertIn(("prose", "src/cpu.h:549"), found)
self.assertNotIn(("prose", "src/cpu.c:3381-3383"), found)
def test_a_joined_citation_is_read_as_prose(self):
found = self.citations(
"src/pce_fast/pce.cpp:477-483 + src/pce_fast/huc.cpp:284-304"
)
self.assertIn(("prose", "src/pce_fast/huc.cpp:284-304"), found)
self.assertEqual([c for c in found if c[0] == "prose"],
[("prose", "src/pce_fast/huc.cpp:284-304")])
def test_offsets_match_the_scalar(self):
from scripts.profile_sync import ref_prose_text
ref = "a.c:1-2, 5 (see b.c:7)"
masked = ref_prose_text(ref)
self.assertEqual(len(masked), len(ref))
self.assertEqual(masked.index("b.c:7"), ref.index("b.c:7"))
self.assertNotIn("a.c", masked)