Skip to content

Commit f1453d7

Browse files
jaapiowouterj
authored andcommitted
[FEATURE] add configuration block directive
Add the implementation of the configuration block directive. To make the implemtation more generic a configuration option is introduced. This will allow the users to configure their own labels. Co-authored-by: Wouter de Jong <wouterj@users.noreply.github.com>
1 parent 409a7e5 commit f1453d7

File tree

12 files changed

+253
-1
lines changed

12 files changed

+253
-1
lines changed

packages/guides-restructured-text/resources/config/guides-restructured-text.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use phpDocumentor\Guides\RestructuredText\Directives\CautionDirective;
1212
use phpDocumentor\Guides\RestructuredText\Directives\ClassDirective;
1313
use phpDocumentor\Guides\RestructuredText\Directives\CodeBlockDirective;
14+
use phpDocumentor\Guides\RestructuredText\Directives\ConfigurationBlockDirective;
1415
use phpDocumentor\Guides\RestructuredText\Directives\ConfvalDirective;
1516
use phpDocumentor\Guides\RestructuredText\Directives\ContainerDirective;
1617
use phpDocumentor\Guides\RestructuredText\Directives\ContentsDirective;
@@ -115,6 +116,7 @@
115116
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
116117

117118
use function Symfony\Component\DependencyInjection\Loader\Configurator\inline_service;
119+
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
118120
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
119121
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
120122

@@ -175,6 +177,10 @@
175177
),
176178
])
177179
->set(ConfvalDirective::class)
180+
->set(ConfigurationBlockDirective::class)
181+
->args([
182+
'$languageLabels' => param('phpdoc.rst.code_language_labels'),
183+
])
178184
->set(ContainerDirective::class)
179185
->set(ContentsDirective::class)
180186
->arg('$documentNameResolver', service(DocumentNameResolverInterface::class))

packages/guides-restructured-text/src/RestructuredText/DependencyInjection/ReStructuredTextExtension.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,41 @@
88
use phpDocumentor\Guides\RestructuredText\Nodes\ConfvalNode;
99
use phpDocumentor\Guides\RestructuredText\Nodes\OptionNode;
1010
use phpDocumentor\Guides\RestructuredText\Nodes\VersionChangeNode;
11+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
12+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
13+
use Symfony\Component\Config\Definition\ConfigurationInterface;
1114
use Symfony\Component\Config\FileLocator;
1215
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1316
use Symfony\Component\DependencyInjection\ContainerBuilder;
1417
use Symfony\Component\DependencyInjection\Extension\Extension;
1518
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
1619
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
1720

21+
use function assert;
1822
use function dirname;
1923
use function phpDocumentor\Guides\DependencyInjection\template;
2024

