Skip to content

Commit 0c15922

Browse files
committed
Redirect processing in compiler pass
1 parent 9804699 commit 0c15922

9 files changed

Lines changed: 179 additions & 3 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@
372372
->set(GlobSearcher::class)
373373
->set(ToctreeBuilder::class)
374374
->set(InlineMarkupRule::class)
375+
376+
->set(\phpDocumentor\Guides\RestructuredText\Compiler\Passes\DirectiveProcessPass::class)
377+
->arg('$directives', tagged_iterator('phpdoc.guides.directive'))
378+
->tag('phpdoc.guides.compiler.nodeTransformers')
375379
->set(DefaultCodeNodeOptionMapper::class)
376380
->alias(CodeNodeOptionMapper::class, DefaultCodeNodeOptionMapper::class);
377381
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\Compiler\Passes;
6+
7+
use phpDocumentor\Guides\Compiler\CompilerContext;
8+
use phpDocumentor\Guides\Compiler\NodeTransformer;
9+
use phpDocumentor\Guides\Nodes\Node;
10+
use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective as DirectiveHandler;
11+
use phpDocumentor\Guides\RestructuredText\Directives\GeneralDirective;
12+
use phpDocumentor\Guides\RestructuredText\Nodes\DirectiveNode;
13+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
14+
use Psr\Log\LoggerInterface;
15+
16+
class DirectiveProcessPass implements NodeTransformer
17+
{
18+
/** @var array<string, DirectiveHandler> */
19+
private array $directives;
20+
21+
/** @param iterable<DirectiveHandler> $directives */
22+
public function __construct(
23+
private readonly LoggerInterface $logger,
24+
private readonly GeneralDirective $generalDirective,
25+
iterable $directives = [],
26+
) {
27+
foreach ($directives as $directive) {
28+
$this->registerDirective($directive);
29+
}
30+
}
31+
32+
private function registerDirective(DirectiveHandler $directive): void
33+
{
34+
$this->directives[strtolower($directive->getName())] = $directive;
35+
foreach ($directive->getAliases() as $alias) {
36+
$this->directives[strtolower($alias)] = $directive;
37+
}
38+
}
39+
40+
public function enterNode(Node $node, CompilerContext $compilerContext): Node
41+
{
42+
return $node;
43+
}
44+
45+
public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null
46+
{
47+
return $this->getDirectiveHandler($node->getDirective())->createNode($node->getDirective());
48+
}
49+
50+
private function getDirectiveHandler(Directive $directive): DirectiveHandler
51+
{
52+
return $this->directives[strtolower($directive->getName())] ?? $this->generalDirective;
53+
}
54+
55+
public function supports(Node $node): bool
56+
{
57+
return $node instanceof DirectiveNode;
58+
}
59+
60+
public function getPriority(): int
61+
{
62+
return PHP_INT_MAX;
63+
}
64+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\Directives\Attributes;
6+
7+
use Attribute;
8+
9+
#[Attribute(Attribute::TARGET_CLASS)]
10+
final class Directive
11+
{
12+
public function __construct(
13+
public readonly string $name,
14+
public readonly array $aliases = [],
15+
) {
16+
17+
}
18+
}

packages/guides-restructured-text/src/RestructuredText/Directives/BaseDirective.php

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,29 @@ abstract class BaseDirective
4040
/** @var array<string, Option|null> Cache of Option attributes indexed by option name */
4141
private array $optionAttributeCache;
4242

43+
private string $name;
44+
45+
private array $aliases;
46+
4347
/**
4448
* Get the directive name
4549
*/
46-
abstract public function getName(): string;
50+
public function getName(): string
51+
{
52+
if (isset($this->name)) {
53+
return $this->name;
54+
}
55+
56+
$reflection = new \ReflectionClass($this);
57+
$attributes = $reflection->getAttributes(Attributes\Directive::class);
58+
59+
if (count($attributes) === 0) {
60+
throw new \LogicException('Directive class must have a Directive attribute');
61+
}
62+
63+
$this->name = $attributes[0]->newInstance()->name;
64+
return $this->name;
65+
}
4766

4867
/**
4968
* Allow a directive to be registered under multiple names.
@@ -54,7 +73,35 @@ abstract public function getName(): string;
5473
*/
5574
public function getAliases(): array
5675
{
57-
return [];
76+
if (isset($this->aliases)) {
77+
return $this->aliases;
78+
}
79+
80+
$reflection = new \ReflectionClass($this);
81+
$attributes = $reflection->getAttributes(Attributes\Directive::class);
82+
$this->aliases = [];
83+
if (count($attributes) !== 0) {
84+
$this->aliases = $attributes[0]->newInstance()->aliases;
85+
}
86+
87+
return $this->aliases;
88+
}
89+
90+
/**
91+
* Returns whether this directive has been upgraded to a new version.
92+
*
93+
* In the new version of directives, the processing is done during the compile phase.
94+
* This method only exists to allow for backward compatibility with directives that
95+
* were written before the upgrade.
96+
*
97+
* @internal
98+
*/
99+
final public function isUpgraded(): bool
100+
{
101+
$reflection = new \ReflectionClass($this);
102+
$attributes = $reflection->getAttributes(Attributes\Directive::class);
103+
104+
return count($attributes) === 1;
58105
}
59106

60107
/**
@@ -89,6 +136,11 @@ public function processNode(
89136
return new GenericNode($directive->getVariable(), $directive->getData());
90137
}
91138

139+
public function createNode(Directive $directive): Node|null
140+
{
141+
return null;
142+
}
143+
92144
/**
93145
* @param DirectiveOption[] $options
94146
*

packages/guides-restructured-text/src/RestructuredText/Directives/NoteDirective.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* This is a note admonition.
2727
* ```
2828
*/
29+
#[Attributes\Directive(name: 'note')]
2930
final class NoteDirective extends AbstractAdmonitionDirective
3031
{
3132
public function __construct(protected Rule $startingRule)

packages/guides-restructured-text/src/RestructuredText/Directives/WarningDirective.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* This is a warning admonition.
2727
* ```
2828
*/
29+
#[Attributes\Directive(name: 'warning')]
2930
final class WarningDirective extends AbstractAdmonitionDirective
3031
{
3132
public function __construct(protected Rule $startingRule)

packages/guides-restructured-text/src/RestructuredText/Directives/YoutubeDirective.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace phpDocumentor\Guides\RestructuredText\Directives;
1515

1616
use phpDocumentor\Guides\Nodes\EmbeddedFrame;
17+
use phpDocumentor\Guides\Nodes\Node;
18+
use phpDocumentor\Guides\RestructuredText\Directives\Attributes;
1719
use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option;
1820
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
1921
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
@@ -37,6 +39,7 @@
3739
* - string allow The allow attribute of the iframe, default is 'encrypted-media; picture-in-picture; web-share'
3840
* - bool allowfullscreen Whether the video should be allowed to go fullscreen, default is true
3941
*/
42+
#[Attributes\Directive(name: 'youtube')]
4043
#[Option('width', type: OptionType::Integer, default: 560, description: 'Width of the video')]
4144
#[Option('title', type: OptionType::String, description: 'Title of the video')]
4245
#[Option('height', type: OptionType::Integer, default: 315, description: 'Height of the video')]
@@ -53,6 +56,11 @@ public function process(
5356
BlockContext $blockContext,
5457
Directive $directive,
5558
): EmbeddedFrame {
59+
return $this->createNode($directive);
60+
}
61+
62+
public function createNode(Directive $directive): EmbeddedFrame
63+
{
5664
$node = new EmbeddedFrame(
5765
'https://www.youtube-nocookie.com/embed/' . $directive->getData(),
5866
);
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\RestructuredText\Nodes;
6+
7+
use phpDocumentor\Guides\Nodes\AbstractNode;
8+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
9+
10+
/** @extends AbstractNode<Directive> */
11+
final class DirectiveNode extends AbstractNode
12+
{
13+
public function __construct(private Directive $directive)
14+
{
15+
16+
}
17+
18+
public function getDirective(): Directive
19+
{
20+
return $this->directive;
21+
}
22+
}

packages/guides-restructured-text/src/RestructuredText/Parser/Productions/DirectiveRule.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use phpDocumentor\Guides\Nodes\Node;
1919
use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective as DirectiveHandler;
2020
use phpDocumentor\Guides\RestructuredText\Directives\GeneralDirective;
21+
use phpDocumentor\Guides\RestructuredText\Nodes\DirectiveNode;
2122
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
2223
use phpDocumentor\Guides\RestructuredText\Parser\Buffer;
2324
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
@@ -84,10 +85,15 @@ public function apply(BlockContext $blockContext, CompoundNode|null $on = null):
8485
}
8586

8687
$this->parseDirectiveContent($directive, $blockContext);
88+
$this->interpretDirectiveOptions($documentIterator, $directive);
8789

8890
$directiveHandler = $this->getDirectiveHandler($directive);
91+
if ($directiveHandler->isUpgraded()) {
92+
//What do we do with the content of the directive?
93+
// Child nodes need to be parsed. and the content needs to be collected as a string.
94+
return new DirectiveNode($directive);
95+
}
8996

90-
$this->interpretDirectiveOptions($documentIterator, $directive);
9197
$buffer = $this->collectDirectiveContents($documentIterator);
9298

9399
// Processing the Directive, the handler is responsible for adding the right Nodes to the document.

0 commit comments

Comments
 (0)