Phred/bin/scripts/generate_toc.php
Funky Waddle cf30f3e41a
Some checks failed
CI / PHP ${{ matrix.php }} (8.1) (push) Has been cancelled
CI / PHP ${{ matrix.php }} (8.2) (push) Has been cancelled
CI / PHP ${{ matrix.php }} (8.3) (push) Has been cancelled
M12: Serialization/validation utilities and pagination
2025-12-23 17:40:02 -06:00

87 lines
3 KiB
PHP

<?php
declare(strict_types=1);
/**
* Automatically generates a Table of Contents and injects breadcrumbs for Markdown files.
*/
function generateToc(string $filePath, string $name): void
{
if (!is_file($filePath)) {
echo "$name not found.\n";
return;
}
$content = file_get_contents($filePath);
$lines = explode("\n", $content);
$inToc = false;
$headers = [];
$bodyLines = [];
foreach ($lines as $line) {
if (trim($line) === '## Table of Contents') {
$inToc = true;
continue;
}
// We assume the TOC ends at the next header or double newline
if ($inToc && (str_starts_with($line, '## ') || (str_contains($content, 'This document outlines') && str_starts_with($line, 'This document outlines')))) {
$inToc = false;
}
if (!$inToc) {
if (preg_match('/^(##+) (.*)/', $line, $matches)) {
$level = strlen($matches[1]) - 1; // ## is level 1 in TOC
if ($level > 0) {
$anchor = strtolower(trim($matches[2]));
$anchor = str_replace('~~', '', $anchor);
$anchor = preg_replace('/[^a-z0-9]+/', '-', $anchor);
$anchor = trim($anchor, '-');
$headers[] = [
'level' => $level,
'title' => trim($matches[2]),
'anchor' => $anchor
];
}
// Add "Back to Top" breadcrumb before level 2 headers, except for the first one or if already present
if ($level === 1 && !empty($bodyLines)) {
$lastLine = end($bodyLines);
if ($lastLine !== '' && !str_contains($lastLine, '[↑ Back to Top]')) {
$bodyLines[] = '';
$bodyLines[] = '[↑ Back to Top](#table-of-contents)';
}
}
}
$bodyLines[] = $line;
}
}
// Generate TOC text
$tocText = "## Table of Contents\n";
foreach ($headers as $header) {
if ($header['title'] === 'Table of Contents') continue;
$indent = str_repeat(' ', $header['level'] - 1);
$tocText .= "{$indent}- [{$header['title']}](#{$header['anchor']})\n";
}
// Reconstruct file
$finalLines = [];
$tocInserted = false;
foreach ($bodyLines as $line) {
if (!$tocInserted && (str_starts_with($line, '## ') || (str_contains($content, 'This document outlines') && str_starts_with($line, 'This document outlines')))) {
$finalLines[] = $tocText;
$tocInserted = true;
}
$finalLines[] = $line;
}
file_put_contents($filePath, implode("\n", $finalLines));
echo "$name TOC and breadcrumbs regenerated successfully.\n";
}
$root = __DIR__ . '/../..';
generateToc($root . '/SPECS.md', 'SPECS.md');
generateToc($root . '/MILESTONES.md', 'MILESTONES.md');