From 76c23b4dae4b7f6e0cf42011a261f93e343355e7 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 1 Mar 2024 21:01:38 +0100 Subject: [PATCH 01/53] [DOCS] add stable version to readme (cherry picked from commit 1b13ea233067f2e738464d751537fc63b3928888) --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 53603bf..81b5ba7 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,10 @@ :alt: PHP Version Require :target: https://packagist.org/packages/phpdocumentor/guides +.. image:: http://poser.pugx.org/phpdocumentor/guides/v/stable + :alt: Latest Stable Version + :target: https://packagist.org/packages/phpdocumentor/guides + .. image:: http://poser.pugx.org/phpdocumentor/guides/v/unstable :alt: Latest Unstable Version :target: https://packagist.org/packages/phpdocumentor/guides From 6e0eeaad2924ef4a5ce850e77acbc3060fe3b2b1 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Sun, 3 Mar 2024 21:30:59 +0100 Subject: [PATCH 02/53] [TASK] prepare for 1.0 release --- composer.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composer.json b/composer.json index d7d6306..33e00ac 100644 --- a/composer.json +++ b/composer.json @@ -38,10 +38,5 @@ }, "require-dev": { "psr/log": "^2.0 || ^3.0" - }, - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } } } From 32fe26d7ba137dd870a6200739c8a5412586e8fd Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 8 Mar 2024 16:42:21 +0100 Subject: [PATCH 03/53] [TASK] Improve menu rendering speed By introducing a cache array for menus we do improve the speed when menus are multiple times rendered. (cherry picked from commit 9f72e028f701aa4352156492265bc9ad1614b061) --- resources/config/guides.php | 6 +++ src/RenderContext.php | 2 +- src/Twig/AssetsExtension.php | 24 ++------- src/Twig/GlobalMenuExtension.php | 83 ++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 src/Twig/GlobalMenuExtension.php diff --git a/resources/config/guides.php b/resources/config/guides.php index 90ced79..94e69a6 100644 --- a/resources/config/guides.php +++ b/resources/config/guides.php @@ -54,6 +54,7 @@ use phpDocumentor\Guides\TemplateRenderer; use phpDocumentor\Guides\Twig\AssetsExtension; use phpDocumentor\Guides\Twig\EnvironmentBuilder; +use phpDocumentor\Guides\Twig\GlobalMenuExtension; use phpDocumentor\Guides\Twig\Theme\ThemeManager; use phpDocumentor\Guides\Twig\TwigTemplateRenderer; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -207,6 +208,11 @@ ->tag('twig.extension') ->autowire() + ->set(GlobalMenuExtension::class) + ->arg('$nodeRenderer', service('phpdoc.guides.output_node_renderer')) + ->tag('twig.extension') + ->autowire() + ->set(ThemeManager::class) ->arg('$filesystemLoader', service(FilesystemLoader::class)) ->arg( diff --git a/src/RenderContext.php b/src/RenderContext.php index 1db91fe..658d0ca 100644 --- a/src/RenderContext.php +++ b/src/RenderContext.php @@ -142,7 +142,7 @@ public function getDirName(): string return $dirname; } - + public function hasCurrentFileName(): bool { return $this->currentFileName !== null; diff --git a/src/Twig/AssetsExtension.php b/src/Twig/AssetsExtension.php index 3594474..98d3357 100644 --- a/src/Twig/AssetsExtension.php +++ b/src/Twig/AssetsExtension.php @@ -21,7 +21,6 @@ use phpDocumentor\Guides\Meta\Target; use phpDocumentor\Guides\NodeRenderers\NodeRenderer; use phpDocumentor\Guides\Nodes\BreadCrumbNode; -use phpDocumentor\Guides\Nodes\CollectionNode; use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; use phpDocumentor\Guides\RenderContext; @@ -29,7 +28,6 @@ use Psr\Log\LoggerInterface; use RuntimeException; use Stringable; -use Throwable; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; use Twig\TwigTest; @@ -39,6 +37,8 @@ final class AssetsExtension extends AbstractExtension { + private GlobalMenuExtension $menuExtension; + /** @param NodeRenderer $nodeRenderer */ public function __construct( private readonly LoggerInterface $logger, @@ -46,6 +46,7 @@ public function __construct( private readonly DocumentNameResolverInterface $documentNameResolver, private readonly UrlGeneratorInterface $urlGenerator, ) { + $this->menuExtension = new GlobalMenuExtension($this->nodeRenderer); } /** @return TwigFunction[] */ @@ -56,7 +57,7 @@ public function getFunctions(): array new TwigFunction('renderNode', $this->renderNode(...), ['is_safe' => ['html'], 'needs_context' => true]), new TwigFunction('renderLink', $this->renderLink(...), ['is_safe' => ['html'], 'needs_context' => true]), new TwigFunction('renderBreadcrumb', $this->renderBreadcrumb(...), ['is_safe' => ['html'], 'needs_context' => true]), - new TwigFunction('renderMenu', $this->renderMenu(...), ['is_safe' => ['html'], 'needs_context' => true]), + new TwigFunction('renderMenu', $this->renderMenu(...), ['is_safe' => ['html'], 'needs_context' => true, 'deprecated' => true]), new TwigFunction('renderTarget', $this->renderTarget(...), ['is_safe' => ['html'], 'needs_context' => true]), ]; } @@ -136,22 +137,7 @@ public function renderBreadcrumb(array $context): string /** @param array{env: RenderContext} $context */ public function renderMenu(array $context, string $menuType, int $maxMenuCount = 0): string { - $renderContext = $this->getRenderContext($context); - $globalMenues = $renderContext->getProjectNode()->getGlobalMenues(); - $menues = []; - foreach ($globalMenues as $menu) { - $menu = $menu->withOptions(['menu' => $menuType]); - try { - $menu = $menu->withCurrentPath($renderContext->getCurrentFileName()); - $menu = $menu->withRootlinePaths($renderContext->getCurrentFileRootline()); - } catch (Throwable) { - // do nothing, we are in a context without active menu like single page or functional test - } - - $menues[] = $menu; - } - - return $this->nodeRenderer->render(new CollectionNode($menues), $renderContext); + return $this->menuExtension->renderMenu($context, $menuType); } /** @param array{env: RenderContext} $context */ diff --git a/src/Twig/GlobalMenuExtension.php b/src/Twig/GlobalMenuExtension.php new file mode 100644 index 0000000..6cf4f5b --- /dev/null +++ b/src/Twig/GlobalMenuExtension.php @@ -0,0 +1,83 @@ + + */ + private array $menuCache = []; + + /** @param NodeRenderer $nodeRenderer */ + public function __construct( + private readonly NodeRenderer $nodeRenderer, + ) { + } + + /** @return TwigFunction[] */ + public function getFunctions(): array + { + return [ + new TwigFunction('renderMenu', $this->renderMenu(...), ['is_safe' => ['html'], 'needs_context' => true]), + ]; + } + + /** @param array{env: RenderContext} $context */ + public function renderMenu(array $context, string $menuType): string + { + $renderContext = $this->getRenderContext($context); + $globalMenues = $renderContext->getProjectNode()->getGlobalMenues(); + if (isset($this->menuCache[$renderContext->getCurrentFileName() . '::' . $menuType])) { + return $this->menuCache[$renderContext->getCurrentFileName() . '::' . $menuType]; + } + + $menues = []; + foreach ($globalMenues as $menu) { + $menu = $menu->withOptions(['menu' => $menuType]); + try { + $menu = $menu->withCurrentPath($renderContext->getCurrentFileName()); + $menu = $menu->withRootlinePaths($renderContext->getCurrentFileRootline()); + } catch (Throwable) { + // do nothing, we are in a context without active menu like single page or functional test + } + + $menues[] = $menu; + } + + return $this->menuCache[$renderContext->getCurrentFileName() . '::' . $menuType] = $this->nodeRenderer->render(new CollectionNode($menues), $renderContext); + } + + /** @param array{env: RenderContext} $context */ + private function getRenderContext(array $context): RenderContext + { + $renderContext = $context['env'] ?? null; + if (!$renderContext instanceof RenderContext) { + throw new RuntimeException('Render context must be set in the twig global state to render nodes'); + } + + return $renderContext; + } +} From 278020c7789b94dee6e4457f7b5266847cc9f937 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 9 Mar 2024 11:13:40 +0100 Subject: [PATCH 04/53] [FEATURE] Support directive image with target resolves https://github.com/phpDocumentor/guides/issues/911 (cherry picked from commit a3508e2135f00c2272b7498f7fcaea363900f8f6) --- resources/config/guides.php | 3 + resources/template/html/body/image.html.twig | 2 + src/Nodes/ImageNode.php | 13 ++++ .../ImageReferenceResolverPreRender.php | 62 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 src/ReferenceResolvers/ImageReferenceResolverPreRender.php diff --git a/resources/config/guides.php b/resources/config/guides.php index 94e69a6..a692518 100644 --- a/resources/config/guides.php +++ b/resources/config/guides.php @@ -28,6 +28,7 @@ use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; use phpDocumentor\Guides\ReferenceResolvers\EmailReferenceResolver; use phpDocumentor\Guides\ReferenceResolvers\ExternalReferenceResolver; +use phpDocumentor\Guides\ReferenceResolvers\ImageReferenceResolverPreRender; use phpDocumentor\Guides\ReferenceResolvers\Interlink\DefaultInventoryLoader; use phpDocumentor\Guides\ReferenceResolvers\Interlink\DefaultInventoryRepository; use phpDocumentor\Guides\ReferenceResolvers\Interlink\InventoryLoader; @@ -192,6 +193,8 @@ ->set(ReferenceResolverPreRender::class) ->tag('phpdoc.guides.prerenderer') + ->set(ImageReferenceResolverPreRender::class) + ->tag('phpdoc.guides.prerenderer') ->set(InMemoryRendererFactory::class) ->arg('$renderSets', tagged_iterator('phpdoc.renderer.typerenderer', 'format')) diff --git a/resources/template/html/body/image.html.twig b/resources/template/html/body/image.html.twig index 9bc9eeb..9564e81 100644 --- a/resources/template/html/body/image.html.twig +++ b/resources/template/html/body/image.html.twig @@ -1,3 +1,4 @@ +{% if node.target %}{% endif %} +{% if node.target %}{% endif %} diff --git a/src/Nodes/ImageNode.php b/src/Nodes/ImageNode.php index cb4f152..14cbd9a 100644 --- a/src/Nodes/ImageNode.php +++ b/src/Nodes/ImageNode.php @@ -13,6 +13,19 @@ namespace phpDocumentor\Guides\Nodes; +use phpDocumentor\Guides\Nodes\Inline\LinkInlineNode; + final class ImageNode extends TextNode { + public LinkInlineNode|null $target = null; + + public function getTarget(): LinkInlineNode|null + { + return $this->target; + } + + public function setTarget(LinkInlineNode|null $target): void + { + $this->target = $target; + } } diff --git a/src/ReferenceResolvers/ImageReferenceResolverPreRender.php b/src/ReferenceResolvers/ImageReferenceResolverPreRender.php new file mode 100644 index 0000000..bb211a4 --- /dev/null +++ b/src/ReferenceResolvers/ImageReferenceResolverPreRender.php @@ -0,0 +1,62 @@ +getTarget() === null) { + return $node; + } + + $referenceLinkNode = $node->getTarget(); + $messages = new Messages(); + $resolved = $this->referenceResolver->resolve($referenceLinkNode, $renderContext, $messages); + if (!$resolved) { + $this->logger->warning( + $messages->getLastWarning()?->getMessage() ?? sprintf( + 'Target %s of image could not be resolved in %s', + $referenceLinkNode->getTargetReference(), + $renderContext->getCurrentFileName(), + ), + array_merge($renderContext->getLoggerInformation(), $messages->getLastWarning()?->getDebugInfo() ?? []), + ); + } + + return $node; + } +} From 81287f48b12b9995309c7437cbe13a99c8183c1c Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Wed, 6 Mar 2024 07:39:03 +0100 Subject: [PATCH 05/53] [FEATURE] Support list-table directive https://sublime-and-sphinx-guide.readthedocs.io/en/latest/tables.html resolves #890 (cherry picked from commit bcbf73a4611706a9390e5e071f30a448cf64d91f) --- .../NodeTransformers/ListNodeTransformer.php | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/Compiler/NodeTransformers/ListNodeTransformer.php diff --git a/src/Compiler/NodeTransformers/ListNodeTransformer.php b/src/Compiler/NodeTransformers/ListNodeTransformer.php new file mode 100644 index 0000000..d2f6dfc --- /dev/null +++ b/src/Compiler/NodeTransformers/ListNodeTransformer.php @@ -0,0 +1,62 @@ + */ +final class ListNodeTransformer implements NodeTransformer +{ + public function __construct( + private readonly LoggerInterface $logger, + ) { + } + + public function enterNode(Node $node, CompilerContext $compilerContext): Node + { + return $node; + } + + public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + { + assert($node instanceof ListNode); + foreach ($node->getChildren() as $listItemNode) { + assert($listItemNode instanceof ListItemNode); + if (!empty($listItemNode->getChildren())) { + continue; + } + + $this->logger->warning('List item without content', $compilerContext->getLoggerInformation()); + } + + return $node; + } + + public function supports(Node $node): bool + { + return $node instanceof ListNode; + } + + public function getPriority(): int + { + return 1000; + } +} From e0b5d8158ab994aa0b4059a0e49dc187b401b271 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Mon, 4 Mar 2024 13:51:59 +0100 Subject: [PATCH 06/53] [BUGFIX] Display external pages on all levels of the main menu Sphinx handels it also that way (cherry picked from commit 068aeb8ca3d55541f0bd557bd22075596861d52b) --- .../ExternalMenuEntryNodeTransformer.php | 66 +++++++++++++++++++ .../MenuEntryManagement.php | 43 ++++++------ .../SubInternalMenuEntryNodeTransformer.php | 45 +++++++++---- src/Compiler/Passes/GlobalMenuPass.php | 41 ++++++++---- src/Nodes/DocumentTree/DocumentEntryNode.php | 31 +++++---- src/Nodes/DocumentTree/EntryNode.php | 35 ++++++++++ src/Nodes/DocumentTree/ExternalEntryNode.php | 28 ++++++++ src/Nodes/DocumentTree/SectionEntryNode.php | 5 +- src/Nodes/Menu/InternalMenuEntryNode.php | 2 +- src/Renderer/DocumentTreeIterator.php | 6 +- 10 files changed, 237 insertions(+), 65 deletions(-) create mode 100644 src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php create mode 100644 src/Nodes/DocumentTree/EntryNode.php create mode 100644 src/Nodes/DocumentTree/ExternalEntryNode.php diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php new file mode 100644 index 0000000..da464c2 --- /dev/null +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php @@ -0,0 +1,66 @@ + */ + 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; + } +} diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php index c2a9262..a1ee0b2 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php @@ -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 $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); } } diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php index d0555af..e362705 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php @@ -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; @@ -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); } } } diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index 11d6c88..6be1c6f 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -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; @@ -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 { @@ -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); + } } } } diff --git a/src/Nodes/DocumentTree/DocumentEntryNode.php b/src/Nodes/DocumentTree/DocumentEntryNode.php index baf51a7..6f7c7b6 100644 --- a/src/Nodes/DocumentTree/DocumentEntryNode.php +++ b/src/Nodes/DocumentTree/DocumentEntryNode.php @@ -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 */ -final class DocumentEntryNode extends AbstractNode +/** @extends EntryNode */ +final class DocumentEntryNode extends EntryNode { - /** @var DocumentEntryNode[] */ + /** @var array */ private array $entries = []; /** @var SectionEntryNode[] */ private array $sections = []; - private DocumentEntryNode|null $parent = null; public function __construct( private readonly string $file, @@ -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 + */ 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 */ + public function getMenuEntries(): array { - $this->parent = $parent; + return $this->entries; } /** @return SectionEntryNode[] */ diff --git a/src/Nodes/DocumentTree/EntryNode.php b/src/Nodes/DocumentTree/EntryNode.php new file mode 100644 index 0000000..2ae0daa --- /dev/null +++ b/src/Nodes/DocumentTree/EntryNode.php @@ -0,0 +1,35 @@ + + */ +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; + } +} diff --git a/src/Nodes/DocumentTree/ExternalEntryNode.php b/src/Nodes/DocumentTree/ExternalEntryNode.php new file mode 100644 index 0000000..cc80768 --- /dev/null +++ b/src/Nodes/DocumentTree/ExternalEntryNode.php @@ -0,0 +1,28 @@ + */ +final class ExternalEntryNode extends EntryNode +{ + public function __construct(string $value, public readonly string $title) + { + $this->value = $value; + } + + public function getTitle(): string + { + return $this->title; + } +} diff --git a/src/Nodes/DocumentTree/SectionEntryNode.php b/src/Nodes/DocumentTree/SectionEntryNode.php index ebc1622..7876c05 100644 --- a/src/Nodes/DocumentTree/SectionEntryNode.php +++ b/src/Nodes/DocumentTree/SectionEntryNode.php @@ -13,12 +13,11 @@ namespace phpDocumentor\Guides\Nodes\DocumentTree; -use phpDocumentor\Guides\Nodes\AbstractNode; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\TitleNode; -/** @extends AbstractNode */ -final class SectionEntryNode extends AbstractNode +/** @extends EntryNode */ +final class SectionEntryNode extends EntryNode { /** @var SectionEntryNode[] */ private array $children = []; diff --git a/src/Nodes/Menu/InternalMenuEntryNode.php b/src/Nodes/Menu/InternalMenuEntryNode.php index b81eba9..dededf7 100644 --- a/src/Nodes/Menu/InternalMenuEntryNode.php +++ b/src/Nodes/Menu/InternalMenuEntryNode.php @@ -57,7 +57,7 @@ public function getEntries(): array return $this->children; } - public function addMenuEntry(InternalMenuEntryNode $menuEntryNode): void + public function addMenuEntry(MenuEntryNode $menuEntryNode): void { $this->children[] = $menuEntryNode; } diff --git a/src/Renderer/DocumentTreeIterator.php b/src/Renderer/DocumentTreeIterator.php index aa46bae..6284cb6 100644 --- a/src/Renderer/DocumentTreeIterator.php +++ b/src/Renderer/DocumentTreeIterator.php @@ -18,6 +18,8 @@ use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; use RecursiveIterator; +use function array_filter; + /** * Iterates over the document tree and returns the documents in the table of contents order. * @@ -77,6 +79,8 @@ public function hasChildren(): bool public function getChildren(): self|null { - return new self($this->levelNodes[$this->position]->getChildren(), $this->documents); + $children = $this->levelNodes[$this->position]->getChildren(); + + return new self($children, $this->documents); } } From 6934f230b299171a5e1ffcc60692b22d6fcd6940 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Tue, 12 Mar 2024 21:02:13 +0100 Subject: [PATCH 07/53] Improve code style of menu pass (cherry picked from commit a4b8b28f8cb11d46a0e15027aeada5b14588e427) --- src/Compiler/Passes/GlobalMenuPass.php | 58 +++++++++++++------- src/Nodes/DocumentTree/DocumentEntryNode.php | 13 ++--- src/Renderer/DocumentTreeIterator.php | 2 - 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index 6be1c6f..a74163a 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -17,6 +17,7 @@ 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; @@ -118,10 +119,11 @@ private function getMenuEntryWithChildren(CompilerContext $compilerContext, Menu return $newMenuEntry; } + /** @param EntryNode|ExternalEntryNode $entryNode */ private function addSubEntries( CompilerContext $compilerContext, MenuEntryNode $sectionMenuEntry, - DocumentEntryNode|ExternalEntryNode $entryNode, + EntryNode $entryNode, int $currentLevel, int $maxDepth, ): void { @@ -138,26 +140,40 @@ private function addSubEntries( } 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); - } + $subMenuEntry = match ($subEntryNode::class) { + DocumentEntryNode::class => $this->createInternalMenuEntry($subEntryNode, $currentLevel), + ExternalEntryNode::class => $this->createExternalMenuEntry($subEntryNode, $currentLevel), + }; + + $sectionMenuEntry->addMenuEntry($subMenuEntry); + $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, + ); + } } diff --git a/src/Nodes/DocumentTree/DocumentEntryNode.php b/src/Nodes/DocumentTree/DocumentEntryNode.php index 6f7c7b6..0bb498b 100644 --- a/src/Nodes/DocumentTree/DocumentEntryNode.php +++ b/src/Nodes/DocumentTree/DocumentEntryNode.php @@ -13,10 +13,12 @@ namespace phpDocumentor\Guides\Nodes\DocumentTree; -use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\TitleNode; -/** @extends EntryNode */ +use function array_filter; +use function array_values; + +/** @extends EntryNode */ final class DocumentEntryNode extends EntryNode { /** @var array */ @@ -41,13 +43,11 @@ public function addChild(DocumentEntryNode|ExternalEntryNode $child): void $this->entries[] = $child; } - /** - * @return array - */ + /** @return array */ public function getChildren(): array { // Filter the entries array to only include DocumentEntryNode instances - $documentEntries = array_filter($this->entries, function ($entry) { + $documentEntries = array_filter($this->entries, static function ($entry) { return $entry instanceof DocumentEntryNode; }); @@ -55,7 +55,6 @@ public function getChildren(): array return array_values($documentEntries); } - /** @return array */ public function getMenuEntries(): array { diff --git a/src/Renderer/DocumentTreeIterator.php b/src/Renderer/DocumentTreeIterator.php index 6284cb6..3026641 100644 --- a/src/Renderer/DocumentTreeIterator.php +++ b/src/Renderer/DocumentTreeIterator.php @@ -18,8 +18,6 @@ use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; use RecursiveIterator; -use function array_filter; - /** * Iterates over the document tree and returns the documents in the table of contents order. * From 642dbef644348bfdf0898e2ca30a090047df4cb7 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 16 Mar 2024 13:42:52 +0100 Subject: [PATCH 08/53] [FEATURE] Support math directive resolves: #928 releases: main, 1.0 (cherry picked from commit dae1810dbf933f14945f2cea540ad97108359a13) --- resources/template/html/body/math.html.twig | 1 + resources/template/html/template.php | 2 ++ src/Nodes/MathNode.php | 25 +++++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 resources/template/html/body/math.html.twig create mode 100644 src/Nodes/MathNode.php diff --git a/resources/template/html/body/math.html.twig b/resources/template/html/body/math.html.twig new file mode 100644 index 0000000..3bdefb3 --- /dev/null +++ b/resources/template/html/body/math.html.twig @@ -0,0 +1 @@ +{{ node.value|raw }} diff --git a/resources/template/html/template.php b/resources/template/html/template.php index 67194cf..21f5e1c 100644 --- a/resources/template/html/template.php +++ b/resources/template/html/template.php @@ -34,6 +34,7 @@ use phpDocumentor\Guides\Nodes\ListItemNode; use phpDocumentor\Guides\Nodes\ListNode; use phpDocumentor\Guides\Nodes\LiteralBlockNode; +use phpDocumentor\Guides\Nodes\MathNode; use phpDocumentor\Guides\Nodes\Metadata\AddressNode; use phpDocumentor\Guides\Nodes\Metadata\AuthorNode; use phpDocumentor\Guides\Nodes\Metadata\AuthorsNode; @@ -75,6 +76,7 @@ ListNode::class => 'body/list/list.html.twig', ListItemNode::class => 'body/list/list-item.html.twig', LiteralBlockNode::class => 'body/literal-block.html.twig', + MathNode::class => 'body/math.html.twig', CitationNode::class => 'body/citation.html.twig', FootnoteNode::class => 'body/footnote.html.twig', AnnotationListNode::class => 'body/annotation-list.html.twig', diff --git a/src/Nodes/MathNode.php b/src/Nodes/MathNode.php new file mode 100644 index 0000000..be29130 --- /dev/null +++ b/src/Nodes/MathNode.php @@ -0,0 +1,25 @@ + Date: Sun, 17 Mar 2024 12:55:45 +0100 Subject: [PATCH 09/53] [TASK] Warn about duplicate link targets releases: main, 1.0 (cherry picked from commit 9daa2a3eaaa5d4c50557fe25937846b8ce06a0df) --- .../CollectLinkTargetsTransformer.php | 46 +++++++++++++++---- src/Nodes/ProjectNode.php | 5 ++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index a0dd7af..13f5071 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -23,9 +23,12 @@ use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer; +use Psr\Log\LoggerInterface; use SplStack; use Webmozart\Assert\Assert; +use function sprintf; + /** @implements NodeTransformer */ final class CollectLinkTargetsTransformer implements NodeTransformer { @@ -34,6 +37,7 @@ final class CollectLinkTargetsTransformer implements NodeTransformer public function __construct( private readonly AnchorNormalizer $anchorReducer, + private LoggerInterface|null $logger = null, ) { /* * TODO: remove stack here, as we should not have sub documents in this way, sub documents are @@ -68,17 +72,43 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node $currentDocument = $this->documentStack->top(); Assert::notNull($currentDocument); $anchor = $node->getId(); - $compilerContext->getProjectNode()->addLinkTarget( - $anchor, - new InternalTarget( - $currentDocument->getFilePath(), + if ($compilerContext->getProjectNode()->hasInternalTarget($anchor, $node->getLinkType())) { + $this->logger?->warning( + sprintf( + 'Duplicate anchor "%s" for link type "%s" in document "%s". The anchor is already used at "%s"', + $anchor, + $node->getLinkType(), + $compilerContext->getDocumentNode()->getFilePath(), + $compilerContext->getProjectNode()->getInternalTarget($anchor, $node->getLinkType())?->getDocumentPath(), + ), + $compilerContext->getLoggerInformation(), + ); + } else { + $compilerContext->getProjectNode()->addLinkTarget( $anchor, - $node->getLinkText(), - $node->getLinkType(), - ), - ); + new InternalTarget( + $currentDocument->getFilePath(), + $anchor, + $node->getLinkText(), + $node->getLinkType(), + ), + ); + } if ($node instanceof MultipleLinkTargetsNode) { foreach ($node->getAdditionalIds() as $id) { + if ($compilerContext->getProjectNode()->hasInternalTarget($id, $node->getLinkType())) { + $this->logger?->warning( + sprintf( + 'Duplicate anchor "%s" for link type "%s" in document "%s". The anchor is already used at "%s"', + $anchor, + $node->getLinkType(), + $compilerContext->getDocumentNode()->getFilePath(), + $compilerContext->getProjectNode()->getInternalTarget($anchor, $node->getLinkType())?->getDocumentPath(), + ), + $compilerContext->getLoggerInformation(), + ); + } + $compilerContext->getProjectNode()->addLinkTarget( $id, new InternalTarget( diff --git a/src/Nodes/ProjectNode.php b/src/Nodes/ProjectNode.php index 2bdb5f3..3ce4bc7 100644 --- a/src/Nodes/ProjectNode.php +++ b/src/Nodes/ProjectNode.php @@ -152,6 +152,11 @@ public function addLinkTarget(string $anchorName, InternalTarget $target): void $this->internalLinkTargets[$target->getLinkType()][$anchorName] = $target; } + public function hasInternalTarget(string $anchorName, string $linkType = SectionNode::STD_LABEL): bool + { + return isset($this->internalLinkTargets[$linkType][$anchorName]); + } + public function getInternalTarget(string $anchorName, string $linkType = SectionNode::STD_LABEL): InternalTarget|null { return $this->internalLinkTargets[$linkType][$anchorName] ?? null; From ee1d00a79ab56b23aedce1d97888602e368fd126 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sun, 17 Mar 2024 18:11:37 +0100 Subject: [PATCH 10/53] [TASK] Warn about duplicate anchors in LinkTargetNode Exclude section for now as this is a bit more tricky. releases: main, 1.0 (cherry picked from commit 17c3a07222db303db6432f988ad21ab2873aa67a) --- .../CollectLinkTargetsTransformer.php | 104 +++++++++++------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index 13f5071..86a5187 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -51,7 +51,11 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node { if ($node instanceof DocumentNode) { $this->documentStack->push($node); - } elseif ($node instanceof AnchorNode) { + + return $node; + } + + if ($node instanceof AnchorNode) { $currentDocument = $compilerContext->getDocumentNode(); $parentSection = $compilerContext->getShadowTree()->getParent()?->getNode(); $title = null; @@ -68,52 +72,48 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node $title, ), ); - } elseif ($node instanceof LinkTargetNode) { + + return $node; + } + + if ($node instanceof SectionNode) { + $currentDocument = $this->documentStack->top(); + Assert::notNull($currentDocument); + $anchorName = $node->getId(); + $compilerContext->getProjectNode()->addLinkTarget( + $anchorName, + new InternalTarget( + $currentDocument->getFilePath(), + $anchorName, + $node->getLinkText(), + $node->getLinkType(), + ), + ); + + return $node; + } + + if ($node instanceof LinkTargetNode) { $currentDocument = $this->documentStack->top(); Assert::notNull($currentDocument); - $anchor = $node->getId(); - if ($compilerContext->getProjectNode()->hasInternalTarget($anchor, $node->getLinkType())) { - $this->logger?->warning( - sprintf( - 'Duplicate anchor "%s" for link type "%s" in document "%s". The anchor is already used at "%s"', - $anchor, - $node->getLinkType(), - $compilerContext->getDocumentNode()->getFilePath(), - $compilerContext->getProjectNode()->getInternalTarget($anchor, $node->getLinkType())?->getDocumentPath(), - ), - $compilerContext->getLoggerInformation(), - ); - } else { - $compilerContext->getProjectNode()->addLinkTarget( + $anchor = $this->anchorReducer->reduceAnchor($node->getId()); + $this->addLinkTargetToProject( + $compilerContext, + new InternalTarget( + $currentDocument->getFilePath(), $anchor, - new InternalTarget( - $currentDocument->getFilePath(), - $anchor, - $node->getLinkText(), - $node->getLinkType(), - ), - ); - } + $node->getLinkText(), + $node->getLinkType(), + ), + ); if ($node instanceof MultipleLinkTargetsNode) { foreach ($node->getAdditionalIds() as $id) { - if ($compilerContext->getProjectNode()->hasInternalTarget($id, $node->getLinkType())) { - $this->logger?->warning( - sprintf( - 'Duplicate anchor "%s" for link type "%s" in document "%s". The anchor is already used at "%s"', - $anchor, - $node->getLinkType(), - $compilerContext->getDocumentNode()->getFilePath(), - $compilerContext->getProjectNode()->getInternalTarget($anchor, $node->getLinkType())?->getDocumentPath(), - ), - $compilerContext->getLoggerInformation(), - ); - } - - $compilerContext->getProjectNode()->addLinkTarget( - $id, + $anchor = $this->anchorReducer->reduceAnchor($id); + $this->addLinkTargetToProject( + $compilerContext, new InternalTarget( $currentDocument->getFilePath(), - $id, + $anchor, $node->getLinkText(), $node->getLinkType(), ), @@ -144,4 +144,28 @@ public function getPriority(): int // After MetasPass return 5000; } + + private function addLinkTargetToProject(CompilerContext $compilerContext, InternalTarget $internalTarget): void + { + if ($compilerContext->getProjectNode()->hasInternalTarget($internalTarget->getAnchor(), $internalTarget->getLinkType())) { + $otherLink = $compilerContext->getProjectNode()->getInternalTarget($internalTarget->getAnchor(), $internalTarget->getLinkType()); + $this->logger?->warning( + sprintf( + 'Duplicate anchor "%s" for link type "%s" in document "%s". The anchor is already used at "%s"', + $internalTarget->getAnchor(), + $internalTarget->getLinkType(), + $compilerContext->getDocumentNode()->getFilePath(), + $otherLink?->getDocumentPath(), + ), + $compilerContext->getLoggerInformation(), + ); + + return; + } + + $compilerContext->getProjectNode()->addLinkTarget( + $internalTarget->getAnchor(), + $internalTarget, + ); + } } From 3c522dd112359553b1d113011f50580542b9b0d9 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 3 Mar 2024 22:37:39 +0100 Subject: [PATCH 11/53] Allow excluding paths from the source directory (cherry picked from commit ce89264b093e68bda280a19f6b799efda854923b) --- src/FileCollector.php | 15 +++++++++++---- src/Handlers/ParseDirectoryCommand.php | 7 +++++++ src/Handlers/ParseDirectoryHandler.php | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/FileCollector.php b/src/FileCollector.php index 1af0209..b062a28 100644 --- a/src/FileCollector.php +++ b/src/FileCollector.php @@ -17,6 +17,8 @@ use Flyfinder\Specification\AndSpecification; use Flyfinder\Specification\HasExtension; use Flyfinder\Specification\InPath; +use Flyfinder\Specification\NotSpecification; +use Flyfinder\Specification\SpecificationInterface; use InvalidArgumentException; use League\Flysystem\FilesystemInterface; @@ -36,14 +38,19 @@ final class FileCollector * This takes into account the presence of cached & fresh MetaEntry * objects, and avoids adding files to the parse queue that have * not changed and whose direct dependencies have not changed. + * + * @param SpecificationInterface|null $excludedSpecification specification that is used to exclude specific files/directories */ - public function collect(FilesystemInterface $filesystem, string $directory, string $extension): Files + public function collect(FilesystemInterface $filesystem, string $directory, string $extension, SpecificationInterface|null $excludedSpecification = null): Files { $directory = trim($directory, '/'); + $specification = new AndSpecification(new InPath(new Path($directory)), new HasExtension([$extension])); + if ($excludedSpecification) { + $specification = new AndSpecification($specification, new NotSpecification($excludedSpecification)); + } + /** @var array> $files */ - $files = $filesystem->find( - new AndSpecification(new InPath(new Path($directory)), new HasExtension([$extension])), - ); + $files = $filesystem->find($specification); // completely populate the splFileInfos property $this->fileInfos = []; diff --git a/src/Handlers/ParseDirectoryCommand.php b/src/Handlers/ParseDirectoryCommand.php index 55a037c..87a8c63 100644 --- a/src/Handlers/ParseDirectoryCommand.php +++ b/src/Handlers/ParseDirectoryCommand.php @@ -13,6 +13,7 @@ namespace phpDocumentor\Guides\Handlers; +use Flyfinder\Specification\SpecificationInterface; use League\Flysystem\FilesystemInterface; use phpDocumentor\Guides\Nodes\ProjectNode; @@ -23,6 +24,7 @@ public function __construct( private readonly string $directory, private readonly string $inputFormat, private readonly ProjectNode $projectNode, + private readonly SpecificationInterface|null $excludedSpecification = null, ) { } @@ -45,4 +47,9 @@ public function getProjectNode(): ProjectNode { return $this->projectNode; } + + public function getExcludedSpecification(): SpecificationInterface|null + { + return $this->excludedSpecification; + } } diff --git a/src/Handlers/ParseDirectoryHandler.php b/src/Handlers/ParseDirectoryHandler.php index 087b2a3..4c70758 100644 --- a/src/Handlers/ParseDirectoryHandler.php +++ b/src/Handlers/ParseDirectoryHandler.php @@ -56,7 +56,7 @@ public function handle(ParseDirectoryCommand $command): array $extension, ); - $files = $this->fileCollector->collect($origin, $currentDirectory, $extension); + $files = $this->fileCollector->collect($origin, $currentDirectory, $extension, $command->getExcludedSpecification()); $postCollectFilesForParsingEvent = $this->eventDispatcher->dispatch( new PostCollectFilesForParsingEvent($command, $files), From 711fb75eb5360712f57875b99ea69de0f6838cdb Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 17 Mar 2024 14:13:37 +0100 Subject: [PATCH 12/53] Improve whitespace control in templates --- resources/template/html/body/admonition.html.twig | 1 + resources/template/html/body/code.html.twig | 2 +- resources/template/html/body/container.html.twig | 1 + .../template/html/body/definition-list.html.twig | 2 +- resources/template/html/body/list/list.html.twig | 1 + resources/template/html/body/menu/menu-item.html.twig | 11 ++++++----- .../template/html/body/menu/menu-level.html.twig | 1 + resources/template/html/body/paragraph.html.twig | 10 ++++------ resources/template/html/body/quote.html.twig | 2 +- .../template/html/body/table/table-body.html.twig | 1 + .../template/html/body/table/table-cell.html.twig | 1 + .../template/html/body/table/table-row.html.twig | 1 + resources/template/html/inline/anchor.html.twig | 2 -- resources/template/html/inline/doc.html.twig | 2 +- resources/template/html/inline/emphasis.html.twig | 2 -- resources/template/html/inline/image.html.twig | 3 +-- resources/template/html/inline/literal.html.twig | 2 -- resources/template/html/inline/nbsp.html.twig | 2 +- resources/template/html/inline/newline.html.twig | 2 +- resources/template/html/inline/ref.html.twig | 2 +- resources/template/html/inline/strong.html.twig | 3 +-- .../html/inline/textroles/abbreviation.html.twig | 2 -- .../template/html/inline/textroles/aspect.html.twig | 2 -- resources/template/html/inline/textroles/br.html.twig | 2 +- .../template/html/inline/textroles/code.html.twig | 3 --- .../template/html/inline/textroles/command.html.twig | 3 --- .../template/html/inline/textroles/dfn.html.twig | 3 --- .../template/html/inline/textroles/emphasis.html.twig | 2 -- .../template/html/inline/textroles/file.html.twig | 3 --- .../template/html/inline/textroles/guilabel.html.twig | 2 -- .../template/html/inline/textroles/kbd.html.twig | 2 -- .../template/html/inline/textroles/literal.html.twig | 2 -- .../html/inline/textroles/mailheader.html.twig | 2 -- .../template/html/inline/textroles/math.html.twig | 2 -- .../template/html/inline/textroles/span.html.twig | 2 -- .../template/html/inline/textroles/strong.html.twig | 2 -- .../template/html/inline/textroles/sub.html.twig | 2 -- .../html/inline/textroles/subscript.html.twig | 2 -- .../template/html/inline/textroles/sup.html.twig | 2 -- .../html/inline/textroles/superscript.html.twig | 2 -- resources/template/html/inline/textroles/t.html.twig | 2 -- .../html/inline/textroles/title-reference.html.twig | 2 -- .../template/html/inline/textroles/title.html.twig | 2 -- .../template/html/inline/textroles/unknown.html.twig | 2 -- resources/template/html/inline/variable.html.twig | 3 +-- resources/template/tex/body/paragraph.tex.twig | 11 ++++------- resources/template/tex/inline/literal.tex.twig | 2 -- .../template/tex/structure/header-title.tex.twig | 2 -- 48 files changed, 32 insertions(+), 90 deletions(-) diff --git a/resources/template/html/body/admonition.html.twig b/resources/template/html/body/admonition.html.twig index f1865a1..3efc77f 100644 --- a/resources/template/html/body/admonition.html.twig +++ b/resources/template/html/body/admonition.html.twig @@ -1,3 +1,4 @@ +
{% if title and isTitled %}

