Skip to content
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
@@ -1,11 +1,11 @@
<div class="level-x">
{% for entry in node.menuEntries -%}
{% spaceless %}
{% apply spaceless %}
<a href="{{ renderLink(entry.url) }}"
class="nav-link {% if entry.options.active %} active {% endif %}"
{% if entry.options.active %} aria-current="page" {% endif %} >
{{ renderNode(entry.value.value) }}
</a>
{% endspaceless %}
{% endapply %}
{% endfor %}
</div>
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<ul class="level-{{ node.level }}">
{% for entry in node.menuEntries -%}
{% spaceless %}
{% apply spaceless %}
<li class="nav-item">
<a href="{{ renderLink(entry.url) }}"
class="nav-link {% if entry.options.active %} active {% endif %}"
{% if entry.options.active %} aria-current="page" {% endif %} >
{{ renderNode(entry.value.value) }}
</a>
</li>
{% endspaceless %}
{% endapply %}
{%- endfor %}
</ul>
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

use function assert;

use const PHP_INT_MAX;

/** @implements NodeTransformer<TocNode> */
class ContentMenuNodeWithSectionEntryTransformer implements NodeTransformer
{
// Setting a default level prevents PHP errors in case of circular references
private const DEFAULT_MAX_LEVELS = 10;

public function enterNode(Node $node, CompilerContext $compilerContext): Node
{
return $node;
Expand All @@ -31,7 +32,7 @@ public function leaveNode(Node $node, CompilerContext $compilerContext): Node|nu
return $node;
}

$depth = (int) $node->getOption('depth', PHP_INT_MAX);
$depth = (int) $node->getOption('depth', self::DEFAULT_MAX_LEVELS);
$documentEntry = $compilerContext->getDocumentNode()->getDocumentEntry();

$menuEntries = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function leaveNode(Node $node, CompilerContext $compilerContext): Node|nu
$this->logger->warning('Document has not title', $node->getLoggerInformation());
}

$entry = new DocumentEntryNode($node->getFilePath(), $node->getTitle() ?? TitleNode::emptyNode());
$entry = new DocumentEntryNode($node->getFilePath(), $node->getTitle() ?? TitleNode::emptyNode(), $node->isRoot());
$compilerContext->getProjectNode()->addDocumentEntry($entry);

return $node->setDocumentEntry($entry);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace phpDocumentor\Guides\Compiler\NodeTransformers;

use phpDocumentor\Guides\Compiler\CompilerContext;
use phpDocumentor\Guides\Compiler\NodeTransformer;
use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\MenuNode;
use phpDocumentor\Guides\Nodes\Menu\TocNode;
use phpDocumentor\Guides\Nodes\Node;

