Skip to content

Commit

Permalink
[BUGFIX] Display external pages on all levels of the main menu
Browse files Browse the repository at this point in the history
Sphinx handels it also that way

(cherry picked from commit 068aeb8)
  • Loading branch information
linawolf committed Mar 12, 2024
1 parent 4174c75 commit 451c53b
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers;

use phpDocumentor\Guides\Compiler\CompilerContext;
use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode;
use phpDocumentor\Guides\Nodes\Menu\ExternalMenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuNode;
use phpDocumentor\Guides\Nodes\Menu\TocNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\TitleNode;
use Psr\Log\LoggerInterface;

use function assert;

final class ExternalMenuEntryNodeTransformer extends AbstractMenuEntryNodeTransformer
{
use MenuEntryManagement;
use SubSectionHierarchyHandler;

public function __construct(
LoggerInterface $logger,
) {
parent::__construct($logger);
}

public function supports(Node $node): bool
{
return $node instanceof ExternalMenuEntryNode;
}

/** @return list<MenuEntryNode> */
protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array
{
assert($entryNode instanceof ExternalMenuEntryNode);

$newEntryNode = new ExternalEntryNode(
$entryNode->getUrl(),
($entryNode->getValue() ?? TitleNode::emptyNode())->toString(),
);

if ($currentMenu instanceof TocNode) {
$this->attachDocumentEntriesToParents([$newEntryNode], $compilerContext, '');
}

return [$entryNode];
}

public function getPriority(): int
{
// After DocumentEntryTransformer
return 4500;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,43 @@

use phpDocumentor\Guides\Compiler\CompilerContext;
use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode;
use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode;

use function sprintf;
use function str_starts_with;

trait MenuEntryManagement
{
/** @param DocumentEntryNode[] $documentEntriesInTree */
/** @param array<DocumentEntryNode|ExternalEntryNode> $entryNodes */
private function attachDocumentEntriesToParents(
array $documentEntriesInTree,
array $entryNodes,
CompilerContext $compilerContext,
string $currentPath,
): void {
foreach ($documentEntriesInTree as $documentEntryInToc) {
if ($documentEntryInToc->isRoot() || $currentPath === $documentEntryInToc->getFile()) {
// The root page may not be attached to any other
continue;
}
foreach ($entryNodes as $entryNode) {
if ($entryNode instanceof DocumentEntryNode) {
if (($entryNode->isRoot() || $currentPath === $entryNode->getFile())) {
// The root page may not be attached to any other
continue;
}

if ($documentEntryInToc->getParent() !== null && $documentEntryInToc->getParent() !== $compilerContext->getDocumentNode()->getDocumentEntry()) {
$this->logger->warning(sprintf(
'Document %s has been added to parents %s and %s. The `toctree` directive changes the '
. 'position of documents in the document tree. Use the `menu` directive to only display a menu without changing the document tree.',
$documentEntryInToc->getFile(),
$documentEntryInToc->getParent()->getFile(),
$compilerContext->getDocumentNode()->getDocumentEntry()->getFile(),
), $compilerContext->getLoggerInformation());
}
if ($entryNode->getParent() !== null && $entryNode->getParent() !== $compilerContext->getDocumentNode()->getDocumentEntry()) {
$this->logger->warning(sprintf(
'Document %s has been added to parents %s and %s. The `toctree` directive changes the '
. 'position of documents in the document tree. Use the `menu` directive to only display a menu without changing the document tree.',
$entryNode->getFile(),
$entryNode->getParent()->getFile(),
$compilerContext->getDocumentNode()->getDocumentEntry()->getFile(),
), $compilerContext->getLoggerInformation());
}

if ($documentEntryInToc->getParent() !== null) {
continue;
if ($entryNode->getParent() !== null) {
continue;
}
}

$documentEntryInToc->setParent($compilerContext->getDocumentNode()->getDocumentEntry());
$compilerContext->getDocumentNode()->getDocumentEntry()->addChild($documentEntryInToc);
$entryNode->setParent($compilerContext->getDocumentNode()->getDocumentEntry());
$compilerContext->getDocumentNode()->getDocumentEntry()->addChild($entryNode);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
use phpDocumentor\Guides\Compiler\CompilerContext;
use phpDocumentor\Guides\Exception\DocumentEntryNotFound;
use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode;
use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode;
use phpDocumentor\Guides\Nodes\Menu\ExternalMenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\InternalMenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\TitleNode;

use function assert;
use function sprintf;
Expand Down Expand Up @@ -74,24 +77,38 @@ private function addSubEntries(
return;
}

foreach ($documentEntry->getChildren() as $subDocumentEntryNode) {
$subMenuEntry = new InternalMenuEntryNode(
$subDocumentEntryNode->getFile(),
$subDocumentEntryNode->getTitle(),
[],
false,
$currentLevel,
'',
self::isInRootline($subDocumentEntryNode, $compilerContext->getDocumentNode()->getDocumentEntry()),
self::isCurrent($subDocumentEntryNode, $compilerContext->getDocumentNode()->getFilePath()),
);
foreach ($documentEntry->getMenuEntries() as $subEntryNode) {
if ($subEntryNode instanceof DocumentEntryNode) {
$subMenuEntry = new InternalMenuEntryNode(
$subEntryNode->getFile(),
$subEntryNode->getTitle(),
[],
false,
$currentLevel,
'',
self::isInRootline($subEntryNode, $compilerContext->getDocumentNode()->getDocumentEntry()),
self::isCurrent($subEntryNode, $compilerContext->getDocumentNode()->getFilePath()),
);

if (!$currentMenu->hasOption('titlesonly') && $maxDepth - $currentLevel + 1 > 1) {
$this->addSubSectionsToMenuEntries($subEntryNode, $subMenuEntry, $maxDepth - $currentLevel + 2);
}

$sectionMenuEntry->addMenuEntry($subMenuEntry);
$this->addSubEntries($currentMenu, $compilerContext, $subMenuEntry, $subEntryNode, $currentLevel + 1, $maxDepth);
continue;
}

if (!$currentMenu->hasOption('titlesonly') && $maxDepth - $currentLevel + 1 > 1) {
$this->addSubSectionsToMenuEntries($subDocumentEntryNode, $subMenuEntry, $maxDepth - $currentLevel + 2);
if (!($subEntryNode instanceof ExternalEntryNode)) {
continue;
}

$subMenuEntry = new ExternalMenuEntryNode(
$subEntryNode->getValue(),
TitleNode::fromString($subEntryNode->getTitle()),
$currentLevel,
);
$sectionMenuEntry->addMenuEntry($subMenuEntry);
$this->addSubEntries($currentMenu, $compilerContext, $subMenuEntry, $subDocumentEntryNode, $currentLevel + 1, $maxDepth);
}
}
}
41 changes: 29 additions & 12 deletions packages/guides/src/Compiler/Passes/GlobalMenuPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
use phpDocumentor\Guides\Compiler\CompilerPass;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode;
use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode;
use phpDocumentor\Guides\Nodes\Menu\ExternalMenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\InternalMenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\NavMenuNode;
use phpDocumentor\Guides\Nodes\Menu\TocNode;
use phpDocumentor\Guides\Nodes\TitleNode;
use phpDocumentor\Guides\Settings\SettingsManager;
use Throwable;

Expand Down Expand Up @@ -118,7 +121,7 @@ private function getMenuEntryWithChildren(CompilerContext $compilerContext, Menu
private function addSubEntries(
CompilerContext $compilerContext,
MenuEntryNode $sectionMenuEntry,
DocumentEntryNode $documentEntry,
DocumentEntryNode|ExternalEntryNode $entryNode,
int $currentLevel,
int $maxDepth,
): void {
Expand All @@ -130,17 +133,31 @@ private function addSubEntries(
return;
}

foreach ($documentEntry->getChildren() as $subDocumentEntryNode) {
$subMenuEntry = new InternalMenuEntryNode(
$subDocumentEntryNode->getFile(),
$subDocumentEntryNode->getTitle(),
[],
false,
$currentLevel,
'',
);
$sectionMenuEntry->addMenuEntry($subMenuEntry);
$this->addSubEntries($compilerContext, $subMenuEntry, $subDocumentEntryNode, $currentLevel + 1, $maxDepth);
if (!$entryNode instanceof DocumentEntryNode) {
return;
}

foreach ($entryNode->getMenuEntries() as $subEntryNode) {
if ($subEntryNode instanceof DocumentEntryNode) {
$subMenuEntry = new InternalMenuEntryNode(
$subEntryNode->getFile(),
$subEntryNode->getTitle(),
[],
false,
$currentLevel,
'',
);
$sectionMenuEntry->addMenuEntry($subMenuEntry);
$this->addSubEntries($compilerContext, $subMenuEntry, $subEntryNode, $currentLevel + 1, $maxDepth);
} elseif ($subEntryNode instanceof ExternalEntryNode) {
$subMenuEntry = new ExternalMenuEntryNode(
$subEntryNode->getValue(),
TitleNode::fromString($subEntryNode->getTitle()),
$currentLevel,
);
$sectionMenuEntry->addMenuEntry($subMenuEntry);
$this->addSubEntries($compilerContext, $subMenuEntry, $subEntryNode, $currentLevel + 1, $maxDepth);
}
}
}
}
31 changes: 17 additions & 14 deletions packages/guides/src/Nodes/DocumentTree/DocumentEntryNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@

namespace phpDocumentor\Guides\Nodes\DocumentTree;

use phpDocumentor\Guides\Nodes\AbstractNode;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\Nodes\TitleNode;

/** @extends AbstractNode<DocumentNode> */
final class DocumentEntryNode extends AbstractNode
/** @extends EntryNode<DocumentNode> */
final class DocumentEntryNode extends EntryNode
{
/** @var DocumentEntryNode[] */
/** @var array<DocumentEntryNode|ExternalEntryNode> */
private array $entries = [];
/** @var SectionEntryNode[] */
private array $sections = [];
private DocumentEntryNode|null $parent = null;

public function __construct(
private readonly string $file,
Expand All @@ -38,25 +36,30 @@ public function getTitle(): TitleNode
return $this->titleNode;
}

public function addChild(DocumentEntryNode $child): void
public function addChild(DocumentEntryNode|ExternalEntryNode $child): void
{
$this->entries[] = $child;
}

/** @return DocumentEntryNode[] */
/**
* @return array<DocumentEntryNode>
*/
public function getChildren(): array
{
return $this->entries;
}
// Filter the entries array to only include DocumentEntryNode instances
$documentEntries = array_filter($this->entries, function ($entry) {
return $entry instanceof DocumentEntryNode;
});

public function getParent(): DocumentEntryNode|null
{
return $this->parent;
// Re-index the array to maintain numeric keys
return array_values($documentEntries);
}

public function setParent(DocumentEntryNode|null $parent): void

/** @return array<DocumentEntryNode|ExternalEntryNode> */
public function getMenuEntries(): array
{
$this->parent = $parent;
return $this->entries;
}

/** @return SectionEntryNode[] */
Expand Down
35 changes: 35 additions & 0 deletions packages/guides/src/Nodes/DocumentTree/EntryNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\Guides\Nodes\DocumentTree;

use phpDocumentor\Guides\Nodes\AbstractNode;

/**
* @template TValue
* @extends AbstractNode<TValue>
*/
abstract class EntryNode extends AbstractNode
{
private DocumentEntryNode|null $parent = null;

public function getParent(): DocumentEntryNode|null
{
return $this->parent;
}

public function setParent(DocumentEntryNode|null $parent): void
{
$this->parent = $parent;
}
}
28 changes: 28 additions & 0 deletions packages/guides/src/Nodes/DocumentTree/ExternalEntryNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/

namespace phpDocumentor\Guides\Nodes\DocumentTree;

/** @extends EntryNode<string> */
final class ExternalEntryNode extends EntryNode
{
public function __construct(string $value, public readonly string $title)
{
$this->value = $value;
}

public function getTitle(): string
{
return $this->title;
}
}
Loading

0 comments on commit 451c53b

Please sign in to comment.