Skip to content

Commit

Permalink
!!![FEATURE] enable basic latex rendering
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jaapio committed Nov 3, 2023
1 parent 671b944 commit d837af0
Show file tree
Hide file tree
Showing 27 changed files with 343 additions and 74 deletions.
3 changes: 1 addition & 2 deletions packages/guides/resources/config/command_bus.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
Expand Down
50 changes: 24 additions & 26 deletions packages/guides/resources/config/guides.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -145,18 +141,32 @@
->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')
Expand All @@ -169,33 +179,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()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{%- for child in node.children ~%}
{{ renderNode(child) }}
{%~ endfor -%}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{% if isMain(node) %}
\documentclass[11pt]{report}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
Expand All @@ -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 %}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,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',
Expand Down Expand Up @@ -122,15 +122,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(
Expand Down
85 changes: 85 additions & 0 deletions packages/guides/src/DependencyInjection/Compiler/RendererPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace phpDocumentor\Guides\DependencyInjection\Compiler;

use phpDocumentor\Guides\NodeRenderers\DefaultNodeRenderer;
use phpDocumentor\Guides\NodeRenderers\DelegatingNodeRenderer;
use phpDocumentor\Guides\NodeRenderers\InMemoryNodeRendererFactory;
use phpDocumentor\Guides\NodeRenderers\PreRenderers\PreNodeRendererFactory;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

Check failure on line 13 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Used function Symfony\Component\DependencyInjection\Loader\Configurator\service not found.
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;

Check failure on line 14 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Used function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator not found.
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_locator;

Check failure on line 15 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Used function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_locator not found.

final class RendererPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
//Node Renderer factory

//Inject node renderer factory into node renderer factory aware services


$definitions = [];

foreach ($container->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);
}

private function createDelegatingNodeRender(mixed $tag): Definition
{
return (new Definition(DelegatingNodeRenderer::class))
->addTag('phpdoc.guides.output_node_renderer', ['format' => $tag['format']])

Check failure on line 57 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Cannot access offset 'format' on mixed.
->addMethodCall('setNodeRendererFactory', [new Reference(sprintf('phpdoc.guides.noderenderer.factory.%s', $tag['format']))]);

Check failure on line 58 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Cannot access offset 'format' on mixed.

Check failure on line 58 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Parameter #2 ...$values of function sprintf expects bool|float|int|string|null, mixed given.
}

private function createNodeRendererFactory(mixed $tag): Definition
{
$nodeRendereFactory = new Definition(
InMemoryNodeRendererFactory::class,
[
'$nodeRenderers' => tagged_iterator($tag['noderender_tag']),

Check failure on line 66 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Cannot access offset 'noderender_tag' on mixed.

Check failure on line 66 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator not found.
'$defaultNodeRenderer' => new Reference(sprintf('phpdoc.guides.noderenderer.default.%s', $tag['format'])),

Check failure on line 67 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Cannot access offset 'format' on mixed.

Check failure on line 67 in packages/guides/src/DependencyInjection/Compiler/RendererPass.php

View workflow job for this annotation

GitHub Actions / Static analysis / Static Code Analysis (8.2)

Parameter #2 ...$values of function sprintf expects bool|float|int|string|null, mixed given.
]
);

return $nodeRendereFactory;
}