/** @implements NodeTransformer<MenuNode> */
class TocNodeSubLevelTransformer implements NodeTransformer
{
// Setting a default level prevents PHP errors in case of circular references
private const DEFAULT_MAX_LEVELS = 10;

public function enterNode(Node $node, CompilerContext $compilerContext): Node
{
return $node;
}

public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null
{
if (!$node instanceof TocNode) {
return $node;
}

$maxDepth = (int) $node->getOption('maxdepth', self::DEFAULT_MAX_LEVELS);


foreach ($node->getMenuEntries() as $menuEntry) {
$documentEntryOfMenuEntry = $compilerContext->getProjectNode()->getDocumentEntry($menuEntry->getUrl());
$this->addSubEntries($menuEntry, $documentEntryOfMenuEntry, $menuEntry->getLevel() + 1, $maxDepth);
}

return $node;
}

private function addSubEntries(
MenuEntryNode $sectionMenuEntry,
DocumentEntryNode $documentEntry,
int $currentLevel,
int $maxDepth,
): void {
if ($maxDepth < $currentLevel) {
return;
}

foreach ($documentEntry->getChildren() as $subDocumentEntryNode) {
$subMenuEntry = new MenuEntryNode(
$subDocumentEntryNode->getFile(),
$subDocumentEntryNode->getTitle(),
[],
false,
$currentLevel,
);
$sectionMenuEntry->addMenuEntry($subMenuEntry);
$this->addSubEntries($subMenuEntry, $subDocumentEntryNode, $currentLevel + 1, $maxDepth);
}
}

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

public function getPriority(): int
{
// After TocNodeWithDocumentEntryTransformer
return 4000;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,25 @@
use phpDocumentor\Guides\Nodes\Menu\MenuNode;
use phpDocumentor\Guides\Nodes\Menu\TocNode;
use phpDocumentor\Guides\Nodes\Node;
use Psr\Log\LoggerInterface;

use function array_pop;
use function assert;
use function explode;
use function implode;
use function preg_match;
use function sprintf;
use function str_replace;
use function str_starts_with;

/** @implements NodeTransformer<MenuNode> */
class TocNodeWithDocumentEntryTransformer implements NodeTransformer
{
public function __construct(
private readonly LoggerInterface $logger,
) {
}

public function enterNode(Node $node, CompilerContext $compilerContext): Node
{
return $node;
Expand All @@ -45,7 +52,10 @@ public function leaveNode(Node $node, CompilerContext $compilerContext): Node|nu

foreach ($files as $file) {
foreach ($documentEntries as $documentEntry) {
if (!$this->isEqualAbsolutePath($documentEntry, $file, $currentPath, $glob) && !$this->isEqualRelativePath($documentEntry, $file, $currentPath, $glob)) {
if (
!self::isEqualAbsolutePath($documentEntry->getFile(), $file, $currentPath, $glob)
&& !self::isEqualRelativePath($documentEntry->getFile(), $file, $currentPath, $glob)
) {
continue;
}

Expand All @@ -64,6 +74,29 @@ public function leaveNode(Node $node, CompilerContext $compilerContext): Node|nu
}
}

foreach ($documentEntriesInTree as $documentEntryInToc) {
if ($documentEntryInToc->isRoot()) {
// 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',
$documentEntryInToc->getFile(),
$documentEntryInToc->getParent()->getFile(),
$compilerContext->getDocumentNode()->getDocumentEntry()->getFile(),
));
}

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

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

$menuEntries[] = $menuEntry;
if (!$glob) {
// If glob is not set, there may only be one result per listed file
Expand Down Expand Up @@ -96,42 +129,59 @@ public function getPriority(): int
return 4500;
}

private function isEqualAbsolutePath(DocumentEntryNode $documentEntry, string $file, string $currentPath, bool $glob): bool
private static function isEqualAbsolutePath(string $actualFile, string $expectedFile, string $currentFile, bool $glob): bool
{
if ($file === '/' . $documentEntry->getFile()) {
if (!self::isAbsoluteFile($expectedFile)) {
return false;
}

if ($expectedFile === '/' . $actualFile) {
return true;
}

return $this->isGlob($glob, $documentEntry->getFile(), $currentPath, $file, '/');
return self::isGlob($glob, $actualFile, $currentFile, $expectedFile, '/');
}

private function isEqualRelativePath(DocumentEntryNode $documentEntry, string $file, string $currentPath, bool $glob): bool
private static function isEqualRelativePath(string $actualFile, string $expectedFile, string $currentFile, bool $glob): bool
{
if (str_starts_with($file, '/')) {
if (self::isAbsoluteFile($expectedFile)) {
return false;
}

$current = explode('/', $currentPath);
$current = explode('/', $currentFile);
array_pop($current);
$current[] = $file;
$absolute = implode('/', $current);
$current[] = $expectedFile;
$absoluteExpectedFile = implode('/', $current);

if ($absolute === $documentEntry->getFile()) {
if ($absoluteExpectedFile === $actualFile) {
return true;
}

return $this->isGlob($glob, $documentEntry->getFile(), $currentPath, $file, '');
return self::isGlob($glob, $actualFile, $currentFile, $absoluteExpectedFile, '');
}

private function isGlob(bool $glob, string $documentEntryFile, string $currentPath, string $file, string $prefix): bool
private static function isGlob(bool $glob, string $documentEntryFile, string $currentPath, string $file, string $prefix): bool
{
if ($glob && $documentEntryFile !== $currentPath) {
$file = str_replace('*', '[a-zA-Z0-9]*', $file);
$file = str_replace('*', '[^\/]*', $file);
$pattern = '`^' . $file . '$`';

return preg_match($pattern, $prefix . $documentEntryFile) > 0;
}

return false;
}

public static function isPatternMatchingFile(string $absoluteExpectedFile, string $actualFile): bool
{
$pattern = str_replace('*', '[a-zA-Z0-9-_]*', $absoluteExpectedFile);
$pattern = '`^' . $pattern . '$`';

return preg_match($pattern, $actualFile) > 0;
}

public static function isAbsoluteFile(string $expectedFile): bool
{
return str_starts_with($expectedFile, '/');
}
}
21 changes: 9 additions & 12 deletions packages/guides/src/Handlers/ParseDirectoryHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function handle(ParseDirectoryCommand $command): array
$currentDirectory = $command->getDirectory();
$extension = $command->getInputFormat();

$this->guardThatAnIndexFileExists(
$indexName = $this->getDirectoryIndexFile(
$origin,
$currentDirectory,
$extension,
Expand All @@ -39,34 +39,31 @@ public function handle(ParseDirectoryCommand $command): array
$documents = [];
foreach ($files as $file) {
$documents[] = $this->commandBus->handle(
new ParseFileCommand($origin, $currentDirectory, $file, $extension, 1, $command->getProjectNode()),
new ParseFileCommand($origin, $currentDirectory, $file, $extension, 1, $command->getProjectNode(), $indexName === $file),
);
}

return $documents;
}

private function guardThatAnIndexFileExists(
private function getDirectoryIndexFile(
FilesystemInterface $filesystem,
string $directory,
string $sourceFormat,
): void {
): string {
$extension = $sourceFormat;
$hasIndexFile = false;
foreach (self::INDEX_FILE_NAMES as $indexName) {
$indexFilename = sprintf('%s.%s', $indexName, $extension);
if ($filesystem->has($directory . '/' . $indexFilename)) {
$hasIndexFile = true;
break;
return $indexName;
}
}

if (!$hasIndexFile) {
$indexFilename = sprintf('%s.%s', self::INDEX_FILE_NAMES[0], $extension);
$indexFilename = sprintf('%s.%s', self::INDEX_FILE_NAMES[0], $extension);

throw new InvalidArgumentException(
sprintf('Could not find index file "%s" in "%s"', $indexFilename, $directory),
);
}
throw new InvalidArgumentException(
sprintf('Could not find index file "%s" in "%s"', $indexFilename, $directory),
);
}
}
6 changes: 6 additions & 0 deletions packages/guides/src/Handlers/ParseFileCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public function __construct(
private readonly string $extension,
private readonly int $initialHeaderLevel,
private readonly ProjectNode $projectNode,
private readonly bool $isRoot,
) {
}

Expand Down Expand Up @@ -57,4 +58,9 @@ public function getProjectNode(): ProjectNode
{
return $this->projectNode;
}

public function isRoot(): bool
{
return $this->isRoot;
}
}
4 changes: 3 additions & 1 deletion packages/guides/src/Handlers/ParseFileHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public function handle(ParseFileCommand $command): DocumentNode|null
$command->getExtension(),
$command->getInitialHeaderLevel(),
$command->getProjectNode(),
$command->isRoot(),
);
}

Expand All @@ -74,6 +75,7 @@ private function createDocument(
string $extension,
int $initialHeaderLevel,
ProjectNode $projectNode,
bool $isRoot,
): DocumentNode|null {
$path = $this->buildPathOnFileSystem($fileName, $documentFolder, $extension);
$fileContents = $this->getFileContents($origin, $path);
Expand All @@ -93,7 +95,7 @@ private function createDocument(

$document = null;
try {
$document = $this->parser->parse($preParseDocumentEvent->getContents(), $extension);
$document = $this->parser->parse($preParseDocumentEvent->getContents(), $extension)->withIsRoot($isRoot);
} catch (RuntimeException) {
$this->logger->error(
sprintf('Unable to parse %s, input format was not recognized', $path),
Expand Down
Loading