Skip to content

Commit 33bc10a

Browse files
Merge pull request #2 from xentral/fix-link-generation-and-navparent-hierarchy
fixes @uses, @navid, and @ref link generation, and enables @navparent navigation hierarchy display and sorting for PHPDoc-generated pages.
2 parents 9e9d0cc + 1a1f65c commit 33bc10a

File tree

1 file changed

+45
-17
lines changed

1 file changed

+45
-17
lines changed

src/MkDocsGenerator.php

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public function generate(array $documentationNodes, string $docsBaseDir): void
6161
$navIdMap = $this->buildNavIdMap($processedNodes);
6262
$usedBy = $this->buildUsedByMap($processedNodes);
6363

64+
// Build reverse registry for file path -> owner lookup (needed for navigation hierarchy)
65+
$reverseRegistry = $this->buildReverseRegistry($registry);
66+
6467
// Build cross-reference maps for bi-directional linking
6568
$referencedBy = $this->buildReferencedByMap($processedNodes, $registry, $navPathMap, $navIdMap);
6669

@@ -79,7 +82,7 @@ public function generate(array $documentationNodes, string $docsBaseDir): void
7982
$this->generateFiles($docTree, $docsOutputDir);
8083

8184
// Generate navigation structure with title mapping
82-
$navStructure = $this->generateNavStructure($docTree, '', $navPathMap, $processedNodes);
85+
$navStructure = $this->generateNavStructure($docTree, '', $navPathMap, $processedNodes, $reverseRegistry);
8386
array_unshift($navStructure, ['Home' => 'index.md']);
8487

8588
// Generate config
@@ -398,6 +401,18 @@ private function buildRegistry(array $documentationNodes): array
398401
return $registry;
399402
}
400403

404+
private function buildReverseRegistry(array $registry): array
405+
{
406+
// Build reverse mapping: file path -> owner
407+
// This allows us to look up which node generated a specific file path
408+
$reverseRegistry = [];
409+
foreach ($registry as $owner => $filePath) {
410+
$reverseRegistry[$filePath] = $owner;
411+
}
412+
413+
return $reverseRegistry;
414+
}
415+
401416
private function buildNavPathMap(array $documentationNodes): array
402417
{
403418
$navPathMap = [];
@@ -1016,7 +1031,7 @@ private function generateFiles(array $tree, string $currentPath): void
10161031
}
10171032
}
10181033