{{ renderNode(title) }}

{% endif %} {% if title and not isTitled %}

{{ renderNode(title) }}

{% endif %} diff --git a/resources/template/html/body/code.html.twig b/resources/template/html/body/code.html.twig index 31c1665..c5efcaa 100644 --- a/resources/template/html/body/code.html.twig +++ b/resources/template/html/body/code.html.twig @@ -12,4 +12,4 @@ {%- if node.emphasizeLines %} data-emphasize-lines="{{ node.emphasizeLines }}"{% endif -%}> {%- include "body/code/highlighted-code.html.twig" -%} -{%- endif -%} +{%~ endif -%} diff --git a/resources/template/html/body/container.html.twig b/resources/template/html/body/container.html.twig index 1a94629..4cf5df7 100644 --- a/resources/template/html/body/container.html.twig +++ b/resources/template/html/body/container.html.twig @@ -1,3 +1,4 @@
{{ renderNode(node) }}
+{# force a new line at the end of the file #} diff --git a/resources/template/html/body/definition-list.html.twig b/resources/template/html/body/definition-list.html.twig index 0279042..d6dcd4e 100644 --- a/resources/template/html/body/definition-list.html.twig +++ b/resources/template/html/body/definition-list.html.twig @@ -16,7 +16,7 @@ {% if definitionListTerm.children %} {%- for definition in definitionListTerm.children -%}
{{ renderNode(definition) }}
- {%- endfor -%} + {%- endfor ~%} {% endif %} {% endfor %} diff --git a/resources/template/html/body/list/list.html.twig b/resources/template/html/body/list/list.html.twig index cedaa81..135646c 100644 --- a/resources/template/html/body/list/list.html.twig +++ b/resources/template/html/body/list/list.html.twig @@ -9,3 +9,4 @@ {{ renderNode(child) }} {% endfor %} +{# force a new line at the end of the file #} diff --git a/resources/template/html/body/menu/menu-item.html.twig b/resources/template/html/body/menu/menu-item.html.twig index 42829f8..9133dd2 100644 --- a/resources/template/html/body/menu/menu-item.html.twig +++ b/resources/template/html/body/menu/menu-item.html.twig @@ -1,16 +1,17 @@ -
  • {{ node.value.toString }} - {%- if node.children|length %} +
  • + {{ node.value.toString }} + {%~ if node.children|length %} - {%- endif -%} - {%- if node.sections|length %} + {%- endif ~%} + {%~ if node.sections|length %}
      {% for subsection in node.sections -%} {{ renderNode(subsection) }} {% endfor %}
    - {%- endif -%} + {%- endif ~%}
  • diff --git a/resources/template/html/body/menu/menu-level.html.twig b/resources/template/html/body/menu/menu-level.html.twig index d0c96bc..48c8ecb 100644 --- a/resources/template/html/body/menu/menu-level.html.twig +++ b/resources/template/html/body/menu/menu-level.html.twig @@ -3,3 +3,4 @@ {{ renderNode(entry) }} {% endfor %} +{# force a new line at the end of the file #} diff --git a/resources/template/html/body/paragraph.html.twig b/resources/template/html/body/paragraph.html.twig index 245fd6f..cb10d5f 100644 --- a/resources/template/html/body/paragraph.html.twig +++ b/resources/template/html/body/paragraph.html.twig @@ -1,7 +1,5 @@ -{% apply spaceless %} - {% set text = renderNode(node.value) %} +{% set text = renderNode(node.value) %} - {% if text %} - {{ text|raw }}

    - {% endif %} -{% endapply %} +{% if text %} + {{ text|raw }}

    +{% endif %} diff --git a/resources/template/html/body/quote.html.twig b/resources/template/html/body/quote.html.twig index 3f8942e..8c6752e 100644 --- a/resources/template/html/body/quote.html.twig +++ b/resources/template/html/body/quote.html.twig @@ -1 +1 @@ -{{ renderNode(node.value) }} +{{- renderNode(node.value) -}} diff --git a/resources/template/html/body/table/table-body.html.twig b/resources/template/html/body/table/table-body.html.twig index 769aca8..7817956 100644 --- a/resources/template/html/body/table/table-body.html.twig +++ b/resources/template/html/body/table/table-body.html.twig @@ -3,3 +3,4 @@ {% include "body/table/table-row.html.twig" %} {% endfor %} +{# force a new line at the end of the file #} diff --git a/resources/template/html/body/table/table-cell.html.twig b/resources/template/html/body/table/table-cell.html.twig index 373906a..5a9e602 100644 --- a/resources/template/html/body/table/table-cell.html.twig +++ b/resources/template/html/body/table/table-cell.html.twig @@ -2,3 +2,4 @@ {%- for child in column.children -%} {{- renderNode(child) -}} {%- else %} {% endfor %} +{# force a new line at the end of the file #} diff --git a/resources/template/html/body/table/table-row.html.twig b/resources/template/html/body/table/table-row.html.twig index 2040ca8..3fa5015 100644 --- a/resources/template/html/body/table/table-row.html.twig +++ b/resources/template/html/body/table/table-row.html.twig @@ -3,3 +3,4 @@ {% include "body/table/table-cell.html.twig" %} {% endfor %} +{# force a new line at the end of the file #} diff --git a/resources/template/html/inline/anchor.html.twig b/resources/template/html/inline/anchor.html.twig index 72ebecd..f95c8df 100644 --- a/resources/template/html/inline/anchor.html.twig +++ b/resources/template/html/inline/anchor.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} -{% endapply %} diff --git a/resources/template/html/inline/doc.html.twig b/resources/template/html/inline/doc.html.twig index 09b8975..04701ab 100644 --- a/resources/template/html/inline/doc.html.twig +++ b/resources/template/html/inline/doc.html.twig @@ -1 +1 @@ -{% apply spaceless %}{{ include('inline/link.html.twig') }}{% endapply %} +{{- include('inline/link.html.twig') -}} diff --git a/resources/template/html/inline/emphasis.html.twig b/resources/template/html/inline/emphasis.html.twig index 6dddff1..eca92aa 100644 --- a/resources/template/html/inline/emphasis.html.twig +++ b/resources/template/html/inline/emphasis.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{- node.value -}} -{% endapply %} diff --git a/resources/template/html/inline/image.html.twig b/resources/template/html/inline/image.html.twig index 0923a3e..85065a6 100644 --- a/resources/template/html/inline/image.html.twig +++ b/resources/template/html/inline/image.html.twig @@ -1,2 +1 @@ -{% apply spaceless %}{{- node.altText -}} -{% endapply %} +{{- node.altText -}} diff --git a/resources/template/html/inline/literal.html.twig b/resources/template/html/inline/literal.html.twig index 905abfb..d5b5dea 100644 --- a/resources/template/html/inline/literal.html.twig +++ b/resources/template/html/inline/literal.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{- node.value -}} -{% endapply %} diff --git a/resources/template/html/inline/nbsp.html.twig b/resources/template/html/inline/nbsp.html.twig index 5a7ba0e..c0c0ffe 100644 --- a/resources/template/html/inline/nbsp.html.twig +++ b/resources/template/html/inline/nbsp.html.twig @@ -1 +1 @@ -{% apply spaceless %} {% endapply %} +  diff --git a/resources/template/html/inline/newline.html.twig b/resources/template/html/inline/newline.html.twig index 399784e..0ca25d9 100644 --- a/resources/template/html/inline/newline.html.twig +++ b/resources/template/html/inline/newline.html.twig @@ -1 +1 @@ -{% apply spaceless %}
    {% endapply %} +
    diff --git a/resources/template/html/inline/ref.html.twig b/resources/template/html/inline/ref.html.twig index 09b8975..04701ab 100644 --- a/resources/template/html/inline/ref.html.twig +++ b/resources/template/html/inline/ref.html.twig @@ -1 +1 @@ -{% apply spaceless %}{{ include('inline/link.html.twig') }}{% endapply %} +{{- include('inline/link.html.twig') -}} diff --git a/resources/template/html/inline/strong.html.twig b/resources/template/html/inline/strong.html.twig index 1c543c1..936f48e 100644 --- a/resources/template/html/inline/strong.html.twig +++ b/resources/template/html/inline/strong.html.twig @@ -1,2 +1 @@ -{% apply spaceless %}{{- node.value -}} -{% endapply %} +{{- node.value -}} diff --git a/resources/template/html/inline/textroles/abbreviation.html.twig b/resources/template/html/inline/textroles/abbreviation.html.twig index 57b4cbf..0d900f0 100644 --- a/resources/template/html/inline/textroles/abbreviation.html.twig +++ b/resources/template/html/inline/textroles/abbreviation.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.term }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/aspect.html.twig b/resources/template/html/inline/textroles/aspect.html.twig index 7f6c3a0..ea89e80 100644 --- a/resources/template/html/inline/textroles/aspect.html.twig +++ b/resources/template/html/inline/textroles/aspect.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/br.html.twig b/resources/template/html/inline/textroles/br.html.twig index cf16fe3..1915ec1 100644 --- a/resources/template/html/inline/textroles/br.html.twig +++ b/resources/template/html/inline/textroles/br.html.twig @@ -1 +1 @@ -{%- apply spaceless %}
    {% endapply -%} +
    diff --git a/resources/template/html/inline/textroles/code.html.twig b/resources/template/html/inline/textroles/code.html.twig index f4fa91f..e9e2930 100644 --- a/resources/template/html/inline/textroles/code.html.twig +++ b/resources/template/html/inline/textroles/code.html.twig @@ -1,4 +1 @@ -{% apply spaceless %} {{ node.value }} - -{% endapply %} diff --git a/resources/template/html/inline/textroles/command.html.twig b/resources/template/html/inline/textroles/command.html.twig index 1ed7a24..98321e1 100644 --- a/resources/template/html/inline/textroles/command.html.twig +++ b/resources/template/html/inline/textroles/command.html.twig @@ -1,4 +1 @@ -{% apply spaceless %} {{ node.value }} - -{% endapply %} diff --git a/resources/template/html/inline/textroles/dfn.html.twig b/resources/template/html/inline/textroles/dfn.html.twig index fca41ef..18c575c 100644 --- a/resources/template/html/inline/textroles/dfn.html.twig +++ b/resources/template/html/inline/textroles/dfn.html.twig @@ -1,4 +1 @@ -{% apply spaceless %} {{ node.value }} - -{% endapply %} diff --git a/resources/template/html/inline/textroles/emphasis.html.twig b/resources/template/html/inline/textroles/emphasis.html.twig index d3c2d9b..c4e714e 100644 --- a/resources/template/html/inline/textroles/emphasis.html.twig +++ b/resources/template/html/inline/textroles/emphasis.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/file.html.twig b/resources/template/html/inline/textroles/file.html.twig index a2c7b73..61a234f 100644 --- a/resources/template/html/inline/textroles/file.html.twig +++ b/resources/template/html/inline/textroles/file.html.twig @@ -1,4 +1 @@ -{% apply spaceless %} {{ node.value }} - -{% endapply %} diff --git a/resources/template/html/inline/textroles/guilabel.html.twig b/resources/template/html/inline/textroles/guilabel.html.twig index 0416d4b..d71b792 100644 --- a/resources/template/html/inline/textroles/guilabel.html.twig +++ b/resources/template/html/inline/textroles/guilabel.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/kbd.html.twig b/resources/template/html/inline/textroles/kbd.html.twig index cacd6cf..d12f64a 100644 --- a/resources/template/html/inline/textroles/kbd.html.twig +++ b/resources/template/html/inline/textroles/kbd.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/literal.html.twig b/resources/template/html/inline/textroles/literal.html.twig index a3d14c7..e9e2930 100644 --- a/resources/template/html/inline/textroles/literal.html.twig +++ b/resources/template/html/inline/textroles/literal.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/mailheader.html.twig b/resources/template/html/inline/textroles/mailheader.html.twig index 713547c..b3eaf3b 100644 --- a/resources/template/html/inline/textroles/mailheader.html.twig +++ b/resources/template/html/inline/textroles/mailheader.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/math.html.twig b/resources/template/html/inline/textroles/math.html.twig index 3ee9551..9cfd6cd 100644 --- a/resources/template/html/inline/textroles/math.html.twig +++ b/resources/template/html/inline/textroles/math.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/span.html.twig b/resources/template/html/inline/textroles/span.html.twig index da3c85d..5a1008f 100644 --- a/resources/template/html/inline/textroles/span.html.twig +++ b/resources/template/html/inline/textroles/span.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{- node.value -}} -{% endapply %} diff --git a/resources/template/html/inline/textroles/strong.html.twig b/resources/template/html/inline/textroles/strong.html.twig index c79e824..6bfb54e 100644 --- a/resources/template/html/inline/textroles/strong.html.twig +++ b/resources/template/html/inline/textroles/strong.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/sub.html.twig b/resources/template/html/inline/textroles/sub.html.twig index d6887d9..5e03d44 100644 --- a/resources/template/html/inline/textroles/sub.html.twig +++ b/resources/template/html/inline/textroles/sub.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/subscript.html.twig b/resources/template/html/inline/textroles/subscript.html.twig index d6887d9..5e03d44 100644 --- a/resources/template/html/inline/textroles/subscript.html.twig +++ b/resources/template/html/inline/textroles/subscript.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/sup.html.twig b/resources/template/html/inline/textroles/sup.html.twig index 83ecfd2..feb157f 100644 --- a/resources/template/html/inline/textroles/sup.html.twig +++ b/resources/template/html/inline/textroles/sup.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/superscript.html.twig b/resources/template/html/inline/textroles/superscript.html.twig index 83ecfd2..feb157f 100644 --- a/resources/template/html/inline/textroles/superscript.html.twig +++ b/resources/template/html/inline/textroles/superscript.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/t.html.twig b/resources/template/html/inline/textroles/t.html.twig index 300329c..6616ef5 100644 --- a/resources/template/html/inline/textroles/t.html.twig +++ b/resources/template/html/inline/textroles/t.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/title-reference.html.twig b/resources/template/html/inline/textroles/title-reference.html.twig index 300329c..6616ef5 100644 --- a/resources/template/html/inline/textroles/title-reference.html.twig +++ b/resources/template/html/inline/textroles/title-reference.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/title.html.twig b/resources/template/html/inline/textroles/title.html.twig index 300329c..6616ef5 100644 --- a/resources/template/html/inline/textroles/title.html.twig +++ b/resources/template/html/inline/textroles/title.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/textroles/unknown.html.twig b/resources/template/html/inline/textroles/unknown.html.twig index bca55e7..c09d70d 100644 --- a/resources/template/html/inline/textroles/unknown.html.twig +++ b/resources/template/html/inline/textroles/unknown.html.twig @@ -1,3 +1 @@ -{% apply spaceless %} {{ node.value }} -{% endapply %} diff --git a/resources/template/html/inline/variable.html.twig b/resources/template/html/inline/variable.html.twig index 71cb90e..1f89324 100644 --- a/resources/template/html/inline/variable.html.twig +++ b/resources/template/html/inline/variable.html.twig @@ -1,2 +1 @@ -{% apply spaceless %}{{ renderNode(node.child) }} -{% endapply %} +{{- renderNode(node.child) -}} diff --git a/resources/template/tex/body/paragraph.tex.twig b/resources/template/tex/body/paragraph.tex.twig index 41bdff7..2ffeb89 100644 --- a/resources/template/tex/body/paragraph.tex.twig +++ b/resources/template/tex/body/paragraph.tex.twig @@ -1,8 +1,5 @@ -{% apply spaceless %} -{% set text = renderNode(node.value) %} +{%- set text = renderNode(node.value) -%} -{% if text|trim %} -{{ text|raw }} - -{% endif %} -{% endapply %} +{%- if text|trim %} + {{- text|raw -}} +{% endif -%} diff --git a/resources/template/tex/inline/literal.tex.twig b/resources/template/tex/inline/literal.tex.twig index 68547a4..78e810b 100644 --- a/resources/template/tex/inline/literal.tex.twig +++ b/resources/template/tex/inline/literal.tex.twig @@ -1,3 +1 @@ -{% apply spaceless %} \texttt{{ '{' }}{{- node.value -}}{{ '}' }} -{% endapply %} diff --git a/resources/template/tex/structure/header-title.tex.twig b/resources/template/tex/structure/header-title.tex.twig index af78ebf..7ed4679 100644 --- a/resources/template/tex/structure/header-title.tex.twig +++ b/resources/template/tex/structure/header-title.tex.twig @@ -1,6 +1,4 @@ -{%- apply spaceless -%} {%- set headingLevel = node.level -%} \{% if headingLevel == 1 %}section{% elseif headingLevel == 2 %}subsection{% elseif headingLevel == 3 %}subsubsection{% elseif headingLevel == 4 %}paragraph{% elseif headingLevel == 5 %}subparagraph{% elseif headingLevel == 6 %}subparagraph{% endif %}{ {{- renderNode(node.value) -}} } -{%- endapply -%} From a7e1f0d878cc154036b7de8147a7606089386295 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 17 Mar 2024 14:14:11 +0100 Subject: [PATCH 13/53] Add custom Twig loader to remove last new line of a file --- resources/config/guides.php | 4 ++- src/Twig/TrimFilesystemLoader.php | 41 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/Twig/TrimFilesystemLoader.php diff --git a/resources/config/guides.php b/resources/config/guides.php index a692518..e2f4fae 100644 --- a/resources/config/guides.php +++ b/resources/config/guides.php @@ -57,6 +57,7 @@ use phpDocumentor\Guides\Twig\EnvironmentBuilder; use phpDocumentor\Guides\Twig\GlobalMenuExtension; use phpDocumentor\Guides\Twig\Theme\ThemeManager; +use phpDocumentor\Guides\Twig\TrimFilesystemLoader; use phpDocumentor\Guides\Twig\TwigTemplateRenderer; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Reference; @@ -223,11 +224,12 @@ param('phpdoc.guides.base_template_paths'), ) - ->set(FilesystemLoader::class) + ->set(TrimFilesystemLoader::class) ->arg( '$paths', param('phpdoc.guides.base_template_paths'), ) + ->alias(FilesystemLoader::class, TrimFilesystemLoader::class) ->set(LoadSettingsFromComposer::class) ->tag('event_listener', ['event' => PostProjectNodeCreated::class]) diff --git a/src/Twig/TrimFilesystemLoader.php b/src/Twig/TrimFilesystemLoader.php new file mode 100644 index 0000000..f0e93b1 --- /dev/null +++ b/src/Twig/TrimFilesystemLoader.php @@ -0,0 +1,41 @@ +getCode()) ?? $source->getCode(), + $source->getName(), + $source->getPath(), + ); + } +} From 5688947000490218c602ec0862d2becc54560820 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 17 Mar 2024 14:14:31 +0100 Subject: [PATCH 14/53] Fix Tex template name (cherry picked from commit 1d4dce0fdb8367150107c7dfafec99bdc6f0d393) --- resources/template/tex/inline/textroles/generic.tex.twig | 2 +- .../tex/inline/textroles/{unkown.tex.twig => unknown.tex.twig} | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) rename resources/template/tex/inline/textroles/{unkown.tex.twig => unknown.tex.twig} (54%) diff --git a/resources/template/tex/inline/textroles/generic.tex.twig b/resources/template/tex/inline/textroles/generic.tex.twig index a0d7329..709a512 100644 --- a/resources/template/tex/inline/textroles/generic.tex.twig +++ b/resources/template/tex/inline/textroles/generic.tex.twig @@ -1,4 +1,4 @@ {%- include [ ('inline/textroles/' ~ node.type ~ '.tex.twig'), - 'inline/textroles/unkown.tex.twig' + 'inline/textroles/unknown.tex.twig' ] -%} diff --git a/resources/template/tex/inline/textroles/unkown.tex.twig b/resources/template/tex/inline/textroles/unknown.tex.twig similarity index 54% rename from resources/template/tex/inline/textroles/unkown.tex.twig rename to resources/template/tex/inline/textroles/unknown.tex.twig index 68547a4..78e810b 100644 --- a/resources/template/tex/inline/textroles/unkown.tex.twig +++ b/resources/template/tex/inline/textroles/unknown.tex.twig @@ -1,3 +1 @@ -{% apply spaceless %} \texttt{{ '{' }}{{- node.value -}}{{ '}' }} -{% endapply %} From 96200cd797971e737d71ee6eff5f2982ab2f8422 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 16 Mar 2024 11:59:40 +0100 Subject: [PATCH 15/53] [BUGFIX] Make links to special objects prefixable If we have both an anchor like '.. _demo:' and a confval of the same name we would get duplicate id's and the links to those two elements could not be distinguished. Sphinx solves this by prefixing all confval links with "confval-". Therefore, I introduce the possibility of prefixing links to distinguished linkable objects. related https://github.com/phpDocumentor/guides/issues/924 releases: main, 1.0 (cherry picked from commit 3ef4ee08b2049e485d6fb30beb85ee1cbbeb883d) --- .../CollectLinkTargetsTransformer.php | 7 +++++++ src/Meta/InternalTarget.php | 6 ++++++ src/Nodes/Inline/ReferenceNode.php | 8 +++++++- src/Nodes/PrefixedLinkTargetNode.php | 19 +++++++++++++++++++ .../AnchorReferenceResolver.php | 2 +- 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/Nodes/PrefixedLinkTargetNode.php diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index 86a5187..1b48909 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -21,6 +21,7 @@ use phpDocumentor\Guides\Nodes\LinkTargetNode; use phpDocumentor\Guides\Nodes\MultipleLinkTargetsNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\Nodes\PrefixedLinkTargetNode; use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer; use Psr\Log\LoggerInterface; @@ -97,6 +98,11 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node $currentDocument = $this->documentStack->top(); Assert::notNull($currentDocument); $anchor = $this->anchorReducer->reduceAnchor($node->getId()); + $prefix = ''; + if ($node instanceof PrefixedLinkTargetNode) { + $prefix = $node->getPrefix(); + } + $this->addLinkTargetToProject( $compilerContext, new InternalTarget( @@ -104,6 +110,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node $anchor, $node->getLinkText(), $node->getLinkType(), + $prefix, ), ); if ($node instanceof MultipleLinkTargetsNode) { diff --git a/src/Meta/InternalTarget.php b/src/Meta/InternalTarget.php index 7048151..bc238a9 100644 --- a/src/Meta/InternalTarget.php +++ b/src/Meta/InternalTarget.php @@ -24,6 +24,7 @@ public function __construct( protected string $anchorName, private readonly string|null $title = null, private readonly string $linkType = SectionNode::STD_LABEL, + private readonly string $prefix = '', ) { } @@ -63,4 +64,9 @@ public function getLinkType(): string { return $this->linkType; } + + public function getPrefix(): string + { + return $this->prefix; + } } diff --git a/src/Nodes/Inline/ReferenceNode.php b/src/Nodes/Inline/ReferenceNode.php index 1dcf3d3..edde092 100644 --- a/src/Nodes/Inline/ReferenceNode.php +++ b/src/Nodes/Inline/ReferenceNode.php @@ -37,6 +37,7 @@ public function __construct( string $value = '', private readonly string $interlinkDomain = '', private readonly string $linkType = SectionNode::STD_LABEL, + private readonly string $prefix = '', ) { parent::__construct(self::TYPE, $targetReference, $value); } @@ -62,6 +63,11 @@ public function getDebugInformation(): array public function getInterlinkGroup(): string { - return 'std:label'; + return $this->linkType; + } + + public function getPrefix(): string + { + return $this->prefix; } } diff --git a/src/Nodes/PrefixedLinkTargetNode.php b/src/Nodes/PrefixedLinkTargetNode.php new file mode 100644 index 0000000..129aee1 --- /dev/null +++ b/src/Nodes/PrefixedLinkTargetNode.php @@ -0,0 +1,19 @@ +setUrl($this->urlGenerator->generateCanonicalOutputUrl($renderContext, $target->getDocumentPath(), $target->getAnchor())); + $node->setUrl($this->urlGenerator->generateCanonicalOutputUrl($renderContext, $target->getDocumentPath(), $target->getPrefix() . $target->getAnchor())); if ($node->getValue() === '') { $node->setValue($target->getTitle() ?? ''); } From fee2fd3d771c0d239eccf10432a6dc0ec3fbf321 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 23 Mar 2024 05:39:36 +0100 Subject: [PATCH 16/53] [FEATURE] Introduce noindex option for confvals https://sphinx-toolbox.readthedocs.io/en/stable/extensions/confval.html#directive-option-confval-noindex releases: main, 1.0 (cherry picked from commit ff4ce84cba1cef0d3afeae1c02ba01ea689a7b05) --- .../CollectLinkTargetsTransformer.php | 5 ++++ src/Nodes/OptionalLinkTargetsNode.php | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/Nodes/OptionalLinkTargetsNode.php diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index 1b48909..86781d4 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -21,6 +21,7 @@ use phpDocumentor\Guides\Nodes\LinkTargetNode; use phpDocumentor\Guides\Nodes\MultipleLinkTargetsNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\Nodes\OptionalLinkTargetsNode; use phpDocumentor\Guides\Nodes\PrefixedLinkTargetNode; use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer; @@ -95,6 +96,10 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node } if ($node instanceof LinkTargetNode) { + if ($node instanceof OptionalLinkTargetsNode && $node->isNoindex()) { + return $node; + } + $currentDocument = $this->documentStack->top(); Assert::notNull($currentDocument); $anchor = $this->anchorReducer->reduceAnchor($node->getId()); diff --git a/src/Nodes/OptionalLinkTargetsNode.php b/src/Nodes/OptionalLinkTargetsNode.php new file mode 100644 index 0000000..46453d5 --- /dev/null +++ b/src/Nodes/OptionalLinkTargetsNode.php @@ -0,0 +1,26 @@ + Date: Sat, 23 Mar 2024 19:43:42 +0100 Subject: [PATCH 17/53] [FEATURE] Add local option to contents directive resolves #901 (cherry picked from commit 4db1f1c216726b66aea9ed94773d1810b55d50ce) --- .../ContentsMenuEntryNodeTransformer.php | 38 ++++++++++++++++--- src/Nodes/DocumentTree/DocumentEntryNode.php | 19 ++++++++++ src/Nodes/DocumentTree/SectionEntryNode.php | 19 ++++++++++ src/Nodes/Menu/ContentMenuNode.php | 15 ++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php index 07953f7..2ff0d4e 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php @@ -14,11 +14,13 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Nodes\DocumentTree\SectionEntryNode; use phpDocumentor\Guides\Nodes\Menu\ContentMenuNode; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuNode; use phpDocumentor\Guides\Nodes\Menu\SectionMenuEntryNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\Nodes\SectionNode; use function assert; @@ -45,12 +47,36 @@ protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNo assert($entryNode instanceof SectionMenuEntryNode); $depth = (int) $currentMenu->getOption('depth', self::DEFAULT_MAX_LEVELS - 1) + 1; $documentEntry = $compilerContext->getDocumentNode()->getDocumentEntry(); - $newEntryNode = new SectionMenuEntryNode( - $documentEntry->getFile(), - $entryNode->getValue() ?? $documentEntry->getTitle(), - 1, - ); - $this->addSubSectionsToMenuEntries($documentEntry, $newEntryNode, $depth); + if ($currentMenu->isLocal()) { + $sectionNode = $compilerContext->getShadowTree()->getParent()?->getParent()?->getNode(); + if (!$sectionNode instanceof SectionNode) { + $this->logger->error('Section of contents directive not found. ', $compilerContext->getLoggerInformation()); + + return []; + } + + $sectionEntry = $documentEntry->findSectionEntry($sectionNode); + if (!$sectionEntry instanceof SectionEntryNode) { + $this->logger->error('Section of contents directive not found. ', $compilerContext->getLoggerInformation()); + + return []; + } + + $newEntryNode = new SectionMenuEntryNode( + $documentEntry->getFile(), + $entryNode->getValue() ?? $sectionEntry->getTitle(), + 1, + $sectionEntry->getId(), + ); + $this->addSubSections($newEntryNode, $sectionEntry, $documentEntry, 1, $depth); + } else { + $newEntryNode = new SectionMenuEntryNode( + $documentEntry->getFile(), + $entryNode->getValue() ?? $documentEntry->getTitle(), + 1, + ); + $this->addSubSectionsToMenuEntries($documentEntry, $newEntryNode, $depth); + } return $newEntryNode->getSections(); } diff --git a/src/Nodes/DocumentTree/DocumentEntryNode.php b/src/Nodes/DocumentTree/DocumentEntryNode.php index 0bb498b..0d1b188 100644 --- a/src/Nodes/DocumentTree/DocumentEntryNode.php +++ b/src/Nodes/DocumentTree/DocumentEntryNode.php @@ -13,6 +13,7 @@ namespace phpDocumentor\Guides\Nodes\DocumentTree; +use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\Nodes\TitleNode; use function array_filter; @@ -81,4 +82,22 @@ public function isRoot(): bool { return $this->isRoot; } + + public function findSectionEntry(SectionNode $sectionNode): SectionEntryNode|null + { + foreach ($this->sections as $sectionEntryNode) { + if ($sectionNode->getId() === $sectionEntryNode->getId()) { + return $sectionEntryNode; + } + } + + foreach ($this->sections as $sectionEntryNode) { + $subsection = $sectionEntryNode->findSectionEntry($sectionNode); + if ($subsection !== null) { + return $subsection; + } + } + + return null; + } } diff --git a/src/Nodes/DocumentTree/SectionEntryNode.php b/src/Nodes/DocumentTree/SectionEntryNode.php index 7876c05..5de4a0b 100644 --- a/src/Nodes/DocumentTree/SectionEntryNode.php +++ b/src/Nodes/DocumentTree/SectionEntryNode.php @@ -14,6 +14,7 @@ namespace phpDocumentor\Guides\Nodes\DocumentTree; use phpDocumentor\Guides\Nodes\DocumentNode; +use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\Nodes\TitleNode; /** @extends EntryNode */ @@ -46,4 +47,22 @@ public function getChildren(): array { return $this->children; } + + public function findSectionEntry(SectionNode $sectionNode): SectionEntryNode|null + { + foreach ($this->children as $sectionEntryNode) { + if ($sectionNode->getId() === $sectionEntryNode->getId()) { + return $sectionEntryNode; + } + } + + foreach ($this->children as $sectionEntryNode) { + $subsection = $sectionEntryNode->findSectionEntry($sectionNode); + if ($subsection !== null) { + return $subsection; + } + } + + return null; + } } diff --git a/src/Nodes/Menu/ContentMenuNode.php b/src/Nodes/Menu/ContentMenuNode.php index 1c6cfbf..e5948a5 100644 --- a/src/Nodes/Menu/ContentMenuNode.php +++ b/src/Nodes/Menu/ContentMenuNode.php @@ -18,6 +18,8 @@ /** @link https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#table-of-contents */ final class ContentMenuNode extends MenuNode { + private bool $local = false; + public function getDepth(): int { if ($this->hasOption('depth') && is_scalar($this->getOption('depth'))) { @@ -31,4 +33,17 @@ public function isPageLevelOnly(): bool { return false; } + + public function isLocal(): bool + { + return $this->local; + } + + public function withLocal(bool $local): ContentMenuNode + { + $that = clone $this; + $that->local = $local; + + return $that; + } } From 07ab2ebe51c94f09dc67b1598fc1b1b30771cced Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 12 Apr 2024 12:09:02 +0200 Subject: [PATCH 18/53] Introduce CompilerContextInterface The Compiler context is a very important class in our compiler, however extending projects may want to extend the information caried around with the context to add extra information for specific use cases in custom compiler passes. To make this possible I introduced a new interface to be able to wrap the compilercontext and extend the functionality. Changing the interfaces would be a backward compatibility break so we need to introduce this step by step. --- src/Compiler/CompilerContext.php | 13 ++++++- src/Compiler/CompilerContextInterface.php | 37 +++++++++++++++++++ .../CitationInlineNodeTransformer.php | 6 +-- .../CitationTargetTransformer.php | 6 +-- .../NodeTransformers/ClassNodeTransformer.php | 6 +-- .../CollectLinkTargetsTransformer.php | 8 ++-- .../DocumentBlockNodeTransformer.php | 6 +-- .../DocumentEntryRegistrationTransformer.php | 6 +-- .../FootNodeNamedTransformer.php | 6 +-- .../FootNodeNumberedTransformer.php | 6 +-- .../FootnoteInlineNodeTransformer.php | 6 +-- .../NodeTransformers/ListNodeTransformer.php | 6 +-- .../AbstractMenuEntryNodeTransformer.php | 8 ++-- .../ContentsMenuEntryNodeTransformer.php | 4 +- .../ExternalMenuEntryNodeTransformer.php | 4 +- .../GlobMenuEntryNodeTransformer.php | 4 +- .../InternalMenuEntryNodeTransformer.php | 4 +- .../MenuEntryManagement.php | 4 +- .../SubInternalMenuEntryNodeTransformer.php | 6 +-- .../TocNodeTransformer.php | 6 +-- .../MoveAnchorTransformer.php | 6 +-- .../SectionCreationTransformer.php | 6 +-- .../SectionEntryRegistrationTransformer.php | 6 +-- .../NodeTransformers/TransformerPass.php | 1 + .../VariableInlineNodeTransformer.php | 6 +-- src/Compiler/Passes/GlobalMenuPass.php | 10 ++--- .../Passes/ImplicitHyperlinkTargetPass.php | 4 +- src/Compiler/Passes/ToctreeValidationPass.php | 4 +- 28 files changed, 122 insertions(+), 73 deletions(-) create mode 100644 src/Compiler/CompilerContextInterface.php diff --git a/src/Compiler/CompilerContext.php b/src/Compiler/CompilerContext.php index e753172..3b8d794 100644 --- a/src/Compiler/CompilerContext.php +++ b/src/Compiler/CompilerContext.php @@ -19,7 +19,18 @@ use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\Nodes\ProjectNode; -final class CompilerContext +/** + * Context class used in compiler passes to store the state of the nodes. + * + * The {@see Compiler} is making changes to the nodes in a {@see DocumentNode} as the nodes are immutable cannot + * do this directly. This class helps to modify the nodes in the {@see DocumentNode} by creating a shadow tree. + * + * The class is final and should not be extended, if you need to provide more information to the compiler pass + * you can use the {@see CompilerContextInterface} and decorate this class. + * + * @final + */ +class CompilerContext implements CompilerContextInterface { /** @var TreeNode */ private TreeNode $shadowTree; diff --git a/src/Compiler/CompilerContextInterface.php b/src/Compiler/CompilerContextInterface.php new file mode 100644 index 0000000..0168da4 --- /dev/null +++ b/src/Compiler/CompilerContextInterface.php @@ -0,0 +1,37 @@ + $shadowTree */ + public function withShadowTree(TreeNode $shadowTree): self; + + /** @return TreeNode */ + public function getShadowTree(): TreeNode; + + /** @return array */ + public function getLoggerInformation(): array; +} diff --git a/src/Compiler/NodeTransformers/CitationInlineNodeTransformer.php b/src/Compiler/NodeTransformers/CitationInlineNodeTransformer.php index d41ccf3..6108357 100644 --- a/src/Compiler/NodeTransformers/CitationInlineNodeTransformer.php +++ b/src/Compiler/NodeTransformers/CitationInlineNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\Inline\CitationInlineNode; use phpDocumentor\Guides\Nodes\Node; @@ -21,7 +21,7 @@ /** @implements NodeTransformer */ final class CitationInlineNodeTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof CitationInlineNode) { $internalTarget = $compilerContext->getProjectNode()->getCitationTarget($node->getName()); @@ -31,7 +31,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; } diff --git a/src/Compiler/NodeTransformers/CitationTargetTransformer.php b/src/Compiler/NodeTransformers/CitationTargetTransformer.php index 9262167..1fa6033 100644 --- a/src/Compiler/NodeTransformers/CitationTargetTransformer.php +++ b/src/Compiler/NodeTransformers/CitationTargetTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Meta\CitationTarget; use phpDocumentor\Guides\Nodes\CitationNode; @@ -22,7 +22,7 @@ /** @implements NodeTransformer */ final class CitationTargetTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof CitationNode) { $compilerContext->getProjectNode()->addCitationTarget( @@ -37,7 +37,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; } diff --git a/src/Compiler/NodeTransformers/ClassNodeTransformer.php b/src/Compiler/NodeTransformers/ClassNodeTransformer.php index 22fb83f..0e0be95 100644 --- a/src/Compiler/NodeTransformers/ClassNodeTransformer.php +++ b/src/Compiler/NodeTransformers/ClassNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\ClassNode; use phpDocumentor\Guides\Nodes\DocumentNode; @@ -32,7 +32,7 @@ final class ClassNodeTransformer implements NodeTransformer /** @var string[] */ private array $classes = []; - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof DocumentNode) { // unset classes when entering the next document @@ -52,7 +52,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if ($node instanceof ClassNode) { //Remove the class node from the tree. diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index 86781d4..28e75f6 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Meta\InternalTarget; use phpDocumentor\Guides\Nodes\AnchorNode; @@ -49,7 +49,7 @@ public function __construct( $this->documentStack = new SplStack(); } - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof DocumentNode) { $this->documentStack->push($node); @@ -137,7 +137,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if ($node instanceof DocumentNode) { $this->documentStack->pop(); @@ -157,7 +157,7 @@ public function getPriority(): int return 5000; } - private function addLinkTargetToProject(CompilerContext $compilerContext, InternalTarget $internalTarget): void + private function addLinkTargetToProject(CompilerContextInterface $compilerContext, InternalTarget $internalTarget): void { if ($compilerContext->getProjectNode()->hasInternalTarget($internalTarget->getAnchor(), $internalTarget->getLinkType())) { $otherLink = $compilerContext->getProjectNode()->getInternalTarget($internalTarget->getAnchor(), $internalTarget->getLinkType()); diff --git a/src/Compiler/NodeTransformers/DocumentBlockNodeTransformer.php b/src/Compiler/NodeTransformers/DocumentBlockNodeTransformer.php index 4936ec8..01b88c7 100644 --- a/src/Compiler/NodeTransformers/DocumentBlockNodeTransformer.php +++ b/src/Compiler/NodeTransformers/DocumentBlockNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\DocumentBlockNode; use phpDocumentor\Guides\Nodes\Menu\TocNode; @@ -27,12 +27,12 @@ */ final class DocumentBlockNodeTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if ($node instanceof DocumentBlockNode) { $children = []; diff --git a/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php b/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php index 1a01611..0edd5c5 100644 --- a/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php +++ b/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; @@ -29,12 +29,12 @@ public function __construct( ) { } - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if (!$node instanceof DocumentNode) { return $node; diff --git a/src/Compiler/NodeTransformers/FootNodeNamedTransformer.php b/src/Compiler/NodeTransformers/FootNodeNamedTransformer.php index 43b96a3..de29f9e 100644 --- a/src/Compiler/NodeTransformers/FootNodeNamedTransformer.php +++ b/src/Compiler/NodeTransformers/FootNodeNamedTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Meta\FootnoteTarget; use phpDocumentor\Guides\Nodes\FootnoteNode; @@ -22,7 +22,7 @@ /** @implements NodeTransformer */ final class FootNodeNamedTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof FootnoteNode && $this->supports($node)) { $number = $compilerContext->getDocumentNode()->addFootnoteTarget(new FootnoteTarget( @@ -37,7 +37,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; } diff --git a/src/Compiler/NodeTransformers/FootNodeNumberedTransformer.php b/src/Compiler/NodeTransformers/FootNodeNumberedTransformer.php index ac7903c..29cbaf2 100644 --- a/src/Compiler/NodeTransformers/FootNodeNumberedTransformer.php +++ b/src/Compiler/NodeTransformers/FootNodeNumberedTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Meta\FootnoteTarget; use phpDocumentor\Guides\Nodes\FootnoteNode; @@ -22,7 +22,7 @@ /** @implements NodeTransformer */ final class FootNodeNumberedTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof FootnoteNode && $this->supports($node)) { $compilerContext->getDocumentNode()->addFootnoteTarget(new FootnoteTarget( @@ -36,7 +36,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; } diff --git a/src/Compiler/NodeTransformers/FootnoteInlineNodeTransformer.php b/src/Compiler/NodeTransformers/FootnoteInlineNodeTransformer.php index 844726b..03d3500 100644 --- a/src/Compiler/NodeTransformers/FootnoteInlineNodeTransformer.php +++ b/src/Compiler/NodeTransformers/FootnoteInlineNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\Inline\FootnoteInlineNode; use phpDocumentor\Guides\Nodes\Node; @@ -21,7 +21,7 @@ /** @implements NodeTransformer */ final class FootnoteInlineNodeTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof FootnoteInlineNode) { if ($node->getNumber() > 0) { @@ -38,7 +38,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; } diff --git a/src/Compiler/NodeTransformers/ListNodeTransformer.php b/src/Compiler/NodeTransformers/ListNodeTransformer.php index d2f6dfc..a4dbd86 100644 --- a/src/Compiler/NodeTransformers/ListNodeTransformer.php +++ b/src/Compiler/NodeTransformers/ListNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\ListItemNode; use phpDocumentor\Guides\Nodes\ListNode; @@ -30,12 +30,12 @@ public function __construct( ) { } - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { assert($node instanceof ListNode); foreach ($node->getChildren() as $listItemNode) { diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/AbstractMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/AbstractMenuEntryNodeTransformer.php index 6cf4166..df8ea46 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/AbstractMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/AbstractMenuEntryNodeTransformer.php @@ -14,7 +14,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; use Exception; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuNode; @@ -32,13 +32,13 @@ public function __construct( ) { } - final public function enterNode(Node $node, CompilerContext $compilerContext): MenuEntryNode + final public function enterNode(Node $node, CompilerContextInterface $compilerContext): MenuEntryNode { return $node; } /** @param MenuEntryNode $node */ - final public function leaveNode(Node $node, CompilerContext $compilerContext): MenuEntryNode|null + final public function leaveNode(Node $node, CompilerContextInterface $compilerContext): MenuEntryNode|null { assert($node instanceof MenuEntryNode); $currentMenuShaddow = $compilerContext->getShadowTree()->getParent(); @@ -70,5 +70,5 @@ final public function leaveNode(Node $node, CompilerContext $compilerContext): M } /** @return list */ - abstract protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array; + abstract protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContextInterface $compilerContext): array; } diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php index 2ff0d4e..7883f83 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/ContentsMenuEntryNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Nodes\DocumentTree\SectionEntryNode; use phpDocumentor\Guides\Nodes\Menu\ContentMenuNode; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; @@ -38,7 +38,7 @@ public function supports(Node $node): bool } /** @return list */ - protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array + protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContextInterface $compilerContext): array { if (!$currentMenu instanceof ContentMenuNode) { return [$entryNode]; diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php index da464c2..2efd0b1 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/ExternalMenuEntryNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode; use phpDocumentor\Guides\Nodes\Menu\ExternalMenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; @@ -42,7 +42,7 @@ public function supports(Node $node): bool } /** @return list */ - protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array + protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContextInterface $compilerContext): array { assert($entryNode instanceof ExternalMenuEntryNode); diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php index b9c4e40..28dae6e 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Nodes\Menu\GlobMenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\InternalMenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; @@ -38,7 +38,7 @@ final class GlobMenuEntryNodeTransformer extends AbstractMenuEntryNodeTransforme private const DEFAULT_MAX_LEVELS = 10; /** @return list */ - protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array + protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContextInterface $compilerContext): array { assert($entryNode instanceof GlobMenuEntryNode); $maxDepth = (int) $currentMenu->getOption('maxdepth', self::DEFAULT_MAX_LEVELS); diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php index 82245cc..f4ec497 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Nodes\Menu\InternalMenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuNode; @@ -49,7 +49,7 @@ public function supports(Node $node): bool } /** @return list */ - protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array + protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContextInterface $compilerContext): array { assert($entryNode instanceof InternalMenuEntryNode); $documentEntries = $compilerContext->getProjectNode()->getAllDocumentEntries(); diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php index a1ee0b2..73b918e 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/MenuEntryManagement.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode; @@ -25,7 +25,7 @@ trait MenuEntryManagement /** @param array $entryNodes */ private function attachDocumentEntriesToParents( array $entryNodes, - CompilerContext $compilerContext, + CompilerContextInterface $compilerContext, string $currentPath, ): void { foreach ($entryNodes as $entryNode) { diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php index e362705..9aa7776 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Exception\DocumentEntryNotFound; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; use phpDocumentor\Guides\Nodes\DocumentTree\ExternalEntryNode; @@ -41,7 +41,7 @@ public function supports(Node $node): bool } /** @return list */ - protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContext $compilerContext): array + protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNode, CompilerContextInterface $compilerContext): array { assert($entryNode instanceof InternalMenuEntryNode); $maxDepth = (int) $currentMenu->getOption('maxdepth', self::DEFAULT_MAX_LEVELS); @@ -67,7 +67,7 @@ public function getPriority(): int private function addSubEntries( MenuNode $currentMenu, - CompilerContext $compilerContext, + CompilerContextInterface $compilerContext, InternalMenuEntryNode $sectionMenuEntry, DocumentEntryNode $documentEntry, int $currentLevel, diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeTransformer.php index 590c7c7..f69643e 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\Menu\TocNode; use phpDocumentor\Guides\Nodes\Node; @@ -23,7 +23,7 @@ /** @implements NodeTransformer */ final class TocNodeTransformer implements NodeTransformer { - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { assert($node instanceof TocNode); $compilerContext->getDocumentNode()->addTocNode($node); @@ -31,7 +31,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { return $node; } diff --git a/src/Compiler/NodeTransformers/MoveAnchorTransformer.php b/src/Compiler/NodeTransformers/MoveAnchorTransformer.php index 6c3158f..9b6ef3c 100644 --- a/src/Compiler/NodeTransformers/MoveAnchorTransformer.php +++ b/src/Compiler/NodeTransformers/MoveAnchorTransformer.php @@ -15,7 +15,7 @@ use ArrayIterator; use LogicException; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Compiler\ShadowTree\TreeNode; use phpDocumentor\Guides\Nodes\AnchorNode; @@ -34,12 +34,12 @@ public function __construct() $this->seen = new WeakMap(); } - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { //When exists in seen, it means that the node has already been processed. Ignore it. if (isset($this->seen[$node])) { diff --git a/src/Compiler/NodeTransformers/SectionCreationTransformer.php b/src/Compiler/NodeTransformers/SectionCreationTransformer.php index 00bcf05..dbda625 100644 --- a/src/Compiler/NodeTransformers/SectionCreationTransformer.php +++ b/src/Compiler/NodeTransformers/SectionCreationTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\Node; @@ -32,7 +32,7 @@ final class SectionCreationTransformer implements NodeTransformer /** @var SectionNode[] $sectionStack */ private array $sectionStack = []; - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if (!$compilerContext->getShadowTree()->getParent()?->getNode() instanceof DocumentNode) { return $node; @@ -48,7 +48,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if (!$compilerContext->getShadowTree()->getParent()?->getNode() instanceof DocumentNode) { return $node; diff --git a/src/Compiler/NodeTransformers/SectionEntryRegistrationTransformer.php b/src/Compiler/NodeTransformers/SectionEntryRegistrationTransformer.php index 136964e..de7e2cc 100644 --- a/src/Compiler/NodeTransformers/SectionEntryRegistrationTransformer.php +++ b/src/Compiler/NodeTransformers/SectionEntryRegistrationTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\DocumentTree\SectionEntryNode; use phpDocumentor\Guides\Nodes\Node; @@ -30,7 +30,7 @@ final class SectionEntryRegistrationTransformer implements NodeTransformer /** @var SectionEntryNode[] $sectionStack */ private array $sectionStack = []; - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if (!$node instanceof SectionNode) { return $node; @@ -50,7 +50,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if (!$node instanceof SectionNode) { return $node; diff --git a/src/Compiler/NodeTransformers/TransformerPass.php b/src/Compiler/NodeTransformers/TransformerPass.php index 4ede37e..e2db86e 100644 --- a/src/Compiler/NodeTransformers/TransformerPass.php +++ b/src/Compiler/NodeTransformers/TransformerPass.php @@ -14,6 +14,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\CompilerPass; use phpDocumentor\Guides\Compiler\DocumentNodeTraverser; use phpDocumentor\Guides\Nodes\DocumentNode; diff --git a/src/Compiler/NodeTransformers/VariableInlineNodeTransformer.php b/src/Compiler/NodeTransformers/VariableInlineNodeTransformer.php index 7d53eac..7d2a0c6 100644 --- a/src/Compiler/NodeTransformers/VariableInlineNodeTransformer.php +++ b/src/Compiler/NodeTransformers/VariableInlineNodeTransformer.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; use phpDocumentor\Guides\Nodes\Inline\PlainTextInlineNode; use phpDocumentor\Guides\Nodes\Inline\VariableInlineNode; @@ -33,12 +33,12 @@ public function __construct( ) { } - public function enterNode(Node $node, CompilerContext $compilerContext): Node + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { return $node; } - public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null { if (!$node instanceof VariableInlineNode) { return $node; diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index a74163a..edc97ba 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\Passes; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\CompilerPass; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; @@ -50,7 +50,7 @@ public function getPriority(): int * * @return DocumentNode[] */ - public function run(array $documents, CompilerContext $compilerContext): array + public function run(array $documents, CompilerContextInterface $compilerContext): array { $projectNode = $compilerContext->getProjectNode(); try { @@ -83,7 +83,7 @@ public function run(array $documents, CompilerContext $compilerContext): array return $documents; } - private function getNavMenuNodefromTocNode(CompilerContext $compilerContext, TocNode $tocNode, string|null $menuType = null): NavMenuNode + private function getNavMenuNodefromTocNode(CompilerContextInterface $compilerContext, TocNode $tocNode, string|null $menuType = null): NavMenuNode { $self = $this; $menuEntries = array_map(static function (MenuEntryNode $tocEntry) use ($compilerContext, $self) { @@ -104,7 +104,7 @@ private function getNavMenuNodefromTocNode(CompilerContext $compilerContext, Toc return $node; } - private function getMenuEntryWithChildren(CompilerContext $compilerContext, MenuEntryNode $menuEntry): MenuEntryNode + private function getMenuEntryWithChildren(CompilerContextInterface $compilerContext, MenuEntryNode $menuEntry): MenuEntryNode { if (!$menuEntry instanceof InternalMenuEntryNode) { return $menuEntry; @@ -121,7 +121,7 @@ private function getMenuEntryWithChildren(CompilerContext $compilerContext, Menu /** @param EntryNode|ExternalEntryNode $entryNode */ private function addSubEntries( - CompilerContext $compilerContext, + CompilerContextInterface $compilerContext, MenuEntryNode $sectionMenuEntry, EntryNode $entryNode, int $currentLevel, diff --git a/src/Compiler/Passes/ImplicitHyperlinkTargetPass.php b/src/Compiler/Passes/ImplicitHyperlinkTargetPass.php index 260bab1..6c7fcbd 100644 --- a/src/Compiler/Passes/ImplicitHyperlinkTargetPass.php +++ b/src/Compiler/Passes/ImplicitHyperlinkTargetPass.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\Passes; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\CompilerPass; use phpDocumentor\Guides\Nodes\AnchorNode; use phpDocumentor\Guides\Nodes\CompoundNode; @@ -43,7 +43,7 @@ public function getPriority(): int } /** {@inheritDoc} */ - public function run(array $documents, CompilerContext $compilerContext): array + public function run(array $documents, CompilerContextInterface $compilerContext): array { return array_map(function (DocumentNode $document): DocumentNode { // implicit references must not conflict with explicit ones diff --git a/src/Compiler/Passes/ToctreeValidationPass.php b/src/Compiler/Passes/ToctreeValidationPass.php index f4bdf88..208f40e 100644 --- a/src/Compiler/Passes/ToctreeValidationPass.php +++ b/src/Compiler/Passes/ToctreeValidationPass.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Compiler\Passes; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\CompilerPass; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; @@ -37,7 +37,7 @@ public function getPriority(): int * * @return DocumentNode[] */ - public function run(array $documents, CompilerContext $compilerContext): array + public function run(array $documents, CompilerContextInterface $compilerContext): array { $projectNode = $compilerContext->getProjectNode(); From a0f19906cc2cf3a61fafdd91da1aac558cb7c310 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 12 Apr 2024 13:36:34 +0200 Subject: [PATCH 19/53] Add deprecation trigger for context extension --- composer.json | 1 + src/Compiler/CompilerContext.php | 10 ++++++ .../NodeTransformers/TransformerPass.php | 1 - tests/unit/Compiler/CompilerContextTest.php | 36 +++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/unit/Compiler/CompilerContextTest.php diff --git a/composer.json b/composer.json index 33e00ac..ee566c1 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "php": "^8.1", "ext-json": "*", "ext-zlib": "*", + "doctrine/deprecations": "^1.0", "league/flysystem": "^1.1", "league/tactician": "^1.1", "league/uri": "^6.5 || ^7.0", diff --git a/src/Compiler/CompilerContext.php b/src/Compiler/CompilerContext.php index 3b8d794..45a1c40 100644 --- a/src/Compiler/CompilerContext.php +++ b/src/Compiler/CompilerContext.php @@ -13,6 +13,7 @@ namespace phpDocumentor\Guides\Compiler; +use Doctrine\Deprecations\Deprecation; use Exception; use phpDocumentor\Guides\Compiler\ShadowTree\TreeNode; use phpDocumentor\Guides\Nodes\DocumentNode; @@ -38,6 +39,15 @@ class CompilerContext implements CompilerContextInterface public function __construct( private readonly ProjectNode $projectNode, ) { + if (self::class === static::class) { + return; + } + + Deprecation::trigger( + 'phpdocumentor/guides', + 'https://github.com/phpDocumentor/guides/issues/971', + 'Extending CompilerContext is deprecated, please use the CompilerContextInterface instead.', + ); } public function getProjectNode(): ProjectNode diff --git a/src/Compiler/NodeTransformers/TransformerPass.php b/src/Compiler/NodeTransformers/TransformerPass.php index e2db86e..4ede37e 100644 --- a/src/Compiler/NodeTransformers/TransformerPass.php +++ b/src/Compiler/NodeTransformers/TransformerPass.php @@ -14,7 +14,6 @@ namespace phpDocumentor\Guides\Compiler\NodeTransformers; use phpDocumentor\Guides\Compiler\CompilerContext; -use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\CompilerPass; use phpDocumentor\Guides\Compiler\DocumentNodeTraverser; use phpDocumentor\Guides\Nodes\DocumentNode; diff --git a/tests/unit/Compiler/CompilerContextTest.php b/tests/unit/Compiler/CompilerContextTest.php new file mode 100644 index 0000000..075bee7 --- /dev/null +++ b/tests/unit/Compiler/CompilerContextTest.php @@ -0,0 +1,36 @@ +expectDeprecationWithIdentifier('https://github.com/phpDocumentor/guides/issues/971'); + $context = new class (new ProjectNode()) extends CompilerContext{ + }; + } + + public function testNoDeprecationOnNormalConstruct(): void + { + $this->expectNoDeprecationWithIdentifier('https://github.com/phpDocumentor/guides/issues/971'); + $context = new CompilerContext(new ProjectNode()); + } +} From abc4bbe9eb21fb4a31a13792ae7e75cbfc7e9d95 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 20 Apr 2024 12:40:22 +0200 Subject: [PATCH 20/53] [FEATURE] Introduce directives for bootstrap cards (cherry picked from commit dfab4729092382112282a54f113586cc1c9c2c9f) --- resources/template/html/structure/header-title.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/template/html/structure/header-title.html.twig b/resources/template/html/structure/header-title.html.twig index 8ca0253..a2775cb 100644 --- a/resources/template/html/structure/header-title.html.twig +++ b/resources/template/html/structure/header-title.html.twig @@ -1 +1 @@ -{{ renderNode(node.value) }} +{{ renderNode(node.value) }} From 5be39be04b4a1c2491d3798fe0070df1d08016a5 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 20 Apr 2024 12:40:22 +0200 Subject: [PATCH 21/53] [FEATURE] Introduce directives for bootstrap cards --- src/Nodes/TitleNode.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Nodes/TitleNode.php b/src/Nodes/TitleNode.php index 628f059..e9f59cd 100644 --- a/src/Nodes/TitleNode.php +++ b/src/Nodes/TitleNode.php @@ -40,6 +40,13 @@ public function getLevel(): int return $this->level; } + public function setLevel(int $level): TitleNode + { + $this->level = $level; + + return $this; + } + public function setTarget(string $target): void { $this->target = $target; From e5d047e687f3b541f02d704ac54fe88dd82b9198 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sat, 20 Apr 2024 20:00:43 +0200 Subject: [PATCH 22/53] [BUGFIX] Prefere anchors over titles in references References https://github.com/phpDocumentor/guides/issues/970 (cherry picked from commit 1af4aad5403fe743ea46d2996bd85e1ba4cca19f) --- resources/config/guides.php | 3 + .../CollectLinkTargetsTransformer.php | 2 +- src/Nodes/SectionNode.php | 1 + .../AnchorHyperlinkResolver.php | 6 +- .../TitleReferenceResolver.php | 62 +++++++++++++++++++ 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/ReferenceResolvers/TitleReferenceResolver.php diff --git a/resources/config/guides.php b/resources/config/guides.php index e2f4fae..50eee87 100644 --- a/resources/config/guides.php +++ b/resources/config/guides.php @@ -40,6 +40,7 @@ use phpDocumentor\Guides\ReferenceResolvers\ReferenceResolver; use phpDocumentor\Guides\ReferenceResolvers\ReferenceResolverPreRender; use phpDocumentor\Guides\ReferenceResolvers\SluggerAnchorNormalizer; +use phpDocumentor\Guides\ReferenceResolvers\TitleReferenceResolver; use phpDocumentor\Guides\Renderer\HtmlRenderer; use phpDocumentor\Guides\Renderer\InMemoryRendererFactory; use phpDocumentor\Guides\Renderer\InterlinkObjectsRenderer; @@ -146,6 +147,8 @@ ->set(AnchorReferenceResolver::class) + ->set(TitleReferenceResolver::class) + ->set(InternalReferenceResolver::class) ->set(DocReferenceResolver::class) diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index 28e75f6..afd5231 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -88,7 +88,7 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) $currentDocument->getFilePath(), $anchorName, $node->getLinkText(), - $node->getLinkType(), + SectionNode::STD_TITLE, ), ); diff --git a/src/Nodes/SectionNode.php b/src/Nodes/SectionNode.php index 4905027..0efcb48 100644 --- a/src/Nodes/SectionNode.php +++ b/src/Nodes/SectionNode.php @@ -19,6 +19,7 @@ final class SectionNode extends CompoundNode implements LinkTargetNode { public const STD_LABEL = 'std:label'; + public const STD_TITLE = 'std:title'; public function __construct(private readonly TitleNode $title) { diff --git a/src/ReferenceResolvers/AnchorHyperlinkResolver.php b/src/ReferenceResolvers/AnchorHyperlinkResolver.php index c2a23fb..ed8757d 100644 --- a/src/ReferenceResolvers/AnchorHyperlinkResolver.php +++ b/src/ReferenceResolvers/AnchorHyperlinkResolver.php @@ -15,6 +15,7 @@ use phpDocumentor\Guides\Nodes\Inline\HyperLinkNode; use phpDocumentor\Guides\Nodes\Inline\LinkInlineNode; +use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\RenderContext; use phpDocumentor\Guides\Renderer\UrlGenerator\UrlGeneratorInterface; @@ -43,7 +44,10 @@ public function resolve(LinkInlineNode $node, RenderContext $renderContext, Mess $target = $renderContext->getProjectNode()->getInternalTarget($reducedAnchor); if ($target === null) { - return false; + $target = $renderContext->getProjectNode()->getInternalTarget($reducedAnchor, SectionNode::STD_TITLE); + if ($target === null) { + return false; + } } $node->setUrl($this->urlGenerator->generateCanonicalOutputUrl($renderContext, $target->getDocumentPath(), $target->getAnchor())); diff --git a/src/ReferenceResolvers/TitleReferenceResolver.php b/src/ReferenceResolvers/TitleReferenceResolver.php new file mode 100644 index 0000000..05e5d72 --- /dev/null +++ b/src/ReferenceResolvers/TitleReferenceResolver.php @@ -0,0 +1,62 @@ +getInterlinkDomain() !== '') { + return false; + } + + $reducedAnchor = $this->anchorReducer->reduceAnchor($node->getTargetReference()); + $target = $renderContext->getProjectNode()->getInternalTarget($reducedAnchor, SectionNode::STD_TITLE); + + if ($target === null) { + return false; + } + + $node->setUrl($this->urlGenerator->generateCanonicalOutputUrl($renderContext, $target->getDocumentPath(), $target->getPrefix() . $target->getAnchor())); + if ($node->getValue() === '') { + $node->setValue($target->getTitle() ?? ''); + } + + return true; + } + + public static function getPriority(): int + { + return self::PRIORITY; + } +} From 53b72b9ba98be0c26fcc9e4062f6eb55c0c53531 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sun, 21 Apr 2024 09:27:21 +0200 Subject: [PATCH 23/53] [BUGFIX] Display value of card-footer, Also support links in footer (cherry picked from commit 925b2ea1fe32438fc698c842f2ffd17db2baebac) --- resources/template/html/inline/link.html.twig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/template/html/inline/link.html.twig b/resources/template/html/inline/link.html.twig index 9c9160e..f92d44f 100644 --- a/resources/template/html/inline/link.html.twig +++ b/resources/template/html/inline/link.html.twig @@ -1,5 +1,8 @@ {%- if node.url -%} - + {{- node.value -}} {%- else -%} From ece54b2fa13a1e87d599af1b30baa8e141cba3ff Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Sun, 21 Apr 2024 13:31:14 +0200 Subject: [PATCH 24/53] [TASK] Add getter for Documents to RenderContext (cherry picked from commit b415ed7eb6017a8de7a3a8e0b731ffd71c799080) --- src/RenderContext.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/RenderContext.php b/src/RenderContext.php index 658d0ca..813100a 100644 --- a/src/RenderContext.php +++ b/src/RenderContext.php @@ -82,6 +82,17 @@ public function withDocument(DocumentNode $documentNode): self )->withIterator($this->getIterator()); } + public function getDocument(): DocumentNode + { + return $this->document; + } + + /** @return DocumentNode[] */ + public function getAllDocuments(): array + { + return $this->allDocuments; + } + public function withIterator(Renderer\DocumentListIterator $iterator): self { $that = clone $this; From d57a527208f8321be99c988897bfe43b9374a912 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Fri, 26 Apr 2024 16:02:52 +0200 Subject: [PATCH 25/53] [TASK] Warn about duplicate anchors Sphinx also warns about these (cherry picked from commit 60cc7d9168adc4a48f9fecdee2d19a7fa992377e) --- .../CollectLinkTargetsTransformer.php | 53 ++++++++++++------- .../DuplicateLinkAnchorException.php | 20 +++++++ src/Nodes/ProjectNode.php | 14 +++-- 3 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 src/Exception/DuplicateLinkAnchorException.php diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index afd5231..93c7889 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -15,6 +15,7 @@ use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; +use phpDocumentor\Guides\Exception\DuplicateLinkAnchorException; use phpDocumentor\Guides\Meta\InternalTarget; use phpDocumentor\Guides\Nodes\AnchorNode; use phpDocumentor\Guides\Nodes\DocumentNode; @@ -66,14 +67,18 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) } $anchorName = $this->anchorReducer->reduceAnchor($node->toString()); - $compilerContext->getProjectNode()->addLinkTarget( - $anchorName, - new InternalTarget( - $currentDocument->getFilePath(), - $node->toString(), - $title, - ), - ); + try { + $compilerContext->getProjectNode()->addLinkTarget( + $anchorName, + new InternalTarget( + $currentDocument->getFilePath(), + $node->toString(), + $title, + ), + ); + } catch (DuplicateLinkAnchorException $exception) { + $this->logger?->warning($exception->getMessage(), $compilerContext->getLoggerInformation()); + } return $node; } @@ -82,15 +87,19 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) $currentDocument = $this->documentStack->top(); Assert::notNull($currentDocument); $anchorName = $node->getId(); - $compilerContext->getProjectNode()->addLinkTarget( - $anchorName, - new InternalTarget( - $currentDocument->getFilePath(), + try { + $compilerContext->getProjectNode()->addLinkTarget( $anchorName, - $node->getLinkText(), - SectionNode::STD_TITLE, - ), - ); + new InternalTarget( + $currentDocument->getFilePath(), + $anchorName, + $node->getLinkText(), + SectionNode::STD_TITLE, + ), + ); + } catch (DuplicateLinkAnchorException $exception) { + $this->logger?->warning($exception->getMessage(), $compilerContext->getLoggerInformation()); + } return $node; } @@ -175,9 +184,13 @@ private function addLinkTargetToProject(CompilerContextInterface $compilerContex return; } - $compilerContext->getProjectNode()->addLinkTarget( - $internalTarget->getAnchor(), - $internalTarget, - ); + try { + $compilerContext->getProjectNode()->addLinkTarget( + $internalTarget->getAnchor(), + $internalTarget, + ); + } catch (DuplicateLinkAnchorException $exception) { + $this->logger?->warning($exception->getMessage(), $compilerContext->getLoggerInformation()); + } } } diff --git a/src/Exception/DuplicateLinkAnchorException.php b/src/Exception/DuplicateLinkAnchorException.php new file mode 100644 index 0000000..d4ebabb --- /dev/null +++ b/src/Exception/DuplicateLinkAnchorException.php @@ -0,0 +1,20 @@ +citationTargets[$name] ?? null; } + /** @throws DuplicateLinkAnchorException */ public function addLinkTarget(string $anchorName, InternalTarget $target): void { - if (!isset($this->internalLinkTargets[$target->getLinkType()])) { - $this->internalLinkTargets[$target->getLinkType()] = []; + $linkType = $target->getLinkType(); + if (!isset($this->internalLinkTargets[$linkType])) { + $this->internalLinkTargets[$linkType] = []; } - $this->internalLinkTargets[$target->getLinkType()][$anchorName] = $target; + if (isset($this->internalLinkTargets[$linkType][$anchorName]) && $linkType !== 'std:title') { + throw new DuplicateLinkAnchorException(sprintf('Duplicate anchor "%s". There is already another anchor of that name in document "%s"', $anchorName, $this->internalLinkTargets[$linkType][$anchorName]->getDocumentPath())); + } + + $this->internalLinkTargets[$linkType][$anchorName] = $target; } public function hasInternalTarget(string $anchorName, string $linkType = SectionNode::STD_LABEL): bool From a190b5989766a5ba3a8e9809425c35b2aa1da595 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Wed, 1 May 2024 13:28:21 +0200 Subject: [PATCH 26/53] [BUGFIX] Fix doc references containing anchors See also https://github.com/TYPO3-Documentation/render-guides/issues/525 (cherry picked from commit 4296dd3c9f5767022e422eae24e7030ea2b3e1d2) --- .../DocReferenceResolver.php | 14 +++- .../Interlink/InventoryGroup.php | 20 ++++- .../Interlink/InventoryLink.php | 10 ++- tests/unit/Interlink/InventoryGroupTest.php | 68 +++++++++++++++++ .../DocReferenceResolverTest.php | 74 +++++++++++++++++++ .../InterlinkReferenceResolverTest.php | 70 ++++++++++++++++++ 6 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 tests/unit/Interlink/InventoryGroupTest.php create mode 100644 tests/unit/ReferenceResolvers/DocReferenceResolverTest.php create mode 100644 tests/unit/ReferenceResolvers/InterlinkReferenceResolverTest.php diff --git a/src/ReferenceResolvers/DocReferenceResolver.php b/src/ReferenceResolvers/DocReferenceResolver.php index 7176d4d..0de9e76 100644 --- a/src/ReferenceResolvers/DocReferenceResolver.php +++ b/src/ReferenceResolvers/DocReferenceResolver.php @@ -18,7 +18,9 @@ use phpDocumentor\Guides\RenderContext; use phpDocumentor\Guides\Renderer\UrlGenerator\UrlGeneratorInterface; +use function explode; use function sprintf; +use function str_contains; final class DocReferenceResolver implements ReferenceResolver { @@ -40,7 +42,15 @@ public function resolve(LinkInlineNode $node, RenderContext $renderContext, Mess return false; } - $canonicalDocumentName = $this->documentNameResolver->canonicalUrl($renderContext->getDirName(), $node->getTargetReference()); + $targetReference = $node->getTargetReference(); + $anchor = ''; + if (str_contains($targetReference, '#')) { + $exploded = explode('#', $targetReference, 2); + $targetReference = $exploded[0]; + $anchor = '#' . $exploded[1]; + } + + $canonicalDocumentName = $this->documentNameResolver->canonicalUrl($renderContext->getDirName(), $targetReference); $document = $renderContext->getProjectNode()->findDocumentEntry($canonicalDocumentName); if ($document === null) { @@ -53,7 +63,7 @@ public function resolve(LinkInlineNode $node, RenderContext $renderContext, Mess return false; } - $node->setUrl($this->urlGenerator->generateCanonicalOutputUrl($renderContext, $document->getFile())); + $node->setUrl($this->urlGenerator->generateCanonicalOutputUrl($renderContext, $document->getFile()) . $anchor); if ($node->getValue() === '') { $node->setValue($document->getTitle()->toString()); } diff --git a/src/ReferenceResolvers/Interlink/InventoryGroup.php b/src/ReferenceResolvers/Interlink/InventoryGroup.php index aa1ed90..38f3214 100644 --- a/src/ReferenceResolvers/Interlink/InventoryGroup.php +++ b/src/ReferenceResolvers/Interlink/InventoryGroup.php @@ -14,6 +14,7 @@ namespace phpDocumentor\Guides\ReferenceResolvers\Interlink; use phpDocumentor\Guides\Nodes\Inline\CrossReferenceNode; +use phpDocumentor\Guides\Nodes\Inline\DocReferenceNode; use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer; use phpDocumentor\Guides\ReferenceResolvers\Message; use phpDocumentor\Guides\ReferenceResolvers\Messages; @@ -21,7 +22,9 @@ use function array_key_exists; use function array_merge; +use function explode; use function sprintf; +use function str_contains; final class InventoryGroup { @@ -47,7 +50,15 @@ public function hasLink(string $key): bool public function getLink(CrossReferenceNode $node, RenderContext $renderContext, Messages $messages): InventoryLink|null { - $reducedKey = $this->anchorNormalizer->reduceAnchor($node->getTargetReference()); + $targetReference = $node->getTargetReference(); + $anchor = ''; + if ($node instanceof DocReferenceNode && str_contains($targetReference, '#')) { + $exploded = explode('#', $targetReference, 2); + $targetReference = $exploded[0]; + $anchor = '#' . $exploded[1]; + } + + $reducedKey = $this->anchorNormalizer->reduceAnchor($targetReference); if (!array_key_exists($reducedKey, $this->links)) { $messages->addWarning( new Message( @@ -64,6 +75,11 @@ public function getLink(CrossReferenceNode $node, RenderContext $renderContext, return null; } - return $this->links[$reducedKey]; + $link = $this->links[$reducedKey]; + if ($anchor !== '') { + $link = $link->withPath($link->getPath() . $anchor); + } + + return $link; } } diff --git a/src/ReferenceResolvers/Interlink/InventoryLink.php b/src/ReferenceResolvers/Interlink/InventoryLink.php index ab3851f..98a47b1 100644 --- a/src/ReferenceResolvers/Interlink/InventoryLink.php +++ b/src/ReferenceResolvers/Interlink/InventoryLink.php @@ -22,7 +22,7 @@ final class InventoryLink public function __construct( private readonly string $project, private readonly string $version, - private readonly string $path, + private string $path, private readonly string $title, ) { if (preg_match('/^([a-zA-Z0-9-_.]+\/)*([a-zA-Z0-9-_.])+\.html(#[^#]*)?$/', $path) < 1) { @@ -49,4 +49,12 @@ public function getTitle(): string { return $this->title; } + + public function withPath(string $path): InventoryLink + { + $that = clone$this; + $that->path = $path; + + return $that; + } } diff --git a/tests/unit/Interlink/InventoryGroupTest.php b/tests/unit/Interlink/InventoryGroupTest.php new file mode 100644 index 0000000..371cbeb --- /dev/null +++ b/tests/unit/Interlink/InventoryGroupTest.php @@ -0,0 +1,68 @@ +inventoryGroup = new InventoryGroup(new NullAnchorNormalizer()); + $this->renderContext = $this->createMock(RenderContext::class); + } + + #[DataProvider('linkProvider')] + public function testGetLinkFromInterlinkGroup(string $expected, string $input, string $path): void + { + $this->inventoryGroup->addLink($path, new InventoryLink('', '', $path . '.html', '')); + $messages = new Messages(); + $link = $this->inventoryGroup->getLink( + new DocReferenceNode($input, '', 'interlink'), + $this->renderContext, + $messages, + ); + self::assertEmpty($messages->getWarnings()); + self::assertEquals($expected, $link?->getPath()); + } + + /** @return string[][] */ + public static function linkProvider(): array + { + return [ + 'plain' => [ + 'expected' => 'some-document.html', + 'input' => 'some-document', + 'path' => 'some-document', + ], + 'withAnchor' => [ + 'expected' => 'some-document.html#anchor', + 'input' => 'some-document#anchor', + 'path' => 'some-document', + ], + ]; + } +} diff --git a/tests/unit/ReferenceResolvers/DocReferenceResolverTest.php b/tests/unit/ReferenceResolvers/DocReferenceResolverTest.php new file mode 100644 index 0000000..d55af44 --- /dev/null +++ b/tests/unit/ReferenceResolvers/DocReferenceResolverTest.php @@ -0,0 +1,74 @@ +projectNode = new ProjectNode('some-name'); + $this->projectNode->addDocumentEntry($documentEntry); + $this->renderContext = $this->createMock(RenderContext::class); + $this->renderContext->expects(self::once())->method('getProjectNode')->willReturn($this->projectNode); + $this->documentNameResolver = self::createMock(DocumentNameResolverInterface::class); + $this->urlGenerator = self::createMock(UrlGeneratorInterface::class); + $this->subject = new DocReferenceResolver($this->urlGenerator, $this->documentNameResolver); + } + + #[DataProvider('pathProvider')] + public function testDocumentReducer(string $expected, string $input, string $path): void + { + $this->documentNameResolver->expects(self::once())->method('canonicalUrl')->with('', $path)->willReturn($path); + $input = new DocReferenceNode($input); + $this->urlGenerator->expects(self::once())->method('generateCanonicalOutputUrl')->willReturn($path); + $messages = new Messages(); + self::assertTrue($this->subject->resolve($input, $this->renderContext, $messages)); + self::assertEmpty($messages->getWarnings()); + self::assertEquals($expected, $input->getUrl()); + } + + /** @return string[][] */ + public static function pathProvider(): array + { + return [ + 'plain' => [ + 'expected' => 'some-document', + 'input' => 'some-document', + 'path' => 'some-document', + ], + 'withAnchor' => [ + 'expected' => 'some-document#anchor', + 'input' => 'some-document#anchor', + 'path' => 'some-document', + ], + ]; + } +} diff --git a/tests/unit/ReferenceResolvers/InterlinkReferenceResolverTest.php b/tests/unit/ReferenceResolvers/InterlinkReferenceResolverTest.php new file mode 100644 index 0000000..60e1654 --- /dev/null +++ b/tests/unit/ReferenceResolvers/InterlinkReferenceResolverTest.php @@ -0,0 +1,70 @@ +renderContext = $this->createMock(RenderContext::class); + $this->inventoryRepository = $this->createMock(InventoryRepository::class); + $this->anchorNormalizer = new NullAnchorNormalizer(); + $this->subject = new InterlinkReferenceResolver($this->inventoryRepository); + } + + #[DataProvider('pathProvider')] + public function testDocumentReducer(string $expected, string $input, string $path): void + { + $input = new DocReferenceNode($input, '', 'interlink-target'); + $inventoryLink = new InventoryLink('project', '1.0', $path, ''); + $inventory = new Inventory('base-url/', $this->anchorNormalizer); + $this->inventoryRepository->expects(self::once())->method('getInventory')->willReturn($inventory); + $this->inventoryRepository->expects(self::once())->method('getLink')->willReturn($inventoryLink); + $messages = new Messages(); + self::assertTrue($this->subject->resolve($input, $this->renderContext, $messages)); + self::assertEmpty($messages->getWarnings()); + self::assertEquals($expected, $input->getUrl()); + } + + /** @return string[][] */ + public static function pathProvider(): array + { + return [ + 'plain' => [ + 'expected' => 'base-url/some-document.html', + 'input' => 'some-document', + 'path' => 'some-document.html', + ], + 'withAnchor' => [ + 'expected' => 'base-url/some-document.html#anchor', + 'input' => 'some-document#anchor', + 'path' => 'some-document.html#anchor', + ], + ]; + } +} From 61cd424d2f863c3567478b8d0a9c36692fc3c3c6 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Mon, 6 May 2024 20:35:57 +0200 Subject: [PATCH 27/53] [BUGFIX] Display order numbers correctly Resolves https://github.com/phpDocumentor/guides/issues/1003 (cherry picked from commit c116fedb2217b036decc2ca848e58208a5e32012) --- .../html/body/list/list-item.html.twig | 3 +- .../template/html/body/list/list.html.twig | 4 ++- src/Nodes/ListItemNode.php | 12 +++++++ src/Nodes/ListNode.php | 32 +++++++++++++++++-- src/Twig/AssetsExtension.php | 19 +++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/resources/template/html/body/list/list-item.html.twig b/resources/template/html/body/list/list-item.html.twig index 1d76fb0..c0b845f 100644 --- a/resources/template/html/body/list/list-item.html.twig +++ b/resources/template/html/body/list/list-item.html.twig @@ -1,4 +1,5 @@ - + {%- for child in node.children -%} {{ renderNode(child) }} {%- endfor -%} diff --git a/resources/template/html/body/list/list.html.twig b/resources/template/html/body/list/list.html.twig index 135646c..2f9d996 100644 --- a/resources/template/html/body/list/list.html.twig +++ b/resources/template/html/body/list/list.html.twig @@ -4,7 +4,9 @@ {% set keyword = 'ol' %} {% endif %} -<{{ keyword }}{% if node.classes %} class="{{ node.classesString }}"{% endif %}> +<{{ keyword }}{% if node.classes %} class="{{ node.classesString }}"{% endif %} +{%- if node.start %} start="{{ node.start }}"{% endif -%} +{%- if node.orderingType %} type="{{ renderOrderedListType(node.orderingType) }}"{% endif -%}> {% for child in node.children %} {{ renderNode(child) }} {% endfor %} diff --git a/src/Nodes/ListItemNode.php b/src/Nodes/ListItemNode.php index 44ec855..3cf9d11 100644 --- a/src/Nodes/ListItemNode.php +++ b/src/Nodes/ListItemNode.php @@ -25,6 +25,8 @@ public function __construct( private readonly string $prefix, private readonly bool $ordered, array $contents, + private readonly string|null $orderNumber = null, + private readonly string|null $orderType = null, ) { parent::__construct($contents); } @@ -38,4 +40,14 @@ public function isOrdered(): bool { return $this->ordered; } + + public function getOrderNumber(): string|null + { + return $this->orderNumber; + } + + public function getOrderType(): string|null + { + return $this->orderType; + } } diff --git a/src/Nodes/ListNode.php b/src/Nodes/ListNode.php index ef9b9b1..4cc76f5 100644 --- a/src/Nodes/ListNode.php +++ b/src/Nodes/ListNode.php @@ -17,11 +17,39 @@ final class ListNode extends CompoundNode { /** @param ListItemNode[] $items */ - public function __construct(array $items, private readonly bool $ordered = false) - { + public function __construct( + array $items, + private readonly bool $ordered = false, + private string|null $start = null, + private string|null $orderingType = null, + ) { parent::__construct($items); } + public function getStart(): string|null + { + return $this->start; + } + + public function setStart(string|null $start): ListNode + { + $this->start = $start; + + return $this; + } + + public function getOrderingType(): string|null + { + return $this->orderingType; + } + + public function setOrderingType(string|null $orderingType): ListNode + { + $this->orderingType = $orderingType; + + return $this; + } + public function isOrdered(): bool { return $this->ordered; diff --git a/src/Twig/AssetsExtension.php b/src/Twig/AssetsExtension.php index 98d3357..16393d1 100644 --- a/src/Twig/AssetsExtension.php +++ b/src/Twig/AssetsExtension.php @@ -59,6 +59,7 @@ public function getFunctions(): array new TwigFunction('renderBreadcrumb', $this->renderBreadcrumb(...), ['is_safe' => ['html'], 'needs_context' => true]), new TwigFunction('renderMenu', $this->renderMenu(...), ['is_safe' => ['html'], 'needs_context' => true, 'deprecated' => true]), new TwigFunction('renderTarget', $this->renderTarget(...), ['is_safe' => ['html'], 'needs_context' => true]), + new TwigFunction('renderOrderedListType', $this->renderOrderedListType(...), ['is_safe' => ['html'], 'needs_context' => false]), ]; } @@ -207,4 +208,22 @@ private function getRenderContext(array $context): RenderContext return $renderContext; } + + public function renderOrderedListType(string $listType): string + { + switch ($listType) { + case 'numberdot': + case 'numberparentheses': + case 'numberright-parenthesis': + return '1'; + + case 'romandot': + case 'romanparentheses': + case 'romanright-parenthesis': + return 'i'; + + default: + return 'a'; + } + } } From 6ad969759faeec06476f6becc7390d188e94ccce Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Thu, 18 Apr 2024 09:36:57 +0200 Subject: [PATCH 28/53] [FEATURE] Introduce NavigationTitle (cherry picked from commit caa17bf7dc87604b3f8b6fa265aea5d91379b82d) --- .../ModifyDocumentEntryAdditionalData.php | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/Event/ModifyDocumentEntryAdditionalData.php diff --git a/src/Event/ModifyDocumentEntryAdditionalData.php b/src/Event/ModifyDocumentEntryAdditionalData.php new file mode 100644 index 0000000..6d87ff1 --- /dev/null +++ b/src/Event/ModifyDocumentEntryAdditionalData.php @@ -0,0 +1,53 @@ + $additionalData */ + public function __construct( + private array $additionalData, + private readonly DocumentNode $documentNode, + private readonly CompilerContextInterface $compilerContext, + ) { + } + + /** @return array */ + public function getAdditionalData(): array + { + return $this->additionalData; + } + + /** @param array $additionalData */ + public function setAdditionalData(array $additionalData): ModifyDocumentEntryAdditionalData + { + $this->additionalData = $additionalData; + + return $this; + } + + public function getDocumentNode(): DocumentNode + { + return $this->documentNode; + } + + public function getCompilerContext(): CompilerContextInterface + { + return $this->compilerContext; + } +} From e08eba4ad63f9a767d0bcf19434d404563a1a8ea Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Mon, 26 Feb 2024 08:09:52 +0100 Subject: [PATCH 29/53] [FEATURE] Introduce NavigationTitle (cherry picked from commit de27f61add1fd478d38dbde4b50bb1838c2187bf) --- .../DocumentEntryRegistrationTransformer.php | 24 ++++++++++++++++- .../GlobMenuEntryNodeTransformer.php | 9 ++++++- .../InternalMenuEntryNodeTransformer.php | 13 +++++++++- .../ModifyDocumentEntryAdditionalData.php | 6 ++--- .../Html/BreadCrumbNodeRenderer.php | 9 ++++++- src/Nodes/DocumentNode.php | 12 +++++++++ src/Nodes/DocumentTree/DocumentEntryNode.php | 13 ++++++++++ src/Nodes/Metadata/NavigationTitleNode.php | 26 +++++++++++++++++++ 8 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/Nodes/Metadata/NavigationTitleNode.php diff --git a/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php b/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php index 0edd5c5..16ef5fe 100644 --- a/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php +++ b/src/Compiler/NodeTransformers/DocumentEntryRegistrationTransformer.php @@ -15,17 +15,23 @@ use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Compiler\NodeTransformer; +use phpDocumentor\Guides\Event\ModifyDocumentEntryAdditionalData; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\Nodes\TitleNode; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Log\LoggerInterface; +use function assert; +use function is_string; + /** @implements NodeTransformer */ final class DocumentEntryRegistrationTransformer implements NodeTransformer { public function __construct( private readonly LoggerInterface $logger, + private readonly EventDispatcherInterface|null $eventDispatcher = null, ) { } @@ -44,7 +50,23 @@ public function leaveNode(Node $node, CompilerContextInterface $compilerContext) $this->logger->warning('Document has no title', $compilerContext->getLoggerInformation()); } - $entry = new DocumentEntryNode($node->getFilePath(), $node->getTitle() ?? TitleNode::emptyNode(), $node->isRoot()); + $additionalData = []; + if (is_string($node->getNavigationTitle())) { + $additionalData['navigationTitle'] = TitleNode::fromString($node->getNavigationTitle()); + } + + if ($this->eventDispatcher !== null) { + $event = $this->eventDispatcher->dispatch(new ModifyDocumentEntryAdditionalData($additionalData, $node, $compilerContext)); + assert($event instanceof ModifyDocumentEntryAdditionalData); + $additionalData = $event->getAdditionalData(); + } + + $entry = new DocumentEntryNode( + $node->getFilePath(), + $node->getTitle() ?? TitleNode::emptyNode(), + $node->isRoot(), + $additionalData, + ); $compilerContext->getProjectNode()->addDocumentEntry($entry); return $node->setDocumentEntry($entry); diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php index 28dae6e..16484f5 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php @@ -20,6 +20,7 @@ use phpDocumentor\Guides\Nodes\Menu\MenuNode; use phpDocumentor\Guides\Nodes\Menu\TocNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\Nodes\TitleNode; use function array_pop; use function assert; @@ -65,10 +66,16 @@ protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNo } } + $titleNode = $documentEntry->getTitle(); + $navigationTitle = $documentEntry->getAdditionalData('navigationTitle'); + if ($navigationTitle instanceof TitleNode) { + $titleNode = $navigationTitle; + } + $documentEntriesInTree[] = $documentEntry; $newEntryNode = new InternalMenuEntryNode( $documentEntry->getFile(), - $documentEntry->getTitle(), + $titleNode, [], false, 1, diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php index f4ec497..6e92838 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/InternalMenuEntryNodeTransformer.php @@ -19,6 +19,7 @@ use phpDocumentor\Guides\Nodes\Menu\MenuNode; use phpDocumentor\Guides\Nodes\Menu\TocNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\Nodes\TitleNode; use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; use Psr\Log\LoggerInterface; @@ -63,10 +64,20 @@ protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNo continue; } + $titleNode = $documentEntry->getTitle(); + $navigationTitle = $documentEntry->getAdditionalData('navigationTitle'); + if ($navigationTitle instanceof TitleNode) { + $titleNode = $navigationTitle; + } + + if ($entryNode->getValue() instanceof TitleNode) { + $titleNode = $entryNode->getValue(); + } + $documentEntriesInTree[] = $documentEntry; $newEntryNode = new InternalMenuEntryNode( $documentEntry->getFile(), - $entryNode->getValue() ?? $documentEntry->getTitle(), + $titleNode, [], false, 1, diff --git a/src/Event/ModifyDocumentEntryAdditionalData.php b/src/Event/ModifyDocumentEntryAdditionalData.php index 6d87ff1..d0e3a25 100644 --- a/src/Event/ModifyDocumentEntryAdditionalData.php +++ b/src/Event/ModifyDocumentEntryAdditionalData.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Event; -use phpDocumentor\Guides\Compiler\CompilerContextInterface; +use phpDocumentor\Guides\Compiler\CompilerContext; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\Node; @@ -23,7 +23,7 @@ final class ModifyDocumentEntryAdditionalData public function __construct( private array $additionalData, private readonly DocumentNode $documentNode, - private readonly CompilerContextInterface $compilerContext, + private readonly CompilerContext $compilerContext, ) { } @@ -46,7 +46,7 @@ public function getDocumentNode(): DocumentNode return $this->documentNode; } - public function getCompilerContext(): CompilerContextInterface + public function getCompilerContext(): CompilerContext { return $this->compilerContext; } diff --git a/src/NodeRenderers/Html/BreadCrumbNodeRenderer.php b/src/NodeRenderers/Html/BreadCrumbNodeRenderer.php index 8c4d28d..24c07e3 100644 --- a/src/NodeRenderers/Html/BreadCrumbNodeRenderer.php +++ b/src/NodeRenderers/Html/BreadCrumbNodeRenderer.php @@ -19,6 +19,7 @@ use phpDocumentor\Guides\Nodes\Menu\InternalMenuEntryNode; use phpDocumentor\Guides\Nodes\Menu\MenuEntryNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\Nodes\TitleNode; use phpDocumentor\Guides\RenderContext; use phpDocumentor\Guides\TemplateRenderer; @@ -107,9 +108,15 @@ private function buildBreadcrumb( int $level, bool $isCurrent, ): array { + $title = $documentEntry->getTitle(); + $navigationTitle = $documentEntry->getAdditionalData('navigationTitle'); + if ($navigationTitle instanceof TitleNode) { + $title = $navigationTitle; + } + $entry = new InternalMenuEntryNode( $documentEntry->getFile(), - $documentEntry->getTitle(), + $title, [], false, $level, diff --git a/src/Nodes/DocumentNode.php b/src/Nodes/DocumentNode.php index 1acead6..05f8974 100644 --- a/src/Nodes/DocumentNode.php +++ b/src/Nodes/DocumentNode.php @@ -19,6 +19,7 @@ use phpDocumentor\Guides\Nodes\DocumentTree\SectionEntryNode; use phpDocumentor\Guides\Nodes\Menu\TocNode; use phpDocumentor\Guides\Nodes\Metadata\MetadataNode; +use phpDocumentor\Guides\Nodes\Metadata\NavigationTitleNode; use function array_filter; use function max; @@ -52,6 +53,8 @@ final class DocumentNode extends CompoundNode private int $maxFootnoteNumber = 0; private int $lastReturnedAnonymousFootnoteNumber = -1; + private string|null $navigationTitle = null; + /** * Variables are replacements in a document or project. * @@ -97,6 +100,11 @@ public function getNodes(string $nodeType = Node::class): array return array_filter($this->value, static fn ($node): bool => $node instanceof $nodeType); } + public function getNavigationTitle(): string|null + { + return $this->navigationTitle; + } + public function getPageTitle(): string|null { if ($this->metaTitle !== null) { @@ -132,6 +140,10 @@ public function setMetaTitle(string $metaTitle): void public function addHeaderNode(MetadataNode $node): void { + if ($node instanceof NavigationTitleNode) { + $this->navigationTitle = $node->getValue(); + } + $this->headerNodes[] = $node; } diff --git a/src/Nodes/DocumentTree/DocumentEntryNode.php b/src/Nodes/DocumentTree/DocumentEntryNode.php index 0d1b188..6dfac5a 100644 --- a/src/Nodes/DocumentTree/DocumentEntryNode.php +++ b/src/Nodes/DocumentTree/DocumentEntryNode.php @@ -13,6 +13,7 @@ namespace phpDocumentor\Guides\Nodes\DocumentTree; +use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\Nodes\SectionNode; use phpDocumentor\Guides\Nodes\TitleNode; @@ -27,10 +28,12 @@ final class DocumentEntryNode extends EntryNode /** @var SectionEntryNode[] */ private array $sections = []; + /** @param array $additionalData */ public function __construct( private readonly string $file, private readonly TitleNode $titleNode, private readonly bool $isRoot = false, + private array $additionalData = [], ) { } @@ -100,4 +103,14 @@ public function findSectionEntry(SectionNode $sectionNode): SectionEntryNode|nul return null; } + + public function getAdditionalData(string $key): Node|null + { + return $this->additionalData[$key] ?? null; + } + + public function addAdditionalData(string $key, Node $value): void + { + $this->additionalData[$key] = $value; + } } diff --git a/src/Nodes/Metadata/NavigationTitleNode.php b/src/Nodes/Metadata/NavigationTitleNode.php new file mode 100644 index 0000000..95cb0b6 --- /dev/null +++ b/src/Nodes/Metadata/NavigationTitleNode.php @@ -0,0 +1,26 @@ + Date: Thu, 18 Apr 2024 09:36:57 +0200 Subject: [PATCH 30/53] [FEATURE] Introduce NavigationTitle (cherry picked from commit caa17bf7dc87604b3f8b6fa265aea5d91379b82d) --- src/Event/ModifyDocumentEntryAdditionalData.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Event/ModifyDocumentEntryAdditionalData.php b/src/Event/ModifyDocumentEntryAdditionalData.php index d0e3a25..6d87ff1 100644 --- a/src/Event/ModifyDocumentEntryAdditionalData.php +++ b/src/Event/ModifyDocumentEntryAdditionalData.php @@ -13,7 +13,7 @@ namespace phpDocumentor\Guides\Event; -use phpDocumentor\Guides\Compiler\CompilerContext; +use phpDocumentor\Guides\Compiler\CompilerContextInterface; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\Node; @@ -23,7 +23,7 @@ final class ModifyDocumentEntryAdditionalData public function __construct( private array $additionalData, private readonly DocumentNode $documentNode, - private readonly CompilerContext $compilerContext, + private readonly CompilerContextInterface $compilerContext, ) { } @@ -46,7 +46,7 @@ public function getDocumentNode(): DocumentNode return $this->documentNode; } - public function getCompilerContext(): CompilerContext + public function getCompilerContext(): CompilerContextInterface { return $this->compilerContext; } From c6766d529735adf8d1b32c8b1c0169aaf448e565 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Tue, 7 May 2024 16:28:53 +0200 Subject: [PATCH 31/53] [BUGFIX] Display Navigation title in submenu (cherry picked from commit 53231d4c88621908064dc898fe4fd1248ab4bb0b) --- .../SubInternalMenuEntryNodeTransformer.php | 9 +++++++-- src/Compiler/Passes/GlobalMenuPass.php | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php index 9aa7776..92c1336 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/SubInternalMenuEntryNodeTransformer.php @@ -53,7 +53,6 @@ protected function handleMenuEntry(MenuNode $currentMenu, MenuEntryNode $entryNo return []; } - $documentEntryOfMenuEntry = $compilerContext->getProjectNode()->getDocumentEntry($entryNode->getUrl()); $this->addSubEntries($currentMenu, $compilerContext, $entryNode, $documentEntryOfMenuEntry, $entryNode->getLevel() + 1, $maxDepth); return [$entryNode]; @@ -79,9 +78,15 @@ private function addSubEntries( foreach ($documentEntry->getMenuEntries() as $subEntryNode) { if ($subEntryNode instanceof DocumentEntryNode) { + $titleNode = $subEntryNode->getTitle(); + $navigationTitle = $subEntryNode->getAdditionalData('navigationTitle'); + if ($navigationTitle instanceof TitleNode) { + $titleNode = $navigationTitle; + } + $subMenuEntry = new InternalMenuEntryNode( $subEntryNode->getFile(), - $subEntryNode->getTitle(), + $titleNode, [], false, $currentLevel, diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index edc97ba..b1891d7 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -158,9 +158,15 @@ private function addSubEntries( private function createInternalMenuEntry(DocumentEntryNode $subEntryNode, int $currentLevel): InternalMenuEntryNode { + $titleNode = $subEntryNode->getTitle(); + $navigationTitle = $subEntryNode->getAdditionalData('navigationTitle'); + if ($navigationTitle instanceof TitleNode) { + $titleNode = $navigationTitle; + } + return new InternalMenuEntryNode( $subEntryNode->getFile(), - $subEntryNode->getTitle(), + $titleNode, [], false, $currentLevel, From 9bdedbeaaaa18eae9d0fa18ff29ab4929dc58c77 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Mon, 29 Jul 2024 12:05:59 +0200 Subject: [PATCH 32/53] [FEATURE] Link to phpdomain API (cherry picked from commit eb63f8452259a7772a27a94615cc3e1da0f19ea8) --- src/ReferenceResolvers/Interlink/InventoryLink.php | 2 +- tests/unit/Interlink/InventoryLinkTest.php | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/ReferenceResolvers/Interlink/InventoryLink.php b/src/ReferenceResolvers/Interlink/InventoryLink.php index 98a47b1..9867235 100644 --- a/src/ReferenceResolvers/Interlink/InventoryLink.php +++ b/src/ReferenceResolvers/Interlink/InventoryLink.php @@ -25,7 +25,7 @@ public function __construct( private string $path, private readonly string $title, ) { - if (preg_match('/^([a-zA-Z0-9-_.]+\/)*([a-zA-Z0-9-_.])+\.html(#[^#]*)?$/', $path) < 1) { + if (preg_match('/^([a-zA-Z0-9-_.]+\/)*([a-zA-Z0-9-_.]+)(\.html)?(#[^#]*)?$/', $path) < 1) { throw new InvalidInventoryLink('Inventory link "' . $path . '" has an invalid scheme. ', 1_671_398_986); } } diff --git a/tests/unit/Interlink/InventoryLinkTest.php b/tests/unit/Interlink/InventoryLinkTest.php index a858caf..689aebd 100644 --- a/tests/unit/Interlink/InventoryLinkTest.php +++ b/tests/unit/Interlink/InventoryLinkTest.php @@ -54,13 +54,6 @@ public function testLinkMayContaintDot(): void self::assertEquals($inventoryLink->getPath(), $link); } - public function testPhpLinkThrowsError(): void - { - $link = 'Some/Path/SomeThing.php#anchor'; - $this->expectException(InvalidInventoryLink::class); - new InventoryLink('', '', $link, ''); - } - public function testJavaScriptLinkThrowsError(): void { $link = 'javascript:alert()'; From 211b0193fc7687e9eb55a871e06bed18cbfe848d Mon Sep 17 00:00:00 2001 From: Jaapio Date: Wed, 4 Sep 2024 21:47:23 +0200 Subject: [PATCH 33/53] [BUGFIX] do not trim per line Table cells may contain multiple lines as we did trim the cell content before the table parsing was completed we did mis empty lines. As empty lines where missing we did break the parsing process of a table cell. (cherry picked from commit 5e6d683e44f4c72ff0efcd6a149bf297d2ba4520) --- src/Nodes/Table/TableColumn.php | 2 +- src/Nodes/Table/TableRow.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Nodes/Table/TableColumn.php b/src/Nodes/Table/TableColumn.php index 34afc2f..35970b4 100644 --- a/src/Nodes/Table/TableColumn.php +++ b/src/Nodes/Table/TableColumn.php @@ -54,7 +54,7 @@ public function getRowSpan(): int public function addContent(string $content): void { - $this->content = trim($this->content . $content); + $this->content .= $content; } public function incrementRowSpan(): void diff --git a/src/Nodes/Table/TableRow.php b/src/Nodes/Table/TableRow.php index 81e0044..8cc37bb 100644 --- a/src/Nodes/Table/TableRow.php +++ b/src/Nodes/Table/TableRow.php @@ -23,8 +23,10 @@ final class TableRow { - /** @var TableColumn[] */ - private array $columns = []; + /** @param TableColumn[] $columns */ + public function __construct(private array $columns = []) + { + } public function addColumn(TableColumn $tableColumn): void { From a431f1d1f398687bf93e80ef525b83e21f75b15b Mon Sep 17 00:00:00 2001 From: Jaapio Date: Wed, 4 Sep 2024 20:06:12 +0200 Subject: [PATCH 34/53] [BUGFIX] Allow escaped version symfony is trying to normalize the xml input using some logic. However in our case we want to fetch the raw value for version. But this is not possible. therefor an escape system is added by adding `'`. --- src/DependencyInjection/GuidesExtension.php | 24 ++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/DependencyInjection/GuidesExtension.php b/src/DependencyInjection/GuidesExtension.php index 89904c0..6db4af6 100644 --- a/src/DependencyInjection/GuidesExtension.php +++ b/src/DependencyInjection/GuidesExtension.php @@ -42,6 +42,7 @@ use function is_int; use function is_string; use function pathinfo; +use function trim; use function var_export; final class GuidesExtension extends Extension implements CompilerPassInterface, ConfigurationInterface, PrependExtensionInterface @@ -68,12 +69,33 @@ static function ($value) { return var_export($value, true); } + if (is_string($value)) { + return trim($value, "'"); + } + + return $value; + }, + ) + ->end() + ->end() + ->scalarNode('release') + ->beforeNormalization() + ->always( + // We need to revert the phpize call in XmlUtils. Version is always a string! + static function ($value) { + if (!is_int($value) && !is_string($value)) { + return var_export($value, true); + } + + if (is_string($value)) { + return trim($value, "'"); + } + return $value; }, ) ->end() ->end() - ->scalarNode('release')->end() ->scalarNode('copyright')->end() ->end() ->end() From 47c9c1dd19294f16b7cdf3244bf4f6ffcc3d17ba Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 13 Sep 2024 22:06:59 +0200 Subject: [PATCH 35/53] Add correct documentation urls (cherry picked from commit 056260e5772d2ea23b8758ebbbd5241826f3cf0a) --- .github/pull_request_template.md | 4 ++-- CONTRIBUTING.rst | 4 ++-- README.rst | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 09fc97f..7447406 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,5 +14,5 @@ to this repository directly but always to the mono-repository linked above. # Make changes (create pull requests) -See the `Contribution chapter `__ in the -`Documentation` `__. +See the `Contribution chapter `__ in the +`Documentation` `__. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 799a791..63feae6 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -19,5 +19,5 @@ Create Issues Make changes (create pull requests) =================================== -See the `Contribution chapter `__ in the -`Documentation` `__. +See the `Contribution chapter `__ in the +`Documentation` `__. diff --git a/README.rst b/README.rst index 81b5ba7..3f2d433 100644 --- a/README.rst +++ b/README.rst @@ -1,13 +1,13 @@ -.. image:: http://poser.pugx.org/phpdocumentor/guides/require/php +.. image:: https://poser.pugx.org/phpdocumentor/guides/require/php :alt: PHP Version Require :target: https://packagist.org/packages/phpdocumentor/guides -.. image:: http://poser.pugx.org/phpdocumentor/guides/v/stable +.. image:: https://poser.pugx.org/phpdocumentor/guides/v/stable :alt: Latest Stable Version :target: https://packagist.org/packages/phpdocumentor/guides -.. image:: http://poser.pugx.org/phpdocumentor/guides/v/unstable +.. image:: https://poser.pugx.org/phpdocumentor/guides/v/unstable :alt: Latest Unstable Version :target: https://packagist.org/packages/phpdocumentor/guides @@ -31,7 +31,7 @@ The package `phpdocumentor/guides Date: Wed, 25 Sep 2024 22:28:54 +0200 Subject: [PATCH 36/53] [BUGFIX] allow ** to deep match all documents (cherry picked from commit f23cbdcbe89aa5033de2fcdbd63f3fe7fe3bcf85) --- .../MenuNodeTransformers/GlobMenuEntryNodeTransformer.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php index 16484f5..5ed5fec 100644 --- a/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/GlobMenuEntryNodeTransformer.php @@ -27,8 +27,9 @@ use function explode; use function implode; use function in_array; +use function is_string; use function preg_match; -use function str_replace; +use function preg_replace; final class GlobMenuEntryNodeTransformer extends AbstractMenuEntryNodeTransformer { @@ -137,7 +138,9 @@ private static function matches(string $actualFile, GlobMenuEntryNode $parsedMen private static function isGlob(string $documentEntryFile, string $currentPath, string $file, string $prefix, array $globExclude): bool { if (!in_array($documentEntryFile, $globExclude, true)) { - $file = str_replace('*', '[^\/]*', $file); + $file = preg_replace('/(? 0; From 21fe1f5a61cf914c2d4f85be238db61594e0269b Mon Sep 17 00:00:00 2001 From: Jaapio Date: Tue, 1 Oct 2024 22:22:30 +0200 Subject: [PATCH 37/53] [BUGFIX] use asset plugin to copy images fixes #1088 (cherry picked from commit c138df6bcea46a3cabec6a75423d23210ed54724) --- resources/template/html/inline/image.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/template/html/inline/image.html.twig b/resources/template/html/inline/image.html.twig index 85065a6..b933435 100644 --- a/resources/template/html/inline/image.html.twig +++ b/resources/template/html/inline/image.html.twig @@ -1 +1 @@ -{{- node.altText -}} +{{- node.altText -}} From 2db153eb7ad062215f10463b2ed096ed07e96838 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Wed, 2 Oct 2024 14:41:38 +0200 Subject: [PATCH 38/53] [BUGFIX] Reset section stack for the next document Resolves #1093 --- src/Compiler/NodeTransformers/SectionCreationTransformer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Compiler/NodeTransformers/SectionCreationTransformer.php b/src/Compiler/NodeTransformers/SectionCreationTransformer.php index dbda625..4bd481b 100644 --- a/src/Compiler/NodeTransformers/SectionCreationTransformer.php +++ b/src/Compiler/NodeTransformers/SectionCreationTransformer.php @@ -34,6 +34,10 @@ final class SectionCreationTransformer implements NodeTransformer public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { + if ($node instanceof DocumentNode) { + $this->sectionStack = []; + } + if (!$compilerContext->getShadowTree()->getParent()?->getNode() instanceof DocumentNode) { return $node; } From 6a9a7921792edbc2643f731aaaed758b29947c5f Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Wed, 2 Oct 2024 21:13:57 +0200 Subject: [PATCH 39/53] [FEATURE] Make index file configurable The index file will also be used in automatic menu building. As oposed to setting "input-file" not only the one file mentioned will be rendererd but all files from the directory. However the name of the input file can be changed. Some markdown projects like to change the name of the index file. For example powermail is using Readme.md as index file in every directory: https://github.com/in2code-de/powermail/tree/master/Documentation References #1108 (cherry picked from commit 34b41fed3583ba897f1f559d8c52acf59f9d1159) --- src/DependencyInjection/GuidesExtension.php | 5 +++++ src/Handlers/ParseDirectoryHandler.php | 25 +++++++++++++++------ src/Settings/ProjectSettings.php | 13 +++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/DependencyInjection/GuidesExtension.php b/src/DependencyInjection/GuidesExtension.php index 6db4af6..10d37c7 100644 --- a/src/DependencyInjection/GuidesExtension.php +++ b/src/DependencyInjection/GuidesExtension.php @@ -114,6 +114,7 @@ static function ($value) { ->scalarNode('theme')->end() ->scalarNode('input')->end() ->scalarNode('input_file')->end() + ->scalarNode('index_name')->end() ->scalarNode('output')->end() ->scalarNode('input_format')->end() ->arrayNode('output_format') @@ -237,6 +238,10 @@ public function load(array $configs, ContainerBuilder $container): void } } + if (isset($config['index_name']) && $config['index_name'] !== '') { + $projectSettings->setIndexName((string) $config['index_name']); + } + if (isset($config['output'])) { $projectSettings->setOutput((string) $config['output']); } diff --git a/src/Handlers/ParseDirectoryHandler.php b/src/Handlers/ParseDirectoryHandler.php index 4c70758..8851e4b 100644 --- a/src/Handlers/ParseDirectoryHandler.php +++ b/src/Handlers/ParseDirectoryHandler.php @@ -21,20 +21,28 @@ use phpDocumentor\Guides\Event\PreParseProcess; use phpDocumentor\Guides\FileCollector; use phpDocumentor\Guides\Nodes\DocumentNode; +use phpDocumentor\Guides\Settings\ProjectSettings; +use phpDocumentor\Guides\Settings\SettingsManager; use Psr\EventDispatcher\EventDispatcherInterface; +use function array_map; use function assert; +use function explode; +use function implode; use function sprintf; final class ParseDirectoryHandler { - private const INDEX_FILE_NAMES = ['index', 'Index']; + private SettingsManager $settingsManager; public function __construct( private readonly FileCollector $fileCollector, private readonly CommandBus $commandBus, private readonly EventDispatcherInterface $eventDispatcher, + SettingsManager|null $settingsManager = null, ) { + // if for backward compatibility reasons no settings manager was passed, use the defaults + $this->settingsManager = $settingsManager ?? new SettingsManager(new ProjectSettings()); } /** @return DocumentNode[] */ @@ -98,17 +106,20 @@ private function getDirectoryIndexFile( $hashedContentFromFilesystem[$itemFromFilesystem['basename']] = true; } - foreach (self::INDEX_FILE_NAMES as $indexName) { - $indexFilename = sprintf('%s.%s', $indexName, $extension); - if (isset($hashedContentFromFilesystem[$indexFilename])) { + $indexFileNames = array_map('trim', explode(',', $this->settingsManager->getProjectSettings()->getIndexName())); + + $indexNamesNotFound = []; + foreach ($indexFileNames as $indexName) { + $fullIndexFilename = sprintf('%s.%s', $indexName, $extension); + if (isset($hashedContentFromFilesystem[$fullIndexFilename])) { return $indexName; } - } - $indexFilename = sprintf('%s.%s', self::INDEX_FILE_NAMES[0], $extension); + $indexNamesNotFound[] = $fullIndexFilename; + } throw new InvalidArgumentException( - sprintf('Could not find index file "%s" in "%s"', $indexFilename, $directory), + sprintf('Could not find an index file "%s", expected file names: %s', $directory, implode(', ', $indexNamesNotFound)), ); } } diff --git a/src/Settings/ProjectSettings.php b/src/Settings/ProjectSettings.php index 84b5272..1256ac7 100644 --- a/src/Settings/ProjectSettings.php +++ b/src/Settings/ProjectSettings.php @@ -24,6 +24,7 @@ final class ProjectSettings private string $theme = 'default'; private string $input = 'docs'; private string $inputFile = ''; + private string $indexName = 'index,Index'; private string $output = 'output'; private string $inputFormat = 'rst'; /** @var string[] */ @@ -230,4 +231,16 @@ public function setIgnoredDomains(array $ignoredDomains): void { $this->ignoredDomains = $ignoredDomains; } + + public function getIndexName(): string + { + return $this->indexName; + } + + public function setIndexName(string $indexName): ProjectSettings + { + $this->indexName = $indexName; + + return $this; + } } From 2b042ef7c9f5f740b61f4bb5bdda1be07206db55 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Wed, 2 Oct 2024 14:12:43 +0200 Subject: [PATCH 40/53] [FEATURE] Handle image titles in Markdown Resolves #1095 (cherry picked from commit 593a27d3aa6916fc13c12fb93f0e5c873490c8e6) --- resources/template/html/inline/image.html.twig | 3 ++- src/Nodes/Inline/ImageInlineNode.php | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/resources/template/html/inline/image.html.twig b/resources/template/html/inline/image.html.twig index b933435..3da6db2 100644 --- a/resources/template/html/inline/image.html.twig +++ b/resources/template/html/inline/image.html.twig @@ -1 +1,2 @@ -{{- node.altText -}} +{{- node.altText -}} diff --git a/src/Nodes/Inline/ImageInlineNode.php b/src/Nodes/Inline/ImageInlineNode.php index ffd0c78..e44550b 100644 --- a/src/Nodes/Inline/ImageInlineNode.php +++ b/src/Nodes/Inline/ImageInlineNode.php @@ -20,8 +20,11 @@ final class ImageInlineNode extends InlineNode { public const TYPE = 'image'; - public function __construct(private readonly string $url, private readonly string $altText) - { + public function __construct( + private readonly string $url, + private readonly string $altText, + private readonly string|null $title = null, + ) { parent::__construct(self::TYPE, $url); } @@ -34,4 +37,9 @@ public function getAltText(): string { return $this->altText; } + + public function getTitle(): string + { + return $this->title ?? ''; + } } From 64432a5b35e25422590c4ba8cd0e12b23fd913a9 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Tue, 8 Oct 2024 22:58:23 +0200 Subject: [PATCH 41/53] [FEAT] add support for raw html sanitizers This feature introduces a new compiler pass that will allow us to sanitize the html in a raw directive. We can disable the full raw usage by setting `raw_node.escape` to `true` this will block all html and displays the raw node as normal paragraph. By default the raw directive is displayed as is and sanitized to safe html, when raw is defined as html content ``` .. raw:: html

    html here

    ``` if the raw language is different than html we no nothing for now. This pr also allows the users to define there own html sanitizer from the configuration. Example can be found in the included test. (cherry picked from commit ed886fee43522e8f07d1afbd22c80d2fdb74f424) # Conflicts: # composer.lock # packages/guides/composer.json --- composer.json | 3 +- resources/config/guides.php | 10 ++- .../RawNodeEscapeTransformer.php | 68 ++++++++++++++++ src/DependencyInjection/GuidesExtension.php | 79 +++++++++++++++++++ src/Nodes/RawNode.php | 6 ++ 5 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php diff --git a/composer.json b/composer.json index ee566c1..77fb628 100644 --- a/composer.json +++ b/composer.json @@ -31,9 +31,10 @@ "phpdocumentor/flyfinder": "^1.1", "psr/event-dispatcher": "^1.0", "symfony/clock": "^6.4.3", + "symfony/html-sanitizer": "^6.4.8", + "symfony/http-client": "^6.4.9", "symfony/string": "^5.4 || ^6.3 || ^7.0", "symfony/translation-contracts": "^3.4.1", - "symfony/http-client": "^6.4.4", "twig/twig": "~2.15 || ^3.0", "webmozart/assert": "^1.11" }, diff --git a/resources/config/guides.php b/resources/config/guides.php index 50eee87..549348e 100644 --- a/resources/config/guides.php +++ b/resources/config/guides.php @@ -10,6 +10,7 @@ use phpDocumentor\Guides\Compiler\NodeTransformers\CustomNodeTransformerFactory; use phpDocumentor\Guides\Compiler\NodeTransformers\MenuNodeTransformers\InternalMenuEntryNodeTransformer; use phpDocumentor\Guides\Compiler\NodeTransformers\NodeTransformerFactory; +use phpDocumentor\Guides\Compiler\NodeTransformers\RawNodeEscapeTransformer; use phpDocumentor\Guides\Event\PostProjectNodeCreated; use phpDocumentor\Guides\EventListener\LoadSettingsFromComposer; use phpDocumentor\Guides\NodeRenderers\Html\BreadCrumbNodeRenderer; @@ -62,6 +63,7 @@ use phpDocumentor\Guides\Twig\TwigTemplateRenderer; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; use Twig\Loader\FilesystemLoader; @@ -105,6 +107,9 @@ ->set(InternalMenuEntryNodeTransformer::class) ->tag('phpdoc.guides.compiler.nodeTransformers') + ->set(RawNodeEscapeTransformer::class) + ->arg('$escapeRawNodes', param('phpdoc.guides.raw_node.escape')) + ->arg('$htmlSanitizerConfig', service('phpdoc.guides.raw_node.sanitizer.default')) ->set(AbsoluteUrlGenerator::class) ->set(RelativeUrlGenerator::class) @@ -244,5 +249,8 @@ ->arg('$themeManager', service(ThemeManager::class)) ->set(TemplateRenderer::class, TwigTemplateRenderer::class) - ->arg('$environmentBuilder', new Reference(EnvironmentBuilder::class)); + ->arg('$environmentBuilder', new Reference(EnvironmentBuilder::class)) + + ->set('phpdoc.guides.raw_node.sanitizer.default', HtmlSanitizerConfig::class) + ->call('allowSafeElements', [], true); }; diff --git a/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php b/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php new file mode 100644 index 0000000..0da9440 --- /dev/null +++ b/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php @@ -0,0 +1,68 @@ +htmlSanitizer = new HtmlSanitizer($htmlSanitizerConfig); + } + + public function enterNode(Node $node, CompilerContext $compilerContext): Node + { + return $node; + } + + public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + { + if ($this->escapeRawNodes) { + $this->logger->warning('We do not support plain HTML for security reasons. Escaping all HTML '); + + return new ParagraphNode([new InlineCompoundNode([new PlainTextInlineNode($node->getValue())])]); + } + + if ($node->getOption('format', 'html') === 'html') { + return new RawNode($this->htmlSanitizer->sanitize($node->getValue())); + } + + return $node; + } + + public function supports(Node $node): bool + { + return $node instanceof RawNode; + } + + public function getPriority(): int + { + return 1000; + } +} diff --git a/src/DependencyInjection/GuidesExtension.php b/src/DependencyInjection/GuidesExtension.php index 10d37c7..61c333f 100644 --- a/src/DependencyInjection/GuidesExtension.php +++ b/src/DependencyInjection/GuidesExtension.php @@ -13,6 +13,7 @@ namespace phpDocumentor\Guides\DependencyInjection; +use phpDocumentor\Guides\Compiler\NodeTransformers\RawNodeEscapeTransformer; use phpDocumentor\Guides\DependencyInjection\Compiler\NodeRendererPass; use phpDocumentor\Guides\DependencyInjection\Compiler\ParserRulesPass; use phpDocumentor\Guides\DependencyInjection\Compiler\RendererPass; @@ -31,6 +32,8 @@ use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; use function array_keys; use function array_map; @@ -162,6 +165,33 @@ static function ($value) { ->end() ->end() ->end() + ->arrayNode('raw_node') + ->fixXmlConfig('sanitizer') + ->children() + ->booleanNode('escape')->defaultValue(false)->end() + ->scalarNode('sanitizer_name')->end() + ->arrayNode('sanitizers') + ->defaultValue([]) + ->arrayPrototype() + ->fixXmlConfig('allow_element') + ->fixXmlConfig('drop_element') + ->fixXmlConfig('block_element') + ->fixXmlConfig('allow_attribute') + ->fixXmlConfig('drop_attribute') + ->children() + ->scalarNode('name')->isRequired()->end() + ->booleanNode('allow_safe_elements')->defaultValue(true)->end() + ->booleanNode('allow_static_elements')->defaultValue(true)->end() + ->arrayNode('allow_elements')->scalarPrototype()->end()->end() + ->arrayNode('block_elements')->scalarPrototype()->end()->end() + ->arrayNode('drop_elements')->scalarPrototype()->end()->end() + ->arrayNode('allow_attributes')->scalarPrototype()->end()->end() + ->arrayNode('drop_attributes')->scalarPrototype()->end()->end() + ->end() + ->end() + ->end() + ->end() + ->end() ->scalarNode('default_code_language')->defaultValue('')->end() ->arrayNode('themes') ->defaultValue([]) @@ -290,6 +320,11 @@ public function load(array $configs, ContainerBuilder $container): void $container->setParameter('phpdoc.guides.base_template_paths', $config['base_template_paths']); $container->setParameter('phpdoc.guides.node_templates', $config['templates']); $container->setParameter('phpdoc.guides.inventories', $config['inventories']); + $container->setParameter('phpdoc.guides.raw_node.escape', $config['raw_node']['escape'] ?? false); + + if ($config['raw_node'] ?? false) { + $this->configureSanitizers($config['raw_node'], $container); + } foreach ($config['themes'] as $themeName => $themeConfig) { $container->getDefinition(ThemeManager::class) @@ -328,6 +363,50 @@ public function prepend(ContainerBuilder $container): void ], ); } + + /** @param array $rawNodeConfig */ + private function configureSanitizers(array $rawNodeConfig, ContainerBuilder $container): void + { + if ($rawNodeConfig['sanitizer_name'] ?? false) { + $container->getDefinition(RawNodeEscapeTransformer::class) + ->setArgument('$htmlSanitizerConfig', new Reference('phpdoc.guides.raw_node.sanitizer.' . $rawNodeConfig['sanitizer_name'])); + } + + foreach ($rawNodeConfig['sanitizers'] as $sanitizerConfig) { + $def = $container->register('phpdoc.guides.raw_node.sanitizer.' . $sanitizerConfig['name'], HtmlSanitizerConfig::class); + + // Base + if ($sanitizerConfig['allow_safe_elements']) { + $def->addMethodCall('allowSafeElements', [], true); + } + + if ($sanitizerConfig['allow_static_elements']) { + $def->addMethodCall('allowStaticElements', [], true); + } + + // Configures elements + foreach ($sanitizerConfig['allow_elements'] as $element => $attributes) { + $def->addMethodCall('allowElement', [$element, $attributes], true); + } + + foreach ($sanitizerConfig['block_elements'] as $element) { + $def->addMethodCall('blockElement', [$element], true); + } + + foreach ($sanitizerConfig['drop_elements'] as $element) { + $def->addMethodCall('dropElement', [$element], true); + } + + // Configures attributes + foreach ($sanitizerConfig['allow_attributes'] as $attribute => $elements) { + $def->addMethodCall('allowAttribute', [$attribute, $elements], true); + } + + foreach ($sanitizerConfig['drop_attributes'] as $attribute => $elements) { + $def->addMethodCall('dropAttribute', [$attribute, $elements], true); + } + } + } } /** diff --git a/src/Nodes/RawNode.php b/src/Nodes/RawNode.php index c55a4c1..74fc82e 100644 --- a/src/Nodes/RawNode.php +++ b/src/Nodes/RawNode.php @@ -15,4 +15,10 @@ final class RawNode extends TextNode { + public function __construct(string $contents, string $format = 'html') + { + parent::__construct($contents); + + $this->options['format'] = $format; + } } From 41ebe39cdf80b768c5d1b05888cebc84273e83da Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Fri, 11 Oct 2024 13:13:52 +0200 Subject: [PATCH 42/53] [TASK] Fix phpstan issues (cherry picked from commit 7378a6225a382a7ee4489d5d659666a1ddd5ea0f) --- src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php | 4 ++++ src/DependencyInjection/GuidesExtension.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php b/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php index 0da9440..a04f5d0 100644 --- a/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php +++ b/src/Compiler/NodeTransformers/RawNodeEscapeTransformer.php @@ -24,6 +24,9 @@ use Symfony\Component\HtmlSanitizer\HtmlSanitizer; use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; +use function assert; + +/** @implements NodeTransformer */ final class RawNodeEscapeTransformer implements NodeTransformer { private HtmlSanitizer $htmlSanitizer; @@ -43,6 +46,7 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null { + assert($node instanceof RawNode); if ($this->escapeRawNodes) { $this->logger->warning('We do not support plain HTML for security reasons. Escaping all HTML '); diff --git a/src/DependencyInjection/GuidesExtension.php b/src/DependencyInjection/GuidesExtension.php index 61c333f..35c707e 100644 --- a/src/DependencyInjection/GuidesExtension.php +++ b/src/DependencyInjection/GuidesExtension.php @@ -372,6 +372,10 @@ private function configureSanitizers(array $rawNodeConfig, ContainerBuilder $con ->setArgument('$htmlSanitizerConfig', new Reference('phpdoc.guides.raw_node.sanitizer.' . $rawNodeConfig['sanitizer_name'])); } + if (!is_array($rawNodeConfig['sanitizers'] ?? false)) { + return; + } + foreach ($rawNodeConfig['sanitizers'] as $sanitizerConfig) { $def = $container->register('phpdoc.guides.raw_node.sanitizer.' . $sanitizerConfig['name'], HtmlSanitizerConfig::class); From 3c12228b028f6e75f592e3fed62b7468e084392b Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Thu, 3 Oct 2024 09:22:15 +0200 Subject: [PATCH 43/53] [FEATURE] Add option to create an automatic menu For now all Document entries are added to the root document entry. In a follow up I plan to create a nested menu as well References #1108 (cherry picked from commit 59d75157c80c57c88c6c65c8e74eada4cce71202) --- src/Compiler/Passes/AutomaticMenuPass.php | 63 +++++++++++++++++++ src/Compiler/Passes/GlobalMenuPass.php | 17 +++++ src/Compiler/Passes/ToctreeValidationPass.php | 11 ++++ src/DependencyInjection/GuidesExtension.php | 5 ++ src/Settings/ProjectSettings.php | 13 ++++ 5 files changed, 109 insertions(+) create mode 100644 src/Compiler/Passes/AutomaticMenuPass.php diff --git a/src/Compiler/Passes/AutomaticMenuPass.php b/src/Compiler/Passes/AutomaticMenuPass.php new file mode 100644 index 0000000..8dee004 --- /dev/null +++ b/src/Compiler/Passes/AutomaticMenuPass.php @@ -0,0 +1,63 @@ +settingsManager->getProjectSettings()->isAutomaticMenu()) { + return $documents; + } + + $projectNode = $compilerContext->getProjectNode(); + $rootDocumentEntry = $projectNode->getRootDocumentEntry(); + foreach ($documents as $documentNode) { + if ($documentNode->isOrphan()) { + // Do not add orphans to the automatic menu + continue; + } + + if ($documentNode->isRoot()) { + continue; + } + + $documentEntry = $projectNode->getDocumentEntry($documentNode->getFilePath()); + $documentEntry->setParent($rootDocumentEntry); + $rootDocumentEntry->addChild($documentEntry); + } + + return $documents; + } +} diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index b1891d7..11bbd1c 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -30,6 +30,7 @@ use function array_map; use function assert; +use function count; use const PHP_INT_MAX; @@ -78,11 +79,27 @@ public function run(array $documents, CompilerContextInterface $compilerContext) $menuNodes[] = $menuNode->withCaption($tocNode->getCaption()); } + if ($this->settingsManager->getProjectSettings()->isAutomaticMenu() && count($menuNodes) === 0) { + $menuNodes[] = $this->getNavMenuNodeFromDocumentEntries($compilerContext); + } + $projectNode->setGlobalMenues($menuNodes); return $documents; } + private function getNavMenuNodeFromDocumentEntries(CompilerContextInterface $compilerContext): NavMenuNode + { + $menuEntries = []; + $rootDocumentEntry = $compilerContext->getProjectNode()->getRootDocumentEntry(); + foreach ($rootDocumentEntry->getChildren() as $documentEntryNode) { + $newMenuEntry = new InternalMenuEntryNode($documentEntryNode->getFile(), $documentEntryNode->getTitle(), [], false, 1); + $menuEntries[] = $newMenuEntry; + } + + return new NavMenuNode($menuEntries); + } + private function getNavMenuNodefromTocNode(CompilerContextInterface $compilerContext, TocNode $tocNode, string|null $menuType = null): NavMenuNode { $self = $this; diff --git a/src/Compiler/Passes/ToctreeValidationPass.php b/src/Compiler/Passes/ToctreeValidationPass.php index 208f40e..75a69bb 100644 --- a/src/Compiler/Passes/ToctreeValidationPass.php +++ b/src/Compiler/Passes/ToctreeValidationPass.php @@ -18,13 +18,20 @@ use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Nodes\DocumentTree\DocumentEntryNode; use phpDocumentor\Guides\Nodes\ProjectNode; +use phpDocumentor\Guides\Settings\ProjectSettings; +use phpDocumentor\Guides\Settings\SettingsManager; use Psr\Log\LoggerInterface; final class ToctreeValidationPass implements CompilerPass { + private SettingsManager $settingsManager; + public function __construct( private readonly LoggerInterface $logger, + SettingsManager|null $settingsManager = null, ) { + // if for backward compatibility reasons no settings manager was passed, use the defaults + $this->settingsManager = $settingsManager ?? new SettingsManager(new ProjectSettings()); } public function getPriority(): int @@ -39,6 +46,10 @@ public function getPriority(): int */ public function run(array $documents, CompilerContextInterface $compilerContext): array { + if ($this->settingsManager->getProjectSettings()->isAutomaticMenu()) { + return $documents; + } + $projectNode = $compilerContext->getProjectNode(); foreach ($documents as $document) { diff --git a/src/DependencyInjection/GuidesExtension.php b/src/DependencyInjection/GuidesExtension.php index 35c707e..52e61a8 100644 --- a/src/DependencyInjection/GuidesExtension.php +++ b/src/DependencyInjection/GuidesExtension.php @@ -146,6 +146,7 @@ static function ($value) { ->scalarNode('show_progress')->end() ->scalarNode('links_are_relative')->end() ->scalarNode('max_menu_depth')->end() + ->scalarNode('automatic_menu')->end() ->arrayNode('base_template_paths') ->defaultValue([]) ->scalarPrototype()->end() @@ -308,6 +309,10 @@ public function load(array $configs, ContainerBuilder $container): void $projectSettings->setMaxMenuDepth((int) $config['max_menu_depth']); } + if (isset($config['automatic_menu'])) { + $projectSettings->setAutomaticMenu((bool) $config['automatic_menu']); + } + if (isset($config['default_code_language'])) { $projectSettings->setDefaultCodeLanguage((string) $config['default_code_language']); } diff --git a/src/Settings/ProjectSettings.php b/src/Settings/ProjectSettings.php index 1256ac7..08bdfd3 100644 --- a/src/Settings/ProjectSettings.php +++ b/src/Settings/ProjectSettings.php @@ -35,6 +35,7 @@ final class ProjectSettings private bool $linksRelative = false; private string $defaultCodeLanguage = ''; private int $maxMenuDepth = 0; + private bool $automaticMenu = false; /** @var string[] */ private array $ignoredDomains = []; @@ -243,4 +244,16 @@ public function setIndexName(string $indexName): ProjectSettings return $this; } + + public function isAutomaticMenu(): bool + { + return $this->automaticMenu; + } + + public function setAutomaticMenu(bool $automaticMenu): ProjectSettings + { + $this->automaticMenu = $automaticMenu; + + return $this; + } } From 647f7353daafa709ccb1a4ad31fe1276834263f3 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Fri, 4 Oct 2024 05:55:57 +0200 Subject: [PATCH 44/53] [TASK] Replace TocNode in automatic-menu node --- .../TocNodeReplacementTransformer.php | 72 +++++++++++++++++++ src/Compiler/Passes/GlobalMenuPass.php | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeReplacementTransformer.php diff --git a/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeReplacementTransformer.php b/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeReplacementTransformer.php new file mode 100644 index 0000000..0063b21 --- /dev/null +++ b/src/Compiler/NodeTransformers/MenuNodeTransformers/TocNodeReplacementTransformer.php @@ -0,0 +1,72 @@ + */ +final class TocNodeReplacementTransformer implements NodeTransformer +{ + public function __construct( + private readonly LoggerInterface $logger, + private readonly SettingsManager $settingsManager, + ) { + } + + public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node + { + return $node; + } + + public function leaveNode(Node $node, CompilerContextInterface $compilerContext): Node|null + { + if (!$node instanceof TocNode) { + return $node; + } + + if (!$this->settingsManager->getProjectSettings()->isAutomaticMenu()) { + return $node; + } + + if ($node->hasOption('hidden')) { + $this->logger->warning('The `.. toctree::` directive with option `:hidden:` is not supported in automatic-menu mode. ', $compilerContext->getLoggerInformation()); + + return null; + } + + $this->logger->warning('The `.. toctree::` directive is not supported in automatic-menu mode. Use `.. menu::` instead. ', $compilerContext->getLoggerInformation()); + $menuNode = new NavMenuNode($node->getMenuEntries()); + $menuNode = $menuNode->withOptions($node->getOptions()); + $menuNode = $menuNode->withCaption($node->getCaption()); + + return $menuNode; + } + + public function supports(Node $node): bool + { + return $node instanceof TocNode; + } + + public function getPriority(): int + { + return 20_000; + } +} diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index 11bbd1c..13ec507 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -57,7 +57,7 @@ public function run(array $documents, CompilerContextInterface $compilerContext) try { $rootDocumentEntry = $projectNode->getRootDocumentEntry(); } catch (Throwable) { - // Todo: Functional tests have not root document entry + // Todo: Functional tests have no root document entry return $documents; } From 92410896f018cbb5241a8e23b756cbfb634856e3 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Fri, 11 Oct 2024 14:10:54 +0200 Subject: [PATCH 45/53] [FEATURE] Support automatically created multi-level menus Resolves https://github.com/phpDocumentor/guides/issues/1108 (cherry picked from commit f030169c1b577150f5541c2540f144844b706d67) --- src/Compiler/Passes/AutomaticMenuPass.php | 55 +++++++++++++++++++++-- src/Compiler/Passes/GlobalMenuPass.php | 15 +++++-- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Passes/AutomaticMenuPass.php b/src/Compiler/Passes/AutomaticMenuPass.php index 8dee004..0775786 100644 --- a/src/Compiler/Passes/AutomaticMenuPass.php +++ b/src/Compiler/Passes/AutomaticMenuPass.php @@ -17,11 +17,20 @@ use phpDocumentor\Guides\Compiler\CompilerPass; use phpDocumentor\Guides\Nodes\DocumentNode; use phpDocumentor\Guides\Settings\SettingsManager; +use Psr\Log\LoggerInterface; + +use function array_pop; +use function count; +use function explode; +use function implode; +use function in_array; +use function sprintf; final class AutomaticMenuPass implements CompilerPass { public function __construct( private readonly SettingsManager $settingsManager, + private readonly LoggerInterface|null $logger = null, ) { } @@ -43,6 +52,7 @@ public function run(array $documents, CompilerContextInterface $compilerContext) $projectNode = $compilerContext->getProjectNode(); $rootDocumentEntry = $projectNode->getRootDocumentEntry(); + $indexNames = explode(',', $this->settingsManager->getProjectSettings()->getIndexName()); foreach ($documents as $documentNode) { if ($documentNode->isOrphan()) { // Do not add orphans to the automatic menu @@ -53,9 +63,48 @@ public function run(array $documents, CompilerContextInterface $compilerContext) continue; } - $documentEntry = $projectNode->getDocumentEntry($documentNode->getFilePath()); - $documentEntry->setParent($rootDocumentEntry); - $rootDocumentEntry->addChild($documentEntry); + $filePath = $documentNode->getFilePath(); + $pathParts = explode('/', $filePath); + $documentEntry = $projectNode->getDocumentEntry($filePath); + if (count($pathParts) === 1 || count($pathParts) === 2 && in_array($pathParts[1], $indexNames, true)) { + $documentEntry->setParent($rootDocumentEntry); + $rootDocumentEntry->addChild($documentEntry); + continue; + } + + $fileName = array_pop($pathParts); + $path = implode('/', $pathParts); + if (in_array($fileName, $indexNames, true)) { + array_pop($pathParts); + $path = implode('/', $pathParts); + } + + $parentFound = false; + foreach ($indexNames as $indexName) { + $indexFile = $path . '/' . $indexName; + $parentEntry = $projectNode->findDocumentEntry($indexFile); + if ($parentEntry === null) { + continue; + } + + $documentEntry->setParent($parentEntry); + $parentEntry->addChild($documentEntry); + $parentFound = true; + break; + } + + if ($parentFound) { + continue; + } + + $parentEntry = $projectNode->findDocumentEntry($path); + if ($parentEntry === null) { + $this->logger?->warning(sprintf('No parent found for file "%s/%s" attaching it to the document root instead. ', $path, $fileName)); + continue; + } + + $documentEntry->setParent($parentEntry); + $parentEntry->addChild($documentEntry); } return $documents; diff --git a/src/Compiler/Passes/GlobalMenuPass.php b/src/Compiler/Passes/GlobalMenuPass.php index 13ec507..bdbabff 100644 --- a/src/Compiler/Passes/GlobalMenuPass.php +++ b/src/Compiler/Passes/GlobalMenuPass.php @@ -90,14 +90,23 @@ public function run(array $documents, CompilerContextInterface $compilerContext) private function getNavMenuNodeFromDocumentEntries(CompilerContextInterface $compilerContext): NavMenuNode { - $menuEntries = []; $rootDocumentEntry = $compilerContext->getProjectNode()->getRootDocumentEntry(); + $menuEntries = $this->getMenuEntriesFromDocumentEntries($rootDocumentEntry); + + return new NavMenuNode($menuEntries); + } + + /** @return InternalMenuEntryNode[] */ + public function getMenuEntriesFromDocumentEntries(DocumentEntryNode $rootDocumentEntry): array + { + $menuEntries = []; foreach ($rootDocumentEntry->getChildren() as $documentEntryNode) { - $newMenuEntry = new InternalMenuEntryNode($documentEntryNode->getFile(), $documentEntryNode->getTitle(), [], false, 1); + $children = $this->getMenuEntriesFromDocumentEntries($documentEntryNode); + $newMenuEntry = new InternalMenuEntryNode($documentEntryNode->getFile(), $documentEntryNode->getTitle(), $children, false, 1); $menuEntries[] = $newMenuEntry; } - return new NavMenuNode($menuEntries); + return $menuEntries; } private function getNavMenuNodefromTocNode(CompilerContextInterface $compilerContext, TocNode $tocNode, string|null $menuType = null): NavMenuNode From 82f20e14ee10144fa67d16a7fdf86aea70f988b1 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 11 Oct 2024 16:54:23 +0200 Subject: [PATCH 46/53] [FIX] use canonicalUrl to fetch images (cherry picked from commit ffb85cddd0b25ad10fde369d14e597fca11570e1) --- src/Twig/AssetsExtension.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Twig/AssetsExtension.php b/src/Twig/AssetsExtension.php index 16393d1..1bcb3fe 100644 --- a/src/Twig/AssetsExtension.php +++ b/src/Twig/AssetsExtension.php @@ -156,25 +156,27 @@ private function copyAsset( } $canonicalUrl = $this->documentNameResolver->canonicalUrl($renderContext->getDirName(), $sourcePath); + $normalizedSourcePath = $this->documentNameResolver->canonicalUrl($renderContext->getDirName(), $sourcePath); + $outputPath = $this->documentNameResolver->absoluteUrl( $renderContext->getDestinationPath(), $canonicalUrl, ); try { - if ($renderContext->getOrigin()->has($sourcePath) === false) { + if ($renderContext->getOrigin()->has($normalizedSourcePath) === false) { $this->logger->error( - sprintf('Image reference not found "%s"', $sourcePath), + sprintf('Image reference not found "%s"', $normalizedSourcePath), $renderContext->getLoggerInformation(), ); return $outputPath; } - $fileContents = $renderContext->getOrigin()->read($sourcePath); + $fileContents = $renderContext->getOrigin()->read($normalizedSourcePath); if ($fileContents === false) { $this->logger->error( - sprintf('Could not read image file "%s"', $sourcePath), + sprintf('Could not read image file "%s"', $normalizedSourcePath), $renderContext->getLoggerInformation(), ); From b8ae7667042ad2c106999598089681fa16900700 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Fri, 11 Oct 2024 15:31:31 +0200 Subject: [PATCH 47/53] [FEATURE] Support GitHub like Markdown Admonitions See https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts Resolves https://github.com/phpDocumentor/guides/issues/1089 (cherry picked from commit b1200a199cbb32db288ae0f2c179d12f2a6ebee9) --- src/Nodes/AdmonitionNode.php | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/Nodes/AdmonitionNode.php diff --git a/src/Nodes/AdmonitionNode.php b/src/Nodes/AdmonitionNode.php new file mode 100644 index 0000000..2373e13 --- /dev/null +++ b/src/Nodes/AdmonitionNode.php @@ -0,0 +1,44 @@ + */ +final class AdmonitionNode extends CompoundNode +{ + /** @param Node[] $value */ + public function __construct(private readonly string $name, private readonly InlineCompoundNode|null $title, private readonly string $text, array $value, private readonly bool $isTitled = false) + { + parent::__construct($value); + } + + public function getName(): string + { + return $this->name; + } + + public function getTitle(): InlineCompoundNode|null + { + return $this->title; + } + + public function getText(): string + { + return $this->text; + } + + public function isTitled(): bool + { + return $this->isTitled; + } +} From e2021e4a0e9f2945d899db418008a2b8388c0357 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Fri, 11 Oct 2024 16:31:01 +0200 Subject: [PATCH 48/53] [FEATURE] Support automatically created multi-level menus Resolves https://github.com/phpDocumentor/guides/issues/1108 (cherry picked from commit 17a39976b87aa89d35c397a81cc28bc9465cc215) --- .../Html/AdmonitionNodeRenderer.php | 59 +++++++++++++++++++ src/Nodes/AdmonitionNode.php | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/NodeRenderers/Html/AdmonitionNodeRenderer.php diff --git a/src/NodeRenderers/Html/AdmonitionNodeRenderer.php b/src/NodeRenderers/Html/AdmonitionNodeRenderer.php new file mode 100644 index 0000000..4deb5bb --- /dev/null +++ b/src/NodeRenderers/Html/AdmonitionNodeRenderer.php @@ -0,0 +1,59 @@ + */ +class AdmonitionNodeRenderer implements NodeRenderer +{ + public function __construct(private readonly TemplateRenderer $renderer) + { + } + + public function supports(string $nodeFqcn): bool + { + return $nodeFqcn === AdmonitionNode::class || is_a($nodeFqcn, AdmonitionNode::class, true); + } + + public function render(Node $node, RenderContext $renderContext): string + { + if ($node instanceof AdmonitionNode === false) { + throw new InvalidArgumentException('Node must be an instance of ' . AdmonitionNode::class); + } + + $classes = $node->getClasses(); + + return $this->renderer->renderTemplate( + $renderContext, + 'body/admonition.html.twig', + [ + 'name' => $node->getName(), + 'text' => $node->getText(), + 'title' => $node->getTitle(), + 'isTitled' => $node->isTitled(), + 'class' => implode(' ', $classes), + 'node' => $node->getValue(), + ], + ); + } +} diff --git a/src/Nodes/AdmonitionNode.php b/src/Nodes/AdmonitionNode.php index 2373e13..26e03fe 100644 --- a/src/Nodes/AdmonitionNode.php +++ b/src/Nodes/AdmonitionNode.php @@ -14,7 +14,7 @@ namespace phpDocumentor\Guides\Nodes; /** @extends CompoundNode */ -final class AdmonitionNode extends CompoundNode +class AdmonitionNode extends CompoundNode { /** @param Node[] $value */ public function __construct(private readonly string $name, private readonly InlineCompoundNode|null $title, private readonly string $text, array $value, private readonly bool $isTitled = false) From 47944925825c2d05ca5a7497d85e780b23244add Mon Sep 17 00:00:00 2001 From: Jaapio Date: Wed, 16 Oct 2024 22:25:03 +0200 Subject: [PATCH 49/53] [FEAT] Allow post parse mutations We should allow post parse events to change the document. (cherry picked from commit 441b1e0f8dfe0eb0a61ec303725add23210a0550) --- src/Event/PostParseDocument.php | 17 +++++++++++++++-- src/Handlers/ParseFileHandler.php | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Event/PostParseDocument.php b/src/Event/PostParseDocument.php index f252473..017b851 100644 --- a/src/Event/PostParseDocument.php +++ b/src/Event/PostParseDocument.php @@ -22,8 +22,11 @@ */ final class PostParseDocument { - public function __construct(private readonly string $fileName, private readonly DocumentNode|null $documentNode) - { + public function __construct( + private readonly string $fileName, + private DocumentNode|null $documentNode, + private readonly string $originalFile, + ) { } public function getDocumentNode(): DocumentNode|null @@ -31,8 +34,18 @@ public function getDocumentNode(): DocumentNode|null return $this->documentNode; } + public function setDocumentNode(DocumentNode|null $documentNode): void + { + $this->documentNode = $documentNode; + } + public function getFileName(): string { return $this->fileName; } + + public function getOriginalFileName(): string + { + return $this->originalFile; + } } diff --git a/src/Handlers/ParseFileHandler.php b/src/Handlers/ParseFileHandler.php index 548f119..f68262c 100644 --- a/src/Handlers/ParseFileHandler.php +++ b/src/Handlers/ParseFileHandler.php @@ -103,7 +103,7 @@ private function createDocument( ); } - $event = $this->eventDispatcher->dispatch(new PostParseDocument($fileName, $document)); + $event = $this->eventDispatcher->dispatch(new PostParseDocument($fileName, $document, $path)); assert($event instanceof PostParseDocument); return $event->getDocumentNode(); From 529c19d60e25882e7cc07189bd648ff7fcf80056 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 25 Oct 2024 17:25:10 +0200 Subject: [PATCH 50/53] Markdown supports more nesting on nodes than RST does. To support this we needed to add support for compound nodes. We tried to be backward compatible for people building their own template. This layer will be removed in v2 and therfor we do trigger deprecations. See #1161 Fixes: #1160 (cherry picked from commit 87f3bfb18fe877d50d064c9acf0035560d60d508) --- .../template/html/inline/emphasis.html.twig | 6 ++- resources/template/html/inline/link.html.twig | 8 ++- .../template/html/inline/strong.html.twig | 6 ++- resources/template/html/template.php | 3 +- src/Nodes/Inline/AbstractLinkInlineNode.php | 32 ++++++++++-- src/Nodes/Inline/BCInlineNodeBehavior.php | 50 +++++++++++++++++++ src/Nodes/Inline/EmphasisInlineNode.php | 26 ++++++++-- src/Nodes/Inline/HyperLinkNode.php | 5 +- src/Nodes/Inline/InlineNode.php | 2 +- src/Nodes/Inline/InlineNodeInterface.php | 23 +++++++++ src/Nodes/Inline/StrongInlineNode.php | 26 ++++++++-- src/Nodes/InlineCompoundNode.php | 11 ++-- 12 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 src/Nodes/Inline/BCInlineNodeBehavior.php create mode 100644 src/Nodes/Inline/InlineNodeInterface.php diff --git a/resources/template/html/inline/emphasis.html.twig b/resources/template/html/inline/emphasis.html.twig index eca92aa..1708213 100644 --- a/resources/template/html/inline/emphasis.html.twig +++ b/resources/template/html/inline/emphasis.html.twig @@ -1 +1,5 @@ -{{- node.value -}} + + {%- for child in node.children -%} + {{- renderNode(child) -}} + {%- endfor -%} + diff --git a/resources/template/html/inline/link.html.twig b/resources/template/html/inline/link.html.twig index f92d44f..a6db95c 100644 --- a/resources/template/html/inline/link.html.twig +++ b/resources/template/html/inline/link.html.twig @@ -3,8 +3,12 @@ {%- for key, value in attributes %} {{- key -}}="{{- value -}}"{% endfor -%} {%- if node.classes %} class="{{ node.classesString }}"{% endif -%} > - {{- node.value -}} + {%- for child in node.children -%} + {{- renderNode(child) -}} + {%- endfor -%} {%- else -%} - {{- node.value -}} + {%- for child in node.children -%} + {{- renderNode(child) -}} + {%- endfor -%} {%- endif -%} diff --git a/resources/template/html/inline/strong.html.twig b/resources/template/html/inline/strong.html.twig index 936f48e..77a6bc0 100644 --- a/resources/template/html/inline/strong.html.twig +++ b/resources/template/html/inline/strong.html.twig @@ -1 +1,5 @@ -{{- node.value -}} + +{%- for child in node.children -%} + {{- renderNode(child) -}} +{%- endfor -%} + diff --git a/resources/template/html/template.php b/resources/template/html/template.php index 21f5e1c..89da801 100644 --- a/resources/template/html/template.php +++ b/resources/template/html/template.php @@ -83,7 +83,6 @@ EmbeddedFrame::class => 'body/embedded-frame.html.twig', // Inline ImageInlineNode::class => 'inline/image.html.twig', - InlineCompoundNode::class => 'inline/inline-node.html.twig', AbbreviationInlineNode::class => 'inline/textroles/abbreviation.html.twig', CitationInlineNode::class => 'inline/citation.html.twig', DocReferenceNode::class => 'inline/doc.html.twig', @@ -98,6 +97,8 @@ StrongInlineNode::class => 'inline/strong.html.twig', VariableInlineNode::class => 'inline/variable.html.twig', GenericTextRoleInlineNode::class => 'inline/textroles/generic.html.twig', + InlineCompoundNode::class => 'inline/inline-node.html.twig', + // Output as Metatags AuthorNode::class => 'structure/header/author.html.twig', CopyrightNode::class => 'structure/header/copyright.html.twig', diff --git a/src/Nodes/Inline/AbstractLinkInlineNode.php b/src/Nodes/Inline/AbstractLinkInlineNode.php index 3e1aa3d..5732d4e 100644 --- a/src/Nodes/Inline/AbstractLinkInlineNode.php +++ b/src/Nodes/Inline/AbstractLinkInlineNode.php @@ -13,13 +13,32 @@ namespace phpDocumentor\Guides\Nodes\Inline; -abstract class AbstractLinkInlineNode extends InlineNode implements LinkInlineNode +use Doctrine\Deprecations\Deprecation; +use phpDocumentor\Guides\Nodes\InlineCompoundNode; + +abstract class AbstractLinkInlineNode extends InlineCompoundNode implements LinkInlineNode { + use BCInlineNodeBehavior; + private string $url = ''; - public function __construct(string $type, private readonly string $targetReference, string $value = '') - { - parent::__construct($type, $value); + /** @param InlineNodeInterface[] $children */ + public function __construct( + private readonly string $type, + private readonly string $targetReference, + string $value = '', + array $children = [], + ) { + if (empty($children)) { + Deprecation::trigger( + 'phpdocumentor/guides', + 'https://github.com/phpDocumentor/guides/issues/1161', + 'Please provide the children as an array of InlineNodeInterface instances instead of a string.', + ); + $children = [new PlainTextInlineNode($value)]; + } + + parent::__construct($children); } public function getTargetReference(): string @@ -46,4 +65,9 @@ public function getDebugInformation(): array 'value' => $this->getValue(), ]; } + + public function getType(): string + { + return $this->type; + } } diff --git a/src/Nodes/Inline/BCInlineNodeBehavior.php b/src/Nodes/Inline/BCInlineNodeBehavior.php new file mode 100644 index 0000000..e34ca85 --- /dev/null +++ b/src/Nodes/Inline/BCInlineNodeBehavior.php @@ -0,0 +1,50 @@ +toString(); + } + + /** @param InlineNodeInterface[]|string $value */ + public function setValue(mixed $value): void + { + if (is_string($value)) { + $value = [new PlainTextInlineNode($value)]; + + Deprecation::trigger( + 'phpdocumentor/guides', + 'https://github.com/phpDocumentor/guides/issues/1161', + 'Please provide the children as an array of InlineNodeInterface instances instead of a string.', + ); + } + + parent::setValue($value); + } + + abstract public function toString(): string; +} diff --git a/src/Nodes/Inline/EmphasisInlineNode.php b/src/Nodes/Inline/EmphasisInlineNode.php index ecb8090..1d4371a 100644 --- a/src/Nodes/Inline/EmphasisInlineNode.php +++ b/src/Nodes/Inline/EmphasisInlineNode.php @@ -13,12 +13,32 @@ namespace phpDocumentor\Guides\Nodes\Inline; -final class EmphasisInlineNode extends InlineNode +use Doctrine\Deprecations\Deprecation; +use phpDocumentor\Guides\Nodes\InlineCompoundNode; + +final class EmphasisInlineNode extends InlineCompoundNode { + use BCInlineNodeBehavior; + public const TYPE = 'emphasis'; - public function __construct(string $value) + /** @param InlineNodeInterface[] $children */ + public function __construct(string $value, array $children = []) + { + if (empty($children)) { + $children = [new PlainTextInlineNode($value)]; + Deprecation::trigger( + 'phpdocumentor/guides', + 'https://github.com/phpDocumentor/guides/issues/1161', + 'Please provide the children as an array of InlineNodeInterface instances instead of a string.', + ); + } + + parent::__construct($children); + } + + public function getType(): string { - parent::__construct(self::TYPE, $value); + return self::TYPE; } } diff --git a/src/Nodes/Inline/HyperLinkNode.php b/src/Nodes/Inline/HyperLinkNode.php index 5ea04e8..89eef6f 100644 --- a/src/Nodes/Inline/HyperLinkNode.php +++ b/src/Nodes/Inline/HyperLinkNode.php @@ -18,8 +18,9 @@ */ final class HyperLinkNode extends AbstractLinkInlineNode { - public function __construct(string $value, string $targetReference) + /** @param InlineNodeInterface[] $children */ + public function __construct(string $value, string $targetReference, array $children = []) { - parent::__construct('link', $targetReference, $value); + parent::__construct('link', $targetReference, $value, $children); } } diff --git a/src/Nodes/Inline/InlineNode.php b/src/Nodes/Inline/InlineNode.php index 3f8414d..59bc5f1 100644 --- a/src/Nodes/Inline/InlineNode.php +++ b/src/Nodes/Inline/InlineNode.php @@ -16,7 +16,7 @@ use phpDocumentor\Guides\Nodes\AbstractNode; /** @extends AbstractNode */ -abstract class InlineNode extends AbstractNode +abstract class InlineNode extends AbstractNode implements InlineNodeInterface { public function __construct(private readonly string $type, string $value = '') { diff --git a/src/Nodes/Inline/InlineNodeInterface.php b/src/Nodes/Inline/InlineNodeInterface.php new file mode 100644 index 0000000..50e67be --- /dev/null +++ b/src/Nodes/Inline/InlineNodeInterface.php @@ -0,0 +1,23 @@ + */ -final class InlineCompoundNode extends CompoundNode +/** @extends CompoundNode */ +class InlineCompoundNode extends CompoundNode implements InlineNodeInterface { public function toString(): string { @@ -33,4 +33,9 @@ public static function getPlainTextInlineNode(string $content): self { return new InlineCompoundNode([new PlainTextInlineNode($content)]); } + + public function getType(): string + { + return 'compound'; + } } From b18915791f7cb5a8b3cad6d671a55502bfeade00 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Mon, 18 Nov 2024 19:04:01 +0100 Subject: [PATCH 51/53] [BUGFIX] Fix interlink target URLs for links with anchor prefix (cherry picked from commit a821ca9cd44f7ec224b5bb9f38e8c883219645be) # Conflicts: # tests/Integration/tests-full/markdown-full/front-matter-md/expected/index.html --- src/Renderer/InterlinkObjectsRenderer.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Renderer/InterlinkObjectsRenderer.php b/src/Renderer/InterlinkObjectsRenderer.php index 189e482..fbef337 100644 --- a/src/Renderer/InterlinkObjectsRenderer.php +++ b/src/Renderer/InterlinkObjectsRenderer.php @@ -14,6 +14,7 @@ namespace phpDocumentor\Guides\Renderer; use phpDocumentor\Guides\Handlers\RenderCommand; +use phpDocumentor\Guides\Meta\InternalTarget; use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; use phpDocumentor\Guides\RenderContext; use phpDocumentor\Guides\Renderer\UrlGenerator\UrlGeneratorInterface; @@ -73,6 +74,13 @@ public function render(RenderCommand $renderCommand): void '', $this->urlGenerator->createFileUrl($context, $internalTarget->getDocumentPath(), $internalTarget->getAnchor()), ); + if ($internalTarget instanceof InternalTarget) { + $url = $this->documentNameResolver->canonicalUrl( + '', + $this->urlGenerator->createFileUrl($context, $internalTarget->getDocumentPath(), $internalTarget->getPrefix() . $internalTarget->getAnchor()), + ); + } + $inventory[$linkType][$key] = [ $projectNode->getTitle() ?? '-', $projectNode->getVersion() ?? '-', From 437334f5658dbac636b999cae435a10a6d68b134 Mon Sep 17 00:00:00 2001 From: "lina.wolf" Date: Mon, 6 Jan 2025 16:02:15 +0100 Subject: [PATCH 52/53] [BUGFIX] Make title links link to the first anchor of a section This prevents collisions. (cherry picked from commit 25afe0d41b4daaa27ce29ce26610bc8ded1ca1ec) --- .../NodeTransformers/CollectLinkTargetsTransformer.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php index 93c7889..90c17e3 100644 --- a/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php +++ b/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php @@ -87,9 +87,16 @@ public function enterNode(Node $node, CompilerContextInterface $compilerContext) $currentDocument = $this->documentStack->top(); Assert::notNull($currentDocument); $anchorName = $node->getId(); + foreach ($node->getChildren() as $childNode) { + if ($childNode instanceof AnchorNode) { + $anchorName = $childNode->getValue(); + break; + } + } + try { $compilerContext->getProjectNode()->addLinkTarget( - $anchorName, + $node->getId(), new InternalTarget( $currentDocument->getFilePath(), $anchorName, From e2a1ae9c20b4db3d600d55214a9cae42d911d02f Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 10 Jan 2025 16:04:10 +0100 Subject: [PATCH 53/53] [FIX] make heading level dynamic The heading level of markdown documents is not strictly 1 based. Some documents are starting with a random heading. Before those documents were rendered empty. This change fixes that behavior, headings to not longer start at level 1. (cherry picked from commit 561af1b3968f600925afe767c985f417858b9bc2) --- .../SectionCreationTransformer.php | 27 ++++++++++++++++--- src/Nodes/DocumentNode.php | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Compiler/NodeTransformers/SectionCreationTransformer.php b/src/Compiler/NodeTransformers/SectionCreationTransformer.php index 4bd481b..4542f3c 100644 --- a/src/Compiler/NodeTransformers/SectionCreationTransformer.php +++ b/src/Compiler/NodeTransformers/SectionCreationTransformer.php @@ -31,10 +31,12 @@ final class SectionCreationTransformer implements NodeTransformer { /** @var SectionNode[] $sectionStack */ private array $sectionStack = []; + private int $firstLevel = 1; public function enterNode(Node $node, CompilerContextInterface $compilerContext): Node { if ($node instanceof DocumentNode) { + $this->firstLevel = 1; $this->sectionStack = []; } @@ -68,7 +70,7 @@ public function leaveNode(Node $node, CompilerContextInterface $compilerContext) if (count($this->sectionStack) > 0 && $compilerContext->getShadowTree()->isLastChildOfParent()) { $lastSection = end($this->sectionStack); - while ($lastSection?->getTitle()->getLevel() > 1) { + while ($lastSection?->getTitle()->getLevel() > $this->firstLevel) { $lastSection = array_pop($this->sectionStack); } @@ -92,9 +94,9 @@ public function leaveNode(Node $node, CompilerContextInterface $compilerContext) end($this->sectionStack)->addChildNode($newSection); } - $this->sectionStack[] = $newSection; + $this->pushNewSectionToStack($newSection); - return $lastSection?->getTitle()->getLevel() === 1 ? $lastSection : null; + return $lastSection?->getTitle()->getLevel() <= $this->firstLevel ? $lastSection : null; } $newSection = new SectionNode($node); @@ -102,7 +104,7 @@ public function leaveNode(Node $node, CompilerContextInterface $compilerContext) $lastSection->addChildNode($newSection); } - $this->sectionStack[] = $newSection; + $this->pushNewSectionToStack($newSection); return null; } @@ -117,4 +119,21 @@ public function getPriority(): int // Should run as first transformer return PHP_INT_MAX; } + + /** + * Pushes the new section to the stack. + * + * The stack is used to track the current level of nodes and adding child + * nodes to the section. As not all documentation formats are using the + * correct level of title nodes we need to track the level of the first + * title node to determine the correct level of the section. + */ + private function pushNewSectionToStack(SectionNode $newSection): void + { + if (count($this->sectionStack) === 0) { + $this->firstLevel = $newSection->getTitle()->getLevel(); + } + + $this->sectionStack[] = $newSection; + } } diff --git a/src/Nodes/DocumentNode.php b/src/Nodes/DocumentNode.php index 05f8974..fad13d2 100644 --- a/src/Nodes/DocumentNode.php +++ b/src/Nodes/DocumentNode.php @@ -121,7 +121,7 @@ public function getPageTitle(): string|null public function getTitle(): TitleNode|null { foreach ($this->value as $node) { - if ($node instanceof SectionNode && $node->getTitle()->getLevel() === 1) { + if ($node instanceof SectionNode) { return $node->getTitle(); }