21-
class ReStructuredTextExtension extends Extension implements PrependExtensionInterface, CompilerPassInterface
25+
final class ReStructuredTextExtension extends Extension implements
26+
PrependExtensionInterface,
27+
CompilerPassInterface,
28+
ConfigurationInterface
2229
{
2330
/** @param mixed[] $configs */
2431
public function load(array $configs, ContainerBuilder $container): void
2532
{
33+
$configuration = $this->getConfiguration($configs, $container);
34+
$config = $this->processConfiguration($configuration, $configs);
2635
$loader = new PhpFileLoader(
2736
$container,
2837
new FileLocator(dirname(__DIR__, 3) . '/resources/config'),
2938
);
3039

40+
$normalizedLanguageLabels = [];
41+
foreach ($config['code_language_labels'] ?? [] as $item) {
42+
$normalizedLanguageLabels[$item['language']] = $item['label'];
43+
}
44+
45+
$container->setParameter('phpdoc.rst.code_language_labels', $normalizedLanguageLabels);
3146
$loader->load('guides-restructured-text.php');
3247
}
3348

@@ -55,4 +70,36 @@ public function process(ContainerBuilder $container): void
5570
{
5671
(new TextRolePass())->process($container);
5772
}
73+
74+
public function getConfigTreeBuilder(): TreeBuilder
75+
{
76+
$treeBuilder = new TreeBuilder('rst');
77+
$rootNode = $treeBuilder->getRootNode();
78+
assert($rootNode instanceof ArrayNodeDefinition);
79+
80+
$rootNode
81+
->fixXmlConfig('code_language_label', 'code_language_labels')
82+
->children()
83+
->arrayNode('code_language_labels')
84+
->arrayPrototype()
85+
->children()
86+
->scalarNode('language')
87+
->isRequired()
88+
->end()
89+
->scalarNode('label')
90+
->isRequired()
91+
->end()
92+
->end()
93+
->end()
94+
->end()
95+
->end();
96+
97+
return $treeBuilder;
98+
}
99+
100+
/** @param mixed[] $config */
101+
public function getConfiguration(array $config, ContainerBuilder $container): static
102+
{
103+
return $this;
104+
}
58105
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\Directives;
6+
7+
use phpDocumentor\Guides\Nodes\CodeNode;
8+
use phpDocumentor\Guides\Nodes\CollectionNode;
9+
use phpDocumentor\Guides\Nodes\Configuration\ConfigurationBlockNode;
10+
use phpDocumentor\Guides\Nodes\Configuration\ConfigurationTab;
11+
use phpDocumentor\Guides\Nodes\Node;
12+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
13+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
14+
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
15+
use Psr\Log\LoggerInterface;
16+
use Symfony\Component\String\Slugger\AsciiSlugger;
17+
use Symfony\Component\String\Slugger\SluggerInterface;
18+
19+
use function assert;
20+
use function get_debug_type;
21+
22+
final class ConfigurationBlockDirective extends SubDirective
23+
{
24+
private SluggerInterface $slugger;
25+
26+
/**
27+
* @param Rule<CollectionNode> $startingRule
28+
* @param array<string, string> $languageLabels
29+
*/
30+
public function __construct(
31+
private LoggerInterface $logger,
32+
Rule $startingRule,
33+
private readonly array $languageLabels = [],
34+
) {
35+
parent::__construct($startingRule);
36+
37+
$this->slugger = new AsciiSlugger();
38+
}
39+
40+
public function getName(): string
41+
{
42+
return 'configuration-block';
43+
}
44+
45+
protected function processSub(
46+
BlockContext $blockContext,
47+
CollectionNode $collectionNode,
48+
Directive $directive,
49+
): Node|null {
50+
$tabs = [];
51+
foreach ($collectionNode->getValue() as $child) {
52+
if (!$child instanceof CodeNode) {
53+
$this->logger->warning('The ".. configuration-block::" directive only supports code blocks, "' . get_debug_type($child) . '" given.');
54+
55+
continue;
56+
}
57+
58+
$language = $child->getLanguage();
59+
assert($language !== null);
60+
61+
$label = $this->languageLabels[$language] ?? $this->slugger->slug($language, ' ')->title()->toString();
62+
63+
$tabs[] = new ConfigurationTab(
64+
$label,
65+
$this->slugger->slug($label)->lower()->toString(),
66+
$child,
67+
);
68+
}
69+
70+
return new ConfigurationBlockNode($tabs);
71+
}
72+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div class="configuration-block">
2+
<div role="tablist" aria-label="Configuration formats" class="configuration-tabs configuration-tabs-length-{{ node.tabs|length }}">
3+
{% for tab in node.tabs %}
4+
<button role="tab" type="button" data-language="{{ tab.slug }}"
5+
aria-controls="{{ 'configuration-block-tabpanel-' ~ tab.hash }}" aria-selected="{{ loop.first ? 'true' : 'false' }}"
6+
{{ loop.first ? 'data-active="true"' }} {{ not loop.first ? 'tabindex="-1"' }}>
7+
<span>{{ tab.label }}</span>
8+
</button>
9+
{% endfor %}
10+
</div>
11+
12+
{% for tab in node.tabs %}
13+
<div role="tabpanel" id="{{ 'configuration-block-tabpanel-' ~ tab.hash }}" aria-label="{{ tab.label }}" class="configuration-codeblock" data-language="{{ tab.slug }}" style="{{ not loop.first ? 'display: none' }}">
14+
{{ renderNode(tab.content) }}
15+
</div>
16+
{% endfor %}
17+
</div>

packages/guides/resources/template/html/template.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use phpDocumentor\Guides\Nodes\AnnotationListNode;
77
use phpDocumentor\Guides\Nodes\CitationNode;
88
use phpDocumentor\Guides\Nodes\CodeNode;
9+
use phpDocumentor\Guides\Nodes\Configuration\ConfigurationBlockNode;
910
use phpDocumentor\Guides\Nodes\DefinitionListNode;
1011
use phpDocumentor\Guides\Nodes\DefinitionLists\DefinitionNode;
1112
use phpDocumentor\Guides\Nodes\DocumentNode;
@@ -65,6 +66,7 @@
6566
DocumentNode::class => 'structure/document.html.twig',
6667
ImageNode::class => 'body/image.html.twig',
6768
CodeNode::class => 'body/code.html.twig',
69+
ConfigurationBlockNode::class => 'body/configuration-block.html.twig',
6870
DefinitionListNode::class => 'body/definition-list.html.twig',
6971
DefinitionNode::class => 'body/definition.html.twig',
7072
FieldListNode::class => 'body/field-list.html.twig',
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\Nodes\Configuration;
6+
7+
use phpDocumentor\Guides\Nodes\AbstractNode;
8+
9+
/** @extends AbstractNode<list<ConfigurationTab>> */
10+
final class ConfigurationBlockNode extends AbstractNode
11+
{
12+
/** @param list<ConfigurationTab> $tabs */
13+
public function __construct(
14+
array $tabs,
15+
) {
16+
$this->value = $tabs;
17+
}
18+
19+
/** @return list<ConfigurationTab> */
20+
public function getTabs(): array
21+
{
22+
return $this->value;
23+
}
24+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\Nodes\Configuration;
6+
7+
use phpDocumentor\Guides\Nodes\CodeNode;
8+
9+
use function hash;
10+
11+
final class ConfigurationTab
12+
{
13+
public readonly string $hash;
14+
15+
public function __construct(
16+
public readonly string $label,
17+
public readonly string $slug,
18+
public readonly CodeNode $content,
19+
) {
20+
$this->hash = hash('xxh128', $content->getValue());
21+
}
22+
}

phpstan-baseline.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ parameters:
9090
count: 1
9191
path: packages/guides-graphs/src/Graphs/Renderer/PlantumlRenderer.php
9292

93+
-
94+
message: "#^Cannot call method scalarNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#"
95+
count: 1
96+
path: packages/guides-restructured-text/src/RestructuredText/DependencyInjection/ReStructuredTextExtension.php
97+
9398
-
9499
message: "#^Using nullsafe property access on non\\-nullable type Doctrine\\\\Common\\\\Lexer\\\\Token\\<int, string\\>\\. Use \\-\\> instead\\.$#"
95100
count: 1

psalm-baseline.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
<code>scalarNode</code>
1919
</UndefinedInterfaceMethod>
2020
</file>
21+
<file src="packages/guides-restructured-text/src/RestructuredText/DependencyInjection/ReStructuredTextExtension.php">
22+
<UndefinedInterfaceMethod>
23+
<code>scalarNode</code>
24+
</UndefinedInterfaceMethod>
25+
</file>
2126
<file src="packages/guides-restructured-text/src/RestructuredText/Parser/Productions/EnumeratedListRule.php">
2227
<InvalidArgument>
2328
<code>$listConfig</code>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!-- content start -->
2+
<div class="section" id="directive-tests">
3+
<h1>Directive tests</h1>
4+
5+
<div class="configuration-block">
6+
<div role="tablist" aria-label="Configuration formats" class="configuration-tabs configuration-tabs-length-2">
7+
<button role="tab" type="button" data-language="yaml"
8+
aria-controls="configuration-block-tabpanel-523eafc9524d67db5f21ecae2362d532" aria-selected="true"
9+
data-active="true" >
10+
<span>Yaml</span>
11+
</button>
12+
<button role="tab" type="button" data-language="custom"
13+
aria-controls="configuration-block-tabpanel-a51ba27b3c76153f629592baf23834dc" aria-selected="false"
14+
tabindex="-1">
15+
<span>CUSTOM</span>
16+
</button>
17+
</div>
18+
19+
<div role="tabpanel" id="configuration-block-tabpanel-523eafc9524d67db5f21ecae2362d532" aria-label="Yaml" class="configuration-codeblock" data-language="yaml" style="">
20+
<pre><code class="language-yaml"># app/config/services.yml</code></pre>
21+
</div>
22+
<div role="tabpanel" id="configuration-block-tabpanel-a51ba27b3c76153f629592baf23834dc" aria-label="CUSTOM" class="configuration-codeblock" data-language="custom" style="display: none">
23+
<pre><code class="language-php">// config/routes.php</code></pre>
24+
</div>
25+
</div>
26+
27+
</div>
28+
29+
30+
<!-- content end -->
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<guides xmlns="https://www.phpdoc.org/guides"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="https://www.phpdoc.org/guides packages/guides-cli/resources/schema/guides.xsd">
5+
<extension class="\phpDocumentor\Guides\RestructuredText\DependencyInjection\ReStructuredTextExtension">
6+
<code-language-label language="php" label="CUSTOM" />
7+
<code-language-label language="other" label="Other" />
8+
</extension>
9+
</guides>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Directive tests
2+
===============
3+
4+
.. configuration-block::
5+
6+
.. code-block:: yaml
7+
8+
# app/config/services.yml
9+
10+
.. code-block:: php
11+
12+
// config/routes.php
13+

0 commit comments

Comments
 (0)