1019-
private function generateNavStructure(array $tree, string $pathPrefix = '', array $navPathMap = [], array $allNodes = []): array
1034+
private function generateNavStructure(array $tree, string $pathPrefix = '', array $navPathMap = [], array $allNodes = [], array $reverseRegistry = []): array
10201035
{
10211036
$navItems = [];
10221037

@@ -1033,7 +1048,7 @@ private function generateNavStructure(array $tree, string $pathPrefix = '', arra
10331048
$dirName = ucwords(str_replace(['_', '-'], ' ', $key));
10341049
$navItems[] = [
10351050
'title' => $dirName,
1036-
'content' => $this->generateNavStructure($value, $pathPrefix.$key.'/', $navPathMap, $allNodes),
1051+
'content' => $this->generateNavStructure($value, $pathPrefix.$key.'/', $navPathMap, $allNodes, $reverseRegistry),
10371052
'type' => $this->getNavItemType($dirName),
10381053
'sortKey' => strtolower($dirName),
10391054
'isChild' => false,
@@ -1042,7 +1057,7 @@ private function generateNavStructure(array $tree, string $pathPrefix = '', arra
10421057
} else {
10431058
// For files, find the display title and node metadata
10441059
$displayTitle = $this->findDisplayTitleForFile($filePath, $allNodes);
1045-
$nodeMetadata = $this->findNodeMetadataForFile($filePath, $allNodes);
1060+
$nodeMetadata = $this->findNodeMetadataForFile($filePath, $allNodes, $reverseRegistry);
10461061

10471062
if ($displayTitle) {
10481063
$title = $displayTitle;
@@ -1072,7 +1087,7 @@ private function generateNavStructure(array $tree, string $pathPrefix = '', arra
10721087
}
10731088

10741089
// Sort the nav items with new parent-child logic
1075-
usort($navItems, function ($a, $b) use ($allNodes) {
1090+
usort($navItems, function ($a, $b) use ($allNodes, $reverseRegistry) {
10761091
// Apply type priority first: regular -> static -> uncategorised
10771092
if ($a['type'] !== $b['type']) {
10781093
$typePriority = ['regular' => 1, 'static' => 2, 'uncategorised' => 3];
@@ -1082,10 +1097,10 @@ private function generateNavStructure(array $tree, string $pathPrefix = '', arra
10821097

10831098
// Within same type, handle parent-child relationships
10841099
// If one is child and the other is parent, parent comes first
1085-
if ($a['isChild'] && ! $b['isChild'] && $a['parentKey'] === $this->findParentIdentifier($b, $allNodes)) {
1100+
if ($a['isChild'] && ! $b['isChild'] && $a['parentKey'] === $this->findParentIdentifier($b, $allNodes, $reverseRegistry)) {
10861101
return 1; // a (child) comes after b (parent)
10871102
}
1088-
if ($b['isChild'] && ! $a['isChild'] && $b['parentKey'] === $this->findParentIdentifier($a, $allNodes)) {
1103+
if ($b['isChild'] && ! $a['isChild'] && $b['parentKey'] === $this->findParentIdentifier($a, $allNodes, $reverseRegistry)) {
10891104
return -1; // a (parent) comes before b (child)
10901105
}
10911106

@@ -1162,7 +1177,7 @@ private function findDisplayTitleForFile(string $filePath, array $allNodes): ?st
11621177
return null;
11631178
}
11641179

1165-
private function findNodeMetadataForFile(string $filePath, array $allNodes): ?array
1180+
private function findNodeMetadataForFile(string $filePath, array $allNodes, array $reverseRegistry = []): ?array
11661181
{
11671182
// Normalize function to handle case and space/underscore differences
11681183
$normalize = (fn ($path) => strtolower(str_replace(' ', '_', $path)));
@@ -1188,23 +1203,31 @@ private function findNodeMetadataForFile(string $filePath, array $allNodes): ?ar
11881203
return $node; // Return the entire node as metadata
11891204
}
11901205
}
1191-
} else {
1192-
// For PHPDoc content, we could match based on generated paths
1193-
// This would require more complex path matching logic
1194-
// For now, we'll skip this and handle only static content
1206+
}
1207+
}
1208+
1209+
// For PHPDoc content, use the reverse registry to find the owner
1210+
if (! empty($reverseRegistry) && isset($reverseRegistry[$filePath])) {
1211+
$owner = $reverseRegistry[$filePath];
1212+
1213+
// Find the node with this owner
1214+
foreach ($allNodes as $node) {
1215+
if ($node['owner'] === $owner) {
1216+
return $node; // Return the entire node as metadata
1217+
}
11951218
}
11961219
}
11971220

11981221
return null;
11991222
}
12001223

1201-
private function findParentIdentifier(array $navItem, array $allNodes): ?string
1224+
private function findParentIdentifier(array $navItem, array $allNodes, array $reverseRegistry = []): ?string
12021225
{
12031226
// For navigation items that are files, we need to find their corresponding node
12041227
// and return the parent identifier (navId or owner)
12051228
if (isset($navItem['content']) && is_string($navItem['content'])) {
12061229
$filePath = $navItem['content'];
1207-
$nodeMetadata = $this->findNodeMetadataForFile($filePath, $allNodes);
1230+
$nodeMetadata = $this->findNodeMetadataForFile($filePath, $allNodes, $reverseRegistry);
12081231

12091232
if ($nodeMetadata) {
12101233
return $nodeMetadata['navId'] ?? $nodeMetadata['owner'] ?? null;
@@ -1231,9 +1254,14 @@ private function makeRelativePath(string $path, string $base): string
12311254
// Calculate proper relative path between two locations in the docs tree
12321255
// This handles cross-references between different directory structures
12331256
// using proper ../ notation that MkDocs expects
1234-
1235-
$pathParts = explode('/', $path);
1236-
$baseParts = explode('/', dirname($base));
1257+
//
1258+
// Strip .md extension from both paths before calculating relative path
1259+
// because MkDocs serves each .md file as a directory (e.g., main-process.md -> /main-process/)
1260+
$path = preg_replace('/\.md$/', '', $path);
1261+
$base = preg_replace('/\.md$/', '', $base);
1262+
1263+
$pathParts = explode('/', (string) $path);
1264+
$baseParts = explode('/', (string) $base);
12371265

12381266
// Remove common path prefix
12391267
while (count($pathParts) > 0 && count($baseParts) > 0 && $pathParts[0] === $baseParts[0]) {

0 commit comments

Comments
 (0)