From 19a4e8b356af7a72f8ce3a15bff22301a6937b24 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Fri, 3 Nov 2023 17:12:13 +0100 Subject: [PATCH] !!![FEATURE] enable basic latex rendering To be able to render different formats we needed a more flexible system to pick the correct renderers. The main change in this patch to introduce a OutputAwareDelegatingNodeRenderer as the templates are using a function to render child nodes. Where we lose the connection with the executed renderer. This new OutputAwareDelegatingNodeRenderer will take the output format from the context to select the correct DelegatingNodeRender that will find the correct renderer on its turn to render the node. To make it easier to add custom output formats using other format renders, eg. a single page html, or pdf using the html renderers. the compiler for DI is adjusted. This is now a more advanced compiler pass that will autowire the configured renderers as much as possible. --- .../guides/resources/config/command_bus.php | 3 +- packages/guides/resources/config/guides.php | 48 +++--- .../tex/{guides => }/body/anchor.tex.twig | 0 .../tex/{guides => }/body/code.tex.twig | 0 .../tex/{guides => }/body/image.tex.twig | 0 .../{guides => }/body/list/list-item.tex.twig | 0 .../tex/{guides => }/body/list/list.tex.twig | 0 .../tex/{guides => }/body/paragraph.tex.twig | 0 .../tex/{guides => }/body/quote.tex.twig | 0 .../tex/{guides => }/body/separator.tex.twig | 0 .../tex/{guides => }/body/toc/toc.tex.twig | 0 .../template/tex/structure/document.tex.twig | 3 + .../project.tex.twig} | 11 +- ...dererPass.php => HtmlNodeRendererPass.php} | 12 +- .../Compiler/RendererPass.php | 88 +++++++++++ .../Compiler/TexNodeRendererPass.php | 141 ++++++++++++++++++ .../DependencyInjection/GuidesExtension.php | 9 +- .../src/DependencyInjection/TestExtension.php | 3 +- .../OutputAwareDelegatingNodeRenderer.php | 46 ++++++ packages/guides/src/RenderContext.php | 8 +- packages/guides/src/Renderer/HtmlRenderer.php | 6 - .../src/Renderer/InMemoryRendererFactory.php | 4 +- .../src/Renderer/InterlinkObjectsRenderer.php | 8 +- .../guides/src/Renderer/LatexRenderer.php | 35 ++++- packages/guides/src/Renderer/TypeRenderer.php | 2 - phpstan-baseline.neon | 10 ++ psalm.xml | 5 + tests/Functional/FunctionalTest.php | 3 +- .../tests/latex/latex-index/input/skip | 1 - 29 files changed, 370 insertions(+), 76 deletions(-) rename packages/guides/resources/template/tex/{guides => }/body/anchor.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/code.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/image.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/list/list-item.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/list/list.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/paragraph.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/quote.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/separator.tex.twig (100%) rename packages/guides/resources/template/tex/{guides => }/body/toc/toc.tex.twig (100%) create mode 100644 packages/guides/resources/template/tex/structure/document.tex.twig rename packages/guides/resources/template/tex/{guides/structure/document.tex.twig => structure/project.tex.twig} (69%) rename packages/guides/src/DependencyInjection/Compiler/{NodeRendererPass.php => HtmlNodeRendererPass.php} (92%) create mode 100644 packages/guides/src/DependencyInjection/Compiler/RendererPass.php create mode 100644 packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php create mode 100644 packages/guides/src/NodeRenderers/OutputAwareDelegatingNodeRenderer.php delete mode 100644 tests/Integration/tests/latex/latex-index/input/skip diff --git a/packages/guides/resources/config/command_bus.php b/packages/guides/resources/config/command_bus.php index e1e450155..1eaf754b2 100644 --- a/packages/guides/resources/config/command_bus.php +++ b/packages/guides/resources/config/command_bus.php @@ -19,7 +19,6 @@ use phpDocumentor\Guides\Handlers\RenderDocumentCommand; use phpDocumentor\Guides\Handlers\RenderDocumentHandler; use phpDocumentor\Guides\Handlers\RenderHandler; -use phpDocumentor\Guides\NodeRenderers\DelegatingNodeRenderer; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -46,7 +45,7 @@ ->set(RenderDocumentHandler::class) ->tag('phpdoc.guides.command', ['command' => RenderDocumentCommand::class]) - ->arg('$renderer', service(DelegatingNodeRenderer::class)) + ->arg('$renderer', service('phpdoc.guides.output_node_renderer')) ->arg('$eventDispatcher', service(EventDispatcherInterface::class)) ->set(CommandBus::class) diff --git a/packages/guides/resources/config/guides.php b/packages/guides/resources/config/guides.php index b2877a5a2..67a5d745e 100644 --- a/packages/guides/resources/config/guides.php +++ b/packages/guides/resources/config/guides.php @@ -12,17 +12,13 @@ use phpDocumentor\Guides\Interlink\InventoryLoader; use phpDocumentor\Guides\Interlink\InventoryRepository; use phpDocumentor\Guides\Interlink\JsonLoader; -use phpDocumentor\Guides\NodeRenderers\DefaultNodeRenderer; -use phpDocumentor\Guides\NodeRenderers\DelegatingNodeRenderer; use phpDocumentor\Guides\NodeRenderers\Html\BreadCrumbNodeRenderer; use phpDocumentor\Guides\NodeRenderers\Html\DocumentNodeRenderer; use phpDocumentor\Guides\NodeRenderers\Html\MenuEntryRenderer; use phpDocumentor\Guides\NodeRenderers\Html\MenuNodeRenderer; use phpDocumentor\Guides\NodeRenderers\Html\TableNodeRenderer; -use phpDocumentor\Guides\NodeRenderers\InMemoryNodeRendererFactory; -use phpDocumentor\Guides\NodeRenderers\NodeRendererFactory; use phpDocumentor\Guides\NodeRenderers\NodeRendererFactoryAware; -use phpDocumentor\Guides\NodeRenderers\PreRenderers\PreNodeRendererFactory; +use phpDocumentor\Guides\NodeRenderers\OutputAwareDelegatingNodeRenderer; use phpDocumentor\Guides\Parser; use phpDocumentor\Guides\ReferenceResolvers\AnchorReducer; use phpDocumentor\Guides\ReferenceResolvers\AnchorReferenceResolver; @@ -145,18 +141,30 @@ ->arg('$resolvers', tagged_iterator('phpdoc.guides.reference_resolver', defaultPriorityMethod: 'getPriority')) ->set(HtmlRenderer::class) - ->tag('phpdoc.renderer.typerenderer') + ->tag( + 'phpdoc.renderer.typerenderer', + [ + 'noderender_tag' => 'phpdoc.guides.noderenderer.html', + 'format' => 'html', + ], + ) ->args( ['$commandBus' => service(CommandBus::class)], ) ->set(LatexRenderer::class) - ->tag('phpdoc.renderer.typerenderer') - ->args( - ['$commandBus' => service(CommandBus::class)], + ->tag( + 'phpdoc.renderer.typerenderer', + [ + 'noderender_tag' => 'phpdoc.guides.noderenderer.tex', + 'format' => 'tex', + ], ) ->set(InterlinkObjectsRenderer::class) - ->tag('phpdoc.renderer.typerenderer') + ->tag( + 'phpdoc.renderer.typerenderer', + ['format' => 'interlink'], + ) ->set(DocumentNodeRenderer::class) ->tag('phpdoc.guides.noderenderer.html') @@ -169,33 +177,21 @@ ->set(BreadCrumbNodeRenderer::class) ->tag('phpdoc.guides.noderenderer.html') - ->set(DefaultNodeRenderer::class) - - ->set(InMemoryNodeRendererFactory::class) - ->args([ - '$nodeRenderers' => tagged_iterator('phpdoc.guides.noderenderer.html'), - '$defaultNodeRenderer' => new Reference(DefaultNodeRenderer::class), - ]) - ->alias(NodeRendererFactory::class, InMemoryNodeRendererFactory::class) - - ->set(PreNodeRendererFactory::class) - ->decorate(NodeRendererFactory::class) - ->arg('$innerFactory', service('.inner')) - ->arg('$preRenderers', tagged_iterator('phpdoc.guides.prerenderer')) - ->set(ReferenceResolverPreRender::class) ->tag('phpdoc.guides.prerenderer') ->set(InMemoryRendererFactory::class) - ->arg('$renderSets', tagged_iterator('phpdoc.renderer.typerenderer')) + ->arg('$renderSets', tagged_iterator('phpdoc.renderer.typerenderer', 'format')) ->alias(TypeRendererFactory::class, InMemoryRendererFactory::class) ->set(SluggerAnchorReducer::class) ->alias(AnchorReducer::class, SluggerAnchorReducer::class) + ->set('phpdoc.guides.output_node_renderer', OutputAwareDelegatingNodeRenderer::class) + ->arg('$nodeRenderers', tagged_iterator('phpdoc.guides.output_node_renderer', 'format')) ->set(AssetsExtension::class) - ->arg('$nodeRenderer', service(DelegatingNodeRenderer::class)) + ->arg('$nodeRenderer', service('phpdoc.guides.output_node_renderer')) ->tag('twig.extension') ->autowire() diff --git a/packages/guides/resources/template/tex/guides/body/anchor.tex.twig b/packages/guides/resources/template/tex/body/anchor.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/anchor.tex.twig rename to packages/guides/resources/template/tex/body/anchor.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/code.tex.twig b/packages/guides/resources/template/tex/body/code.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/code.tex.twig rename to packages/guides/resources/template/tex/body/code.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/image.tex.twig b/packages/guides/resources/template/tex/body/image.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/image.tex.twig rename to packages/guides/resources/template/tex/body/image.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/list/list-item.tex.twig b/packages/guides/resources/template/tex/body/list/list-item.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/list/list-item.tex.twig rename to packages/guides/resources/template/tex/body/list/list-item.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/list/list.tex.twig b/packages/guides/resources/template/tex/body/list/list.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/list/list.tex.twig rename to packages/guides/resources/template/tex/body/list/list.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/paragraph.tex.twig b/packages/guides/resources/template/tex/body/paragraph.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/paragraph.tex.twig rename to packages/guides/resources/template/tex/body/paragraph.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/quote.tex.twig b/packages/guides/resources/template/tex/body/quote.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/quote.tex.twig rename to packages/guides/resources/template/tex/body/quote.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/separator.tex.twig b/packages/guides/resources/template/tex/body/separator.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/separator.tex.twig rename to packages/guides/resources/template/tex/body/separator.tex.twig diff --git a/packages/guides/resources/template/tex/guides/body/toc/toc.tex.twig b/packages/guides/resources/template/tex/body/toc/toc.tex.twig similarity index 100% rename from packages/guides/resources/template/tex/guides/body/toc/toc.tex.twig rename to packages/guides/resources/template/tex/body/toc/toc.tex.twig diff --git a/packages/guides/resources/template/tex/structure/document.tex.twig b/packages/guides/resources/template/tex/structure/document.tex.twig new file mode 100644 index 000000000..16b84b896 --- /dev/null +++ b/packages/guides/resources/template/tex/structure/document.tex.twig @@ -0,0 +1,3 @@ +{%- for child in node.children ~%} + {{ renderNode(child) }} +{%~ endfor -%} diff --git a/packages/guides/resources/template/tex/guides/structure/document.tex.twig b/packages/guides/resources/template/tex/structure/project.tex.twig similarity index 69% rename from packages/guides/resources/template/tex/guides/structure/document.tex.twig rename to packages/guides/resources/template/tex/structure/project.tex.twig index 23e9dfbd8..848333e90 100644 --- a/packages/guides/resources/template/tex/guides/structure/document.tex.twig +++ b/packages/guides/resources/template/tex/structure/project.tex.twig @@ -1,4 +1,3 @@ -{% if isMain(node) %} \documentclass[11pt]{report} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} @@ -10,13 +9,9 @@ \usepackage{graphicx} \usepackage{hyperref} \usepackage{listings} -{% for node in document.headerNodes %} -{{ renderNode(node) }} -{% endfor %} \begin{document} -{% endif %} \label{{ '{' }}{{ document.environment.url}}{{ '}' }} -{{ body|raw }} -{% if isMain %} +{%- for document in documents -%} +{{ renderNode(document) }} +{%- endfor -%} \end{document} -{% endif %} diff --git a/packages/guides/src/DependencyInjection/Compiler/NodeRendererPass.php b/packages/guides/src/DependencyInjection/Compiler/HtmlNodeRendererPass.php similarity index 92% rename from packages/guides/src/DependencyInjection/Compiler/NodeRendererPass.php rename to packages/guides/src/DependencyInjection/Compiler/HtmlNodeRendererPass.php index 706177d74..98741f5a0 100644 --- a/packages/guides/src/DependencyInjection/Compiler/NodeRendererPass.php +++ b/packages/guides/src/DependencyInjection/Compiler/HtmlNodeRendererPass.php @@ -4,7 +4,6 @@ namespace phpDocumentor\Guides\DependencyInjection\Compiler; -use phpDocumentor\Guides\NodeRenderers\NodeRendererFactory; use phpDocumentor\Guides\NodeRenderers\TemplateNodeRenderer; use phpDocumentor\Guides\Nodes\AnchorNode; use phpDocumentor\Guides\Nodes\AnnotationListNode; @@ -62,7 +61,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; -final class NodeRendererPass implements CompilerPassInterface +final class HtmlNodeRendererPass implements CompilerPassInterface { private const HTML = [ AnchorNode::class => 'inline/anchor.html.twig', @@ -122,15 +121,6 @@ final class NodeRendererPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { - foreach ($container->findTaggedServiceIds('phpdoc.guides.noderendererfactoryaware') as $id => $tags) { - $definition = $container->getDefinition($id); - $definition->addMethodCall( - 'setNodeRendererFactory', - [new Reference(NodeRendererFactory::class)], - ); - $definition->clearTag('phpdoc.guides.noderendererfactoryaware'); - } - $htmlRendererDefinitions = []; foreach (self::HTML as $node => $template) { $definition = new Definition( diff --git a/packages/guides/src/DependencyInjection/Compiler/RendererPass.php b/packages/guides/src/DependencyInjection/Compiler/RendererPass.php new file mode 100644 index 000000000..c2758b3d2 --- /dev/null +++ b/packages/guides/src/DependencyInjection/Compiler/RendererPass.php @@ -0,0 +1,88 @@ +findTaggedServiceIds('phpdoc.guides.noderendererfactoryaware') as $id => $tags) { + $definition = $container->getDefinition($id); + $definition->addMethodCall( + 'setNodeRendererFactory', + [new Reference('phpdoc.guides.noderenderer.factory.html')], + ); + $definition->clearTag('phpdoc.guides.noderendererfactoryaware'); + } + + foreach ($container->findTaggedServiceIds('phpdoc.renderer.typerenderer') as $id => $tags) { + foreach ($tags as $tag) { + if (isset($tag['noderender_tag']) === false) { + continue; + } + + $definitions[sprintf('phpdoc.guides.noderenderer.factory.%s', $tag['format'])] = $this->createNodeRendererFactory($tag); + $definitions[sprintf('phpdoc.guides.noderenderer.prefactory.%s', $tag['format'])] = $this->createPreNodeRendererFactory($tag); + $definitions[sprintf('phpdoc.guides.noderenderer.delegating.%s', $tag['format'])] = $this->createDelegatingNodeRender($tag); + $definitions[sprintf('phpdoc.guides.noderenderer.default.%s', $tag['format'])] = (new Definition(DefaultNodeRenderer::class))->setAutowired(true) + ->addTag('phpdoc.guides.noderenderer.html'); + } + } + + $container->addDefinitions($definitions); + } + + /** @param array{format: string} $tag */ + private function createDelegatingNodeRender(array $tag): Definition + { + return (new Definition(DelegatingNodeRenderer::class)) + ->addTag('phpdoc.guides.output_node_renderer', ['format' => $tag['format']]) + ->addMethodCall('setNodeRendererFactory', [new Reference(sprintf('phpdoc.guides.noderenderer.factory.%s', $tag['format']))]); + } + + /** @param array{format: string, noderender_tag: string} $tag */ + private function createNodeRendererFactory(array $tag): Definition + { + return new Definition( + InMemoryNodeRendererFactory::class, + [ + '$nodeRenderers' => tagged_iterator($tag['noderender_tag']), + '$defaultNodeRenderer' => new Reference(sprintf('phpdoc.guides.noderenderer.default.%s', $tag['format'])), + ], + ); + } + + /** @param array{format: string, noderender_tag: string} $tag */ + private function createPreNodeRendererFactory(array $tag): Definition + { + return (new Definition( + PreNodeRendererFactory::class, + [ + '$innerFactory' => new Reference('.inner'), + '$preRenderers' => tagged_iterator('phpdoc.guides.prerenderer'), + ], + )) + ->setDecoratedService(sprintf('phpdoc.guides.noderenderer.factory.%s', $tag['format'])); + } +} diff --git a/packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php b/packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php new file mode 100644 index 000000000..1e06cb0f5 --- /dev/null +++ b/packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php @@ -0,0 +1,141 @@ + 'inline/anchor.tex.twig', + FigureNode::class => 'body/figure.tex.twig', + MetaNode::class => 'structure/header/meta.tex.twig', + ParagraphNode::class => 'body/paragraph.tex.twig', + QuoteNode::class => 'body/quote.tex.twig', + SeparatorNode::class => 'body/separator.tex.twig', + TitleNode::class => 'structure/header-title.tex.twig', + SectionNode::class => 'structure/section.tex.twig', + DocumentNode::class => 'structure/document.tex.twig', + ImageNode::class => 'body/image.tex.twig', + CodeNode::class => 'body/code.tex.twig', + DefinitionListNode::class => 'body/definition-list.tex.twig', + DefinitionNode::class => 'body/definition.tex.twig', + FieldListNode::class => 'body/field-list.tex.twig', + ListNode::class => 'body/list/list.tex.twig', + ListItemNode::class => 'body/list/list-item.tex.twig', + LiteralBlockNode::class => 'body/literal-block.tex.twig', + CitationNode::class => 'body/citation.tex.twig', + FootnoteNode::class => 'body/footnote.tex.twig', + AnnotationListNode::class => 'body/annotation-list.tex.twig', + // Inline + ImageInlineNode::class => 'inline/image.tex.twig', + InlineCompoundNode::class => 'inline/inline-node.tex.twig', + AbbreviationInlineNode::class => 'inline/textroles/abbreviation.tex.twig', + CitationInlineNode::class => 'inline/citation.tex.twig', + DocReferenceNode::class => 'inline/doc.tex.twig', + EmphasisInlineNode::class => 'inline/emphasis.tex.twig', + FootnoteInlineNode::class => 'inline/footnote.tex.twig', + HyperLinkNode::class => 'inline/link.tex.twig', + LiteralInlineNode::class => 'inline/literal.tex.twig', + NewlineInlineNode::class => 'inline/newline.tex.twig', + WhitespaceInlineNode::class => 'inline/nbsp.tex.twig', + PlainTextInlineNode::class => 'inline/plain-text.tex.twig', + ReferenceNode::class => 'inline/ref.tex.twig', + StrongInlineNode::class => 'inline/strong.tex.twig', + VariableInlineNode::class => 'inline/variable.tex.twig', + GenericTextRoleInlineNode::class => 'inline/textroles/generic.tex.twig', + // Output as Metatags + AuthorNode::class => 'structure/header/author.tex.twig', + CopyrightNode::class => 'structure/header/copyright.tex.twig', + DateNode::class => 'structure/header/date.tex.twig', + NoSearchNode::class => 'structure/header/no-search.tex.twig', + TopicNode::class => 'structure/header/topic.tex.twig', + // No output in page header in tex - might be output in i.e. LaTex + AddressNode::class => 'structure/header/blank.tex.twig', + AuthorsNode::class => 'structure/header/blank.tex.twig', + ContactNode::class => 'structure/header/blank.tex.twig', + NoCommentsNode::class => 'structure/header/blank.tex.twig', + OrganizationNode::class => 'structure/header/blank.tex.twig', + OrphanNode::class => 'structure/header/blank.tex.twig', + RevisionNode::class => 'structure/header/blank.tex.twig', + TocDepthNode::class => 'structure/header/blank.tex.twig', + VersionNode::class => 'structure/header/blank.tex.twig', + ]; + + public function process(ContainerBuilder $container): void + { + $texRendererDefinitions = []; + foreach (self::TEX as $node => $template) { + $definition = new Definition( + TemplateNodeRenderer::class, + [ + '$renderer' => new Reference(TemplateRenderer::class), + '$template' => $template, + '$nodeClass' => $node, + ], + ); + $definition->addTag('phpdoc.guides.noderenderer.tex'); + + $texRendererDefinitions['phpdoc.guides.noderenderer.tex.' . $node] = $definition; + } + + $container->addDefinitions($texRendererDefinitions); + } +} diff --git a/packages/guides/src/DependencyInjection/GuidesExtension.php b/packages/guides/src/DependencyInjection/GuidesExtension.php index 5ba334d8f..9c24fff46 100644 --- a/packages/guides/src/DependencyInjection/GuidesExtension.php +++ b/packages/guides/src/DependencyInjection/GuidesExtension.php @@ -4,8 +4,10 @@ namespace phpDocumentor\Guides\DependencyInjection; -use phpDocumentor\Guides\DependencyInjection\Compiler\NodeRendererPass; +use phpDocumentor\Guides\DependencyInjection\Compiler\HtmlNodeRendererPass; use phpDocumentor\Guides\DependencyInjection\Compiler\ParserRulesPass; +use phpDocumentor\Guides\DependencyInjection\Compiler\RendererPass; +use phpDocumentor\Guides\DependencyInjection\Compiler\TexNodeRendererPass; use phpDocumentor\Guides\Settings\ProjectSettings; use phpDocumentor\Guides\Settings\SettingsManager; use phpDocumentor\Guides\Twig\Theme\ThemeConfig; @@ -173,6 +175,7 @@ public function load(array $configs, ContainerBuilder $container): void ->addMethodCall('setProjectSettings', [$projectSettings]); $config['base_template_paths'][] = dirname(__DIR__, 2) . '/resources/template/html'; + $config['base_template_paths'][] = dirname(__DIR__, 2) . '/resources/template/tex'; $container->setParameter('phpdoc.guides.base_template_paths', $config['base_template_paths']); foreach ($config['themes'] as $themeName => $themeConfig) { @@ -183,8 +186,10 @@ public function load(array $configs, ContainerBuilder $container): void public function process(ContainerBuilder $container): void { - (new NodeRendererPass())->process($container); (new ParserRulesPass())->process($container); + (new HtmlNodeRendererPass())->process($container); + (new TexNodeRendererPass())->process($container); + (new RendererPass())->process($container); } /** @param mixed[] $config */ diff --git a/packages/guides/src/DependencyInjection/TestExtension.php b/packages/guides/src/DependencyInjection/TestExtension.php index 376212104..f830a03e6 100644 --- a/packages/guides/src/DependencyInjection/TestExtension.php +++ b/packages/guides/src/DependencyInjection/TestExtension.php @@ -7,7 +7,6 @@ use Monolog\Handler\TestHandler; use Monolog\Logger; use phpDocumentor\Guides\Compiler\Compiler; -use phpDocumentor\Guides\NodeRenderers\DelegatingNodeRenderer; use phpDocumentor\Guides\Parser; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -25,7 +24,7 @@ public function process(ContainerBuilder $container): void { $container->getDefinition(Parser::class)->setPublic(true); $container->getDefinition(Compiler::class)->setPublic(true); - $container->getDefinition(DelegatingNodeRenderer::class)->setPublic(true); + $container->getDefinition('phpdoc.guides.output_node_renderer')->setPublic(true); $container->register(TestHandler::class, TestHandler::class)->setPublic(true); $container->getDefinition(Logger::class) diff --git a/packages/guides/src/NodeRenderers/OutputAwareDelegatingNodeRenderer.php b/packages/guides/src/NodeRenderers/OutputAwareDelegatingNodeRenderer.php new file mode 100644 index 000000000..594879332 --- /dev/null +++ b/packages/guides/src/NodeRenderers/OutputAwareDelegatingNodeRenderer.php @@ -0,0 +1,46 @@ + */ +final class OutputAwareDelegatingNodeRenderer implements NodeRenderer +{ + /** @var array> */ + private array $nodeRenderers; + + /** @param iterable> $nodeRenderers */ + public function __construct(iterable $nodeRenderers) + { + if (is_array($nodeRenderers) === false) { + $nodeRenderers = iterator_to_array($nodeRenderers); + } + + $this->nodeRenderers = $nodeRenderers; + } + + public function supports(Node $node): bool + { + return true; + } + + public function render(Node $node, RenderContext $renderContext): string + { + if (isset($this->nodeRenderers[$renderContext->getOutputFormat()]) === false) { + throw new InvalidArgumentException( + sprintf('No node renderer found for output format "%s"', $renderContext->getOutputFormat()), + ); + } + + return $this->nodeRenderers[$renderContext->getOutputFormat()]->render($node, $renderContext); + } +} diff --git a/packages/guides/src/RenderContext.php b/packages/guides/src/RenderContext.php index cd300c90c..aefbc1acc 100644 --- a/packages/guides/src/RenderContext.php +++ b/packages/guides/src/RenderContext.php @@ -64,14 +64,16 @@ public static function forDocument( return $self; } + /** @param DocumentNode[] $allDocumentNodes */ public static function forProject( ProjectNode $projectNode, + array $allDocumentNodes, FilesystemInterface $origin, FilesystemInterface $destination, string $destinationPath, string $ouputFormat, ): self { - return new self( + $self = new self( $destinationPath, null, $origin, @@ -79,6 +81,10 @@ public static function forProject( $ouputFormat, $projectNode, ); + + $self->allDocuments = $allDocumentNodes; + + return $self; } /** diff --git a/packages/guides/src/Renderer/HtmlRenderer.php b/packages/guides/src/Renderer/HtmlRenderer.php index 86d3f605b..541afcef7 100644 --- a/packages/guides/src/Renderer/HtmlRenderer.php +++ b/packages/guides/src/Renderer/HtmlRenderer.php @@ -6,10 +6,4 @@ class HtmlRenderer extends BaseTypeRenderer { - final public const TYPE = 'html'; - - public function supports(string $outputFormat): bool - { - return $outputFormat === self::TYPE; - } } diff --git a/packages/guides/src/Renderer/InMemoryRendererFactory.php b/packages/guides/src/Renderer/InMemoryRendererFactory.php index 498f530fc..9454be06f 100644 --- a/packages/guides/src/Renderer/InMemoryRendererFactory.php +++ b/packages/guides/src/Renderer/InMemoryRendererFactory.php @@ -17,8 +17,8 @@ public function __construct(private readonly iterable $renderSets) public function getRenderSet(string $outputFormat): TypeRenderer { - foreach ($this->renderSets as $renderSet) { - if ($renderSet->supports($outputFormat)) { + foreach ($this->renderSets as $format => $renderSet) { + if ($format === $outputFormat) { return $renderSet; } } diff --git a/packages/guides/src/Renderer/InterlinkObjectsRenderer.php b/packages/guides/src/Renderer/InterlinkObjectsRenderer.php index 1a5566901..72b612eea 100644 --- a/packages/guides/src/Renderer/InterlinkObjectsRenderer.php +++ b/packages/guides/src/Renderer/InterlinkObjectsRenderer.php @@ -15,19 +15,12 @@ class InterlinkObjectsRenderer implements TypeRenderer { - final public const TYPE = 'interlink'; - public function __construct( private readonly UrlGeneratorInterface $urlGenerator, private readonly DocumentNameResolverInterface $documentNameResolver, ) { } - public function supports(string $outputFormat): bool - { - return $outputFormat === self::TYPE; - } - public function render(RenderCommand $renderCommand): void { $inventory = [ @@ -38,6 +31,7 @@ public function render(RenderCommand $renderCommand): void $context = RenderContext::forProject( $projectNode, + $renderCommand->getDocumentArray(), $renderCommand->getOrigin(), $renderCommand->getDestination(), $renderCommand->getDestinationPath(), diff --git a/packages/guides/src/Renderer/LatexRenderer.php b/packages/guides/src/Renderer/LatexRenderer.php index 22159d2db..729f5bdca 100644 --- a/packages/guides/src/Renderer/LatexRenderer.php +++ b/packages/guides/src/Renderer/LatexRenderer.php @@ -4,12 +4,39 @@ namespace phpDocumentor\Guides\Renderer; -class LatexRenderer extends BaseTypeRenderer +use phpDocumentor\Guides\Handlers\RenderCommand; +use phpDocumentor\Guides\RenderContext; +use phpDocumentor\Guides\TemplateRenderer; + +class LatexRenderer implements TypeRenderer { - final public const TYPE = 'tex'; + public function __construct(private readonly TemplateRenderer $renderer) + { + } - public function supports(string $outputFormat): bool + public function render(RenderCommand $renderCommand): void { - return $outputFormat === self::TYPE; + $projectNode = $renderCommand->getProjectNode(); + + $context = RenderContext::forProject( + $projectNode, + $renderCommand->getDocumentArray(), + $renderCommand->getOrigin(), + $renderCommand->getDestination(), + $renderCommand->getDestinationPath(), + 'tex', + ); + + $context->getDestination()->put( + $renderCommand->getDestinationPath() . '/index.tex', + $this->renderer->renderTemplate( + $context, + 'structure/project.tex.twig', + [ + 'project' => $projectNode, + 'documents' => $renderCommand->getDocumentIterator(), + ], + ), + ); } } diff --git a/packages/guides/src/Renderer/TypeRenderer.php b/packages/guides/src/Renderer/TypeRenderer.php index eb23a2aae..38038dd25 100644 --- a/packages/guides/src/Renderer/TypeRenderer.php +++ b/packages/guides/src/Renderer/TypeRenderer.php @@ -8,7 +8,5 @@ interface TypeRenderer { - public function supports(string $outputFormat): bool; - public function render(RenderCommand $renderCommand): void; } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d1f2a7999..b21e94d59 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -130,6 +130,16 @@ parameters: count: 1 path: packages/guides-restructured-text/src/RestructuredText/Parser/Productions/InlineRules/NamedPhraseRule.php + - + message: "#^Function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\tagged_iterator not found\\.$#" + count: 2 + path: packages/guides/src/DependencyInjection/Compiler/RendererPass.php + + - + message: "#^Used function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\tagged_iterator not found\\.$#" + count: 1 + path: packages/guides/src/DependencyInjection/Compiler/RendererPass.php + - message: "#^Cannot call method scalarNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 1 diff --git a/psalm.xml b/psalm.xml index bbf333419..b70e1deee 100644 --- a/psalm.xml +++ b/psalm.xml @@ -45,6 +45,11 @@ + + + + + diff --git a/tests/Functional/FunctionalTest.php b/tests/Functional/FunctionalTest.php index e5757a503..3e1691e40 100644 --- a/tests/Functional/FunctionalTest.php +++ b/tests/Functional/FunctionalTest.php @@ -14,7 +14,6 @@ use phpDocumentor\Guides\ApplicationTestCase; use phpDocumentor\Guides\Compiler\Compiler; use phpDocumentor\Guides\Compiler\CompilerContext; -use phpDocumentor\Guides\NodeRenderers\DelegatingNodeRenderer; use phpDocumentor\Guides\Nodes\ProjectNode; use phpDocumentor\Guides\Parser; use phpDocumentor\Guides\RenderContext; @@ -105,7 +104,7 @@ public function testFunctional( $settingsManager = $this->createMock(SettingsManager::class); $settingsManager->method('getProjectSettings')->willReturn($projectSettings); - $renderer = $this->getContainer()->get(DelegatingNodeRenderer::class); + $renderer = $this->getContainer()->get('phpdoc.guides.output_node_renderer'); $context = RenderContext::forDocument( $document, [$document], diff --git a/tests/Integration/tests/latex/latex-index/input/skip b/tests/Integration/tests/latex/latex-index/input/skip deleted file mode 100644 index 9e211cb18..000000000 --- a/tests/Integration/tests/latex/latex-index/input/skip +++ /dev/null @@ -1 +0,0 @@ -Latex output is currently not supported \ No newline at end of file