diff --git a/scripts/generate_site.py b/scripts/generate_site.py index ab49e477..d4313b05 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -1373,10 +1373,26 @@ def _plain_markdown(text: str) -> str: text = re.sub(r"<[^>]+>", " ", text) text = re.sub(r"!\[([^]]*)\]\([^)]*\)", r"\1", text) text = re.sub(r"\[([^]]+)\]\([^)]*\)", r"\1", text) - text = re.sub(r"[`*_~]", "", text) + # Code spans keep their text whole: validate_schemas.py read + # validateschemas.py once every underscore was taken for emphasis. + pieces = re.split(r"(`[^`]*`)", text) + text = "".join( + piece.strip("`") if piece.startswith("`") + else re.sub(r"(? str: + """A description cut at a word, never inside one: six snippets ended + in "op..." or "Fo...".""" + if len(plain) <= limit: + return plain + cut = plain[: limit + 1].rsplit(" ", 1)[0] if " " in plain[:limit] else plain[:limit] + return cut.rstrip(" ,;:-(.") + "..." + + def _page_description(markdown: str, relative: Path, title: str) -> str: key = relative.as_posix() specific = { @@ -1398,6 +1414,12 @@ def _page_description(markdown: str, relative: Path, title: str) -> str: about = PAGE_DATA.get(key, {}).get("about", {}) pinned = any(a.get("version") for a in (about if isinstance(about, list) else [about])) kind = "source-pinned emulator firmware profile" if pinned else "emulator firmware profile" + if not PAGE_DATA.get(key, {}).get("hasPart"): + # 152 cores that load no file announced paths and hashes. + kind = kind.replace(" firmware", "") + trees = (" The data directories it reads are listed." + if "\n## Data directories\n" in markdown else "") + return f"{title}: {kind}. No BIOS or firmware file is required.{trees}" return f"{title}: {kind} with paths, hashes, requirements and validation behavior." if key.startswith("systems/") and relative.stem != "index": return f"{title}: indexed firmware files, hashes, variants, provenance and platform or emulator usage." @@ -1412,7 +1434,7 @@ def _page_description(markdown: str, relative: Path, title: str) -> str: continue plain = _plain_markdown(stripped) if len(plain) >= 35: - return plain[:157].rstrip(" ,;:-") + ("..." if len(plain) > 157 else "") + return _shorten(plain) return f"{title}: RetroBIOS source-traced retrogaming firmware reference." @@ -1733,7 +1755,9 @@ def decorate_markdown_pages(docs: Path) -> None: page_path = "" if len(relative.parts) == 1 else "/".join(relative.parts[:-1]) + "/" else: page_path = relative.with_suffix("").as_posix() + "/" - canonical = urllib.parse.urljoin(SITE_URL, page_path) + # Encoded as mkdocs encodes the canonical link and the sitemap: + # idtech4a++ named .../idtech4a++/ here and .../idtech4a%2B%2B/ there. + canonical = urllib.parse.urljoin(SITE_URL, urllib.parse.quote(page_path)) schema_type = "WebSite" if relative.as_posix() == "index.md" else "TechArticle" if relative.as_posix() == "data.md": schema_type = "Dataset" diff --git a/tests/test_site_emulator_page.py b/tests/test_site_emulator_page.py index 8f0e60af..fd364aad 100644 --- a/tests/test_site_emulator_page.py +++ b/tests/test_site_emulator_page.py @@ -69,5 +69,49 @@ class WhoShipsTheBuildAndWhy(unittest.TestCase): "found only as rebuilt copies) |", page) +class WhatASearchSnippetSays(unittest.TestCase): + def test_a_core_loading_no_file_promises_no_hash(self): + """152 pages of cores that load no file announced paths and hashes.""" + rel = Path("emulators/lutro.md") + saved = dict(gs.PAGE_DATA) + self.addCleanup(lambda: (gs.PAGE_DATA.clear(), gs.PAGE_DATA.update(saved))) + gs.PAGE_DATA[rel.as_posix()] = {} + text = gs._page_description("# Lutro\n", rel, "Lutro") + self.assertNotIn("hashes", text) + self.assertIn("No BIOS or firmware file is required.", text) + gs.PAGE_DATA[rel.as_posix()] = {"hasPart": [{"name": "a.bin"}]} + self.assertIn("paths, hashes", gs._page_description("# Lutro\n", rel, "Lutro")) + + def test_a_long_paragraph_is_cut_at_a_word(self): + plain = ("files/?sha1= is the address that does not move: it opens the card " + "wherever the file is now, " * 4) + text = gs._shorten(plain) + self.assertTrue(text.endswith("...")) + self.assertLessEqual(len(text), 160) + self.assertTrue(plain.startswith(text[:-3])) + self.assertIn(plain[len(text) - 3], " ,:") + + def test_code_keeps_its_underscores(self): + self.assertEqual(gs._plain_markdown("only `validate_schemas.py` and _this_"), + "only validate_schemas.py and this") + + def test_structured_url_is_encoded_like_the_canonical(self): + """mkdocs encodes idtech4a++ as idtech4a%2B%2B in the canonical link + and the sitemap, and the JSON-LD named the unencoded URL.""" + import json + import tempfile + + with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp: + page = Path(tmp) / "emulators" / "idtech4a++.md" + page.parent.mkdir() + page.write_text("# idTech4A++\n\nSome text long enough to be a description.\n") + gs.decorate_markdown_pages(Path(tmp)) + text = page.read_text() + data = json.loads(text.split('")[0].replace("<\\/", "