mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
feat: resolve bare and annotated ref paths
This commit is contained in:
1 parent
344599f7ef
commit
c585452d46
2 files changed
+90
-4
No files matched your search
+28
-3
@@ -45,7 +45,8 @@ HEAD = "head"
|
||||
_REF_RE = re.compile(r"^(?P<path>[^:]+?)(?::(?P<start>\d+)(?:-(?P<end>\d+))?)?$")
|
||||
_BARE_RANGE_RE = re.compile(r"^\d+(?:-\d+)?$")
|
||||
_SPLIT_RE = re.compile(r"[,;]")
|
||||
_ANNOTATION_RE = re.compile(r"\s*\([^)]*\)")
|
||||
_ANNOTATION_RE = re.compile(r"\s*\([^)]*\)?")
|
||||
_LOCATED_RE = re.compile(r"^[^\s:]+:\d+(?:-\d+)?$")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -119,8 +120,11 @@ def split_source_ref(ref: str) -> list[RefPart]:
|
||||
across 50 profiles carry them.
|
||||
"""
|
||||
parts: list[RefPart] = []
|
||||
for raw in _SPLIT_RE.split(str(ref or "")):
|
||||
chunk = _ANNOTATION_RE.sub("", raw).strip()
|
||||
# Annotations come out first: they may contain the separators themselves,
|
||||
# and splitting inside one would cut it into meaningless fragments.
|
||||
cleaned = _ANNOTATION_RE.sub("", str(ref or ""))
|
||||
for raw in _SPLIT_RE.split(cleaned):
|
||||
chunk = _trim_prose(raw.strip())
|
||||
if not chunk:
|
||||
continue
|
||||
if _BARE_RANGE_RE.match(chunk) and parts:
|
||||
@@ -134,6 +138,22 @@ def split_source_ref(ref: str) -> list[RefPart]:
|
||||
return parts
|
||||
|
||||
|
||||
def _trim_prose(chunk: str) -> str:
|
||||
"""Drop a trailing comment written without parentheses.
|
||||
|
||||
`libretro.cpp:1520-1521 candidates_a1200` cites a location followed by a
|
||||
note. The leading token is kept only when it already reads as a reference,
|
||||
so `munt ROMInfo.cpp`, which names an external project, stays intact and
|
||||
is reported rather than silently truncated to `munt`.
|
||||
"""
|
||||
if " " not in chunk:
|
||||
return chunk
|
||||
head = chunk.split(" ", 1)[0]
|
||||
if _LOCATED_RE.match(head) or _BARE_RANGE_RE.match(head):
|
||||
return head
|
||||
return chunk
|
||||
|
||||
|
||||
def collect_tokens(entry: dict) -> list[str]:
|
||||
"""Values declared by one file entry: hashes, then name as fallback."""
|
||||
tokens: list[str] = []
|
||||
@@ -334,6 +354,11 @@ def anchor_part(part: RefPart, fetch, rename_getter, describe=None) -> PartResul
|
||||
)
|
||||
|
||||
pin_lines = fetch(PIN, part.path)
|
||||
if pin_lines is None and path != part.path:
|
||||
# A ref may cite a bare filename the tree search resolved at HEAD; the
|
||||
# same resolved path usually holds at the pin. A genuine rename keeps
|
||||
# the old path at the pin, which is why that one is tried first.
|
||||
pin_lines = fetch(PIN, path)
|
||||
if pin_lines is None:
|
||||
return PartResult(
|
||||
part, "GONE", None, None, None, [], "pin revision missing", slug, url
|
||||
|
||||
@@ -140,6 +140,33 @@ class TestSplitSourceRef(unittest.TestCase):
|
||||
[("src/emulator.cpp", 234, 234), ("src/core/aes.cpp", 13, 92)],
|
||||
)
|
||||
|
||||
def test_annotation_containing_a_separator_is_not_split(self):
|
||||
parts = split_source_ref(
|
||||
"libretro.cpp:944-951 (system_dir + name, then fopen), core.c:12"
|
||||
)
|
||||
self.assertEqual(
|
||||
[(p.path, p.start) for p in parts],
|
||||
[("libretro.cpp", 944), ("core.c", 12)],
|
||||
)
|
||||
|
||||
def test_trailing_prose_after_a_location_is_dropped(self):
|
||||
parts = split_source_ref("libretro/libretro.cpp:1520-1521 candidates_a1200")
|
||||
self.assertEqual(
|
||||
(parts[0].path, parts[0].start, parts[0].end),
|
||||
("libretro/libretro.cpp", 1520, 1521),
|
||||
)
|
||||
|
||||
def test_bare_range_followed_by_prose_still_continues(self):
|
||||
parts = split_source_ref("a.c:10-12, 151-156 load loop")
|
||||
self.assertEqual(
|
||||
[(p.path, p.start, p.end) for p in parts],
|
||||
[("a.c", 10, 12), ("a.c", 151, 156)],
|
||||
)
|
||||
|
||||
def test_external_project_citation_is_kept_whole(self):
|
||||
parts = split_source_ref("munt ROMInfo.cpp")
|
||||
self.assertEqual(parts[0].path, "munt ROMInfo.cpp")
|
||||
|
||||
def test_leading_bare_range_stays_a_path(self):
|
||||
parts = split_source_ref("273-285")
|
||||
self.assertEqual(parts[0].path, "273-285")
|
||||
@@ -359,6 +386,32 @@ class TestAnchorPart(unittest.TestCase):
|
||||
self.assertEqual(result.new_path, "new.c")
|
||||
self.assertEqual(result.start, 2)
|
||||
|
||||
def test_bare_filename_resolves_at_both_revisions(self):
|
||||
fetch = make_fetch(
|
||||
{
|
||||
(PIN, "src/midi/mt32.cpp"): ["x", "hit"],
|
||||
(HEAD, "src/midi/mt32.cpp"): ["x", "hit"],
|
||||
}
|
||||
)
|
||||
comparison = CompareResult([], True)
|
||||
result = anchor_part(
|
||||
RefPart("mt32.cpp", 2, 2),
|
||||
fetch,
|
||||
renamer(comparison, ["src/midi/mt32.cpp"]),
|
||||
)
|
||||
self.assertEqual(result.status, "RENAMED")
|
||||
self.assertEqual(result.new_path, "src/midi/mt32.cpp")
|
||||
self.assertEqual(result.start, 2)
|
||||
|
||||
def test_genuine_rename_still_reads_the_pin_at_the_old_path(self):
|
||||
fetch = make_fetch(
|
||||
{(PIN, "old.c"): ["x", "hit"], (HEAD, "new.c"): ["pad", "x", "hit"]}
|
||||
)
|
||||
comparison = CompareResult([FileChange("renamed", "new.c", "old.c")], False)
|
||||
result = anchor_part(RefPart("old.c", 2, 2), fetch, renamer(comparison))
|
||||
self.assertEqual(result.status, "RENAMED")
|
||||
self.assertEqual(result.start, 3)
|
||||
|
||||
def test_gone_when_pin_file_missing(self):
|
||||
result = anchor_part(
|
||||
RefPart("a.c", 1, 1), make_fetch({}), renamer(CompareResult([], False))
|
||||
@@ -595,10 +648,18 @@ class TestBuildReport(unittest.TestCase):
|
||||
self.assertEqual(part.status, "ANCHORED")
|
||||
self.assertIsNone(part.new_path)
|
||||
|
||||
def test_prefix_matching_no_repository_is_not_stripped(self):
|
||||
def test_unknown_prefix_resolves_by_basename_not_by_stripping(self):
|
||||
self.files[("pinsha", "a.c")] = ["x", "hit"]
|
||||
self.files[("headsha", "a.c")] = ["x", "hit"]
|
||||
report = build_report("test", self._profile(["other/a.c:2"]), self.dir)
|
||||
part = report.entries[0].parts[0]
|
||||
self.assertEqual(part.status, "RENAMED")
|
||||
self.assertEqual(part.new_path, "a.c")
|
||||
|
||||
def test_basename_absent_everywhere_stays_gone(self):
|
||||
self.files[("pinsha", "a.c")] = ["x", "hit"]
|
||||
self.files[("headsha", "a.c")] = ["x", "hit"]
|
||||
report = build_report("test", self._profile(["other/absent.c:2"]), self.dir)
|
||||
self.assertEqual(report.entries[0].parts[0].status, "GONE")
|
||||
|
||||
def test_missing_date_and_commit_is_skipped(self):
|
||||
|
||||
Reference in new issue
Block a user