private function createPreNodeRendererFactory(mixed $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']));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

declare(strict_types=1);

namespace phpDocumentor\Guides\DependencyInjection\Compiler;

use phpDocumentor\Guides\NodeRenderers\TemplateNodeRenderer;
use phpDocumentor\Guides\Nodes\AnchorNode;
use phpDocumentor\Guides\Nodes\AnnotationListNode;
use phpDocumentor\Guides\Nodes\CitationNode;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\Nodes\DefinitionListNode;
use phpDocumentor\Guides\Nodes\DefinitionLists\DefinitionNode;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\Nodes\FieldListNode;
use phpDocumentor\Guides\Nodes\FigureNode;
use phpDocumentor\Guides\Nodes\FootnoteNode;
use phpDocumentor\Guides\Nodes\ImageNode;
use phpDocumentor\Guides\Nodes\Inline\AbbreviationInlineNode;

Check failure on line 19 in packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php

View workflow job for this annotation

GitHub Actions / Coding Standards

Type phpDocumentor\Guides\Nodes\Inline\AbbreviationInlineNode is not used in this file.
use phpDocumentor\Guides\Nodes\Inline\CitationInlineNode;

Check failure on line 20 in packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php

View workflow job for this annotation

GitHub Actions / Coding Standards

Type phpDocumentor\Guides\Nodes\Inline\CitationInlineNode is not used in this file.
use phpDocumentor\Guides\Nodes\Inline\DocReferenceNode;

Check failure on line 21 in packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php

View workflow job for this annotation

GitHub Actions / Coding Standards

Type phpDocumentor\Guides\Nodes\Inline\DocReferenceNode is not used in this file.
use phpDocumentor\Guides\Nodes\Inline\EmphasisInlineNode;

Check failure on line 22 in packages/guides/src/DependencyInjection/Compiler/TexNodeRendererPass.php

View workflow job for this annotation

GitHub Actions / Coding Standards

Type phpDocumentor\Guides\Nodes\Inline\EmphasisInlineNode is not used in this file.
use phpDocumentor\Guides\Nodes\Inline\FootnoteInlineNode;
use phpDocumentor\Guides\Nodes\Inline\GenericTextRoleInlineNode;
use phpDocumentor\Guides\Nodes\Inline\HyperLinkNode;
use phpDocumentor\Guides\Nodes\Inline\ImageInlineNode;
use phpDocumentor\Guides\Nodes\Inline\LiteralInlineNode;
use phpDocumentor\Guides\Nodes\Inline\NewlineInlineNode;
use phpDocumentor\Guides\Nodes\Inline\PlainTextInlineNode;
use phpDocumentor\Guides\Nodes\Inline\ReferenceNode;
use phpDocumentor\Guides\Nodes\Inline\StrongInlineNode;
use phpDocumentor\Guides\Nodes\Inline\VariableInlineNode;
use phpDocumentor\Guides\Nodes\Inline\WhitespaceInlineNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\ListItemNode;
use phpDocumentor\Guides\Nodes\ListNode;
use phpDocumentor\Guides\Nodes\LiteralBlockNode;
use phpDocumentor\Guides\Nodes\Metadata\AddressNode;
use phpDocumentor\Guides\Nodes\Metadata\AuthorNode;
use phpDocumentor\Guides\Nodes\Metadata\AuthorsNode;
use phpDocumentor\Guides\Nodes\Metadata\ContactNode;
use phpDocumentor\Guides\Nodes\Metadata\CopyrightNode;
use phpDocumentor\Guides\Nodes\Metadata\DateNode;
use phpDocumentor\Guides\Nodes\Metadata\MetaNode;
use phpDocumentor\Guides\Nodes\Metadata\NoCommentsNode;
use phpDocumentor\Guides\Nodes\Metadata\NoSearchNode;
use phpDocumentor\Guides\Nodes\Metadata\OrganizationNode;
use phpDocumentor\Guides\Nodes\Metadata\OrphanNode;
use phpDocumentor\Guides\Nodes\Metadata\RevisionNode;
use phpDocumentor\Guides\Nodes\Metadata\TocDepthNode;
use phpDocumentor\Guides\Nodes\Metadata\TopicNode;
use phpDocumentor\Guides\Nodes\Metadata\VersionNode;
use phpDocumentor\Guides\Nodes\ParagraphNode;
use phpDocumentor\Guides\Nodes\QuoteNode;
use phpDocumentor\Guides\Nodes\SectionNode;
use phpDocumentor\Guides\Nodes\SeparatorNode;
use phpDocumentor\Guides\Nodes\TitleNode;
use phpDocumentor\Guides\TemplateRenderer;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class TexNodeRendererPass implements CompilerPassInterface
{
private const TEX = [
AnchorNode::class => '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);
}
}
Loading

0 comments on commit d837af0

Please sign in to comment.