|
42 | 42 |
|
43 | 43 |
|
44 | 44 | def slugify(heading: str) -> str: |
45 | | - """GitHub-style anchor slug for a markdown heading. |
| 45 | + """GitHub-faithful anchor slug for a markdown heading. |
46 | 46 |
|
47 | | - GitHub keeps underscores and hyphens in anchors, lowercases everything, |
48 | | - strips the remaining punctuation, and replaces spaces with hyphens. |
| 47 | + Mirrors github/html-pipeline's TableOfContentsFilter: lowercase, drop |
| 48 | + everything that is not a word character (letters, digits, underscore), |
| 49 | + hyphen, or space, then replace *each* space with a hyphen (consecutive |
| 50 | + spaces yield consecutive hyphens, e.g. `a b` -> `a--b`). |
49 | 51 | """ |
50 | 52 | s: str = heading.strip().lower() |
51 | | - s = re.sub(r"[`*~]", "", s) # drop markdown emphasis / backticks |
52 | | - s = re.sub(r"[^\w\s\-]", "", s) # drop remaining punctuation |
53 | | - s = re.sub(r"\s+", "-", s) # spaces -> hyphens |
| 53 | + s = re.sub(r"^#{1,6}\s+", "", s) # drop leading markdown markers |
| 54 | + s = re.sub(r"[^\w\- ]", "", s) # remove punctuation |
| 55 | + s = s.strip() # drop space left by markers |
| 56 | + s = s.replace(" ", "-") # each space -> hyphen |
54 | 57 | return s |
55 | 58 |
|
56 | 59 |
|
57 | 60 | def headings_slugs(path: Path) -> List[str]: |
58 | | - """Return the GitHub anchor slugs of all headings in a markdown file.""" |
| 61 | + """Return the GitHub anchor slugs of all headings in a markdown file. |
| 62 | +
|
| 63 | + Duplicate headings get GitHub's `-1`, `-2`, ... suffixes (the first |
| 64 | + occurrence keeps the bare slug). |
| 65 | + """ |
59 | 66 | slugs: List[str] = [] |
| 67 | + seen: Dict[str, int] = {} |
60 | 68 | try: |
61 | 69 | text: str = path.read_text(encoding="utf-8", errors="replace") |
62 | 70 | except OSError: |
63 | 71 | return slugs |
64 | 72 | for line in text.splitlines(): |
65 | 73 | m = re.match(r"^#{1,6}\s+(.+?)\s*#*\s*$", line) |
66 | 74 | if m: |
67 | | - slugs.append(slugify(m.group(1))) |
| 75 | + slug: str = slugify(m.group(1)) |
| 76 | + n: int = seen.get(slug, 0) |
| 77 | + seen[slug] = n + 1 |
| 78 | + slugs.append(slug if n == 0 else f"{slug}-{n}") |
68 | 79 | return slugs |
69 | 80 |
|
70 | 81 |
|
|
0 commit comments