Skip to content

Commit

Permalink
Merge pull request #923 from phpDocumentor/backport/1.x/pr-900
Browse files Browse the repository at this point in the history
[1.x] Merge pull request #900 from phpDocumentor/task/menu-external
  • Loading branch information
phpdoc-bot authored Mar 12, 2024
2 parents 4174c75 + caf4b09 commit 9a7e59a
Show file tree
Hide file tree
Showing 14 changed files with 412 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);
}
}
}
55 changes: 44 additions & 11 deletions packages/guides/src/Compiler/Passes/GlobalMenuPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
use phpDocumentor\Guides\Compiler\CompilerPass;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode;
use phpDocumentor\Guides\Nodes\DocumentTree\EntryNode;
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 @@ -115,10 +119,11 @@ private function getMenuEntryWithChildren(CompilerContext $compilerContext, Menu
return $newMenuEntry;
}

/** @param EntryNode<DocumentEntryNode|ExternalEntryNode>|ExternalEntryNode $entryNode */
private function addSubEntries(
CompilerContext $compilerContext,
MenuEntryNode $sectionMenuEntry,
DocumentEntryNode $documentEntry,
EntryNode $entryNode,
int $currentLevel,
int $maxDepth,
): void {
Expand All @@ -130,17 +135,45 @@ private function addSubEntries(
return;
}

foreach ($documentEntry->getChildren() as $subDocumentEntryNode) {
$subMenuEntry = new InternalMenuEntryNode(
$subDocumentEntryNode->getFile(),
$subDocumentEntryNode->getTitle(),
[],
false,
$currentLevel,
'',
);
if (!$entryNode instanceof DocumentEntryNode) {
return;
}

foreach ($entryNode->getMenuEntries() as $subEntryNode) {
$subMenuEntry = match ($subEntryNode::class) {
DocumentEntryNode::class => $this->createInternalMenuEntry($subEntryNode, $currentLevel),
ExternalEntryNode::class => $this->createExternalMenuEntry($subEntryNode, $currentLevel),
};

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

private function createInternalMenuEntry(DocumentEntryNode $subEntryNode, int $currentLevel): InternalMenuEntryNode
{
return new InternalMenuEntryNode(
$subEntryNode->getFile(),
$subEntryNode->getTitle(),
[],
false,
$currentLevel,
'',
);
}

private function createExternalMenuEntry(ExternalEntryNode $subEntryNode, int $currentLevel): ExternalMenuEntryNode
{
return new ExternalMenuEntryNode(
$subEntryNode->getValue(),
TitleNode::fromString($subEntryNode->getTitle()),
$currentLevel,
);
}
}
32 changes: 17 additions & 15 deletions packages/guides/src/Nodes/DocumentTree/DocumentEntryNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@

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
use function array_filter;
use function array_values;

/** @extends EntryNode<DocumentEntryNode|ExternalEntryNode> */
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 +38,27 @@ 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, static 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;
}
}
Loading

0 comments on commit 9a7e59a

Please sign in to comment.