Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.x] Merge pull request #900 from phpDocumentor/task/menu-external #923

Merged
merged 2 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading