From 9804699ab8c39a8d7262809bc4338ff191694634 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Tue, 17 Jun 2025 22:37:02 +0200 Subject: [PATCH 1/2] First iteration on option attributes The new option attribute makes it easier to validate directives. An extra advantage is that we can use the new attributes to document the directive options. --- .../Directives/Attributes/Option.php | 22 +++++ .../Directives/BaseDirective.php | 87 +++++++++++++++++++ .../Directives/ConfvalDirective.php | 14 ++- .../Directives/ContentsDirective.php | 5 +- .../Directives/CsvTableDirective.php | 1 + .../Directives/DocumentBlockDirective.php | 4 +- .../Directives/FigureDirective.php | 9 ++ .../Directives/ImageDirective.php | 16 +++- .../Directives/IncludeDirective.php | 3 + .../Directives/OptionType.php | 13 +++ .../Directives/YoutubeDirective.php | 18 ++-- 11 files changed, 173 insertions(+), 19 deletions(-) create mode 100644 packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Option.php create mode 100644 packages/guides-restructured-text/src/RestructuredText/Directives/OptionType.php diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Option.php b/packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Option.php new file mode 100644 index 000000000..ab9b7e313 --- /dev/null +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Option.php @@ -0,0 +1,22 @@ + Cache of Option attributes indexed by option name */ + private array $optionAttributeCache; + /** * Get the directive name */ @@ -94,4 +98,87 @@ protected function optionsToArray(array $options): array { return array_map(static fn (DirectiveOption $option): bool|float|int|string|null => $option->getValue(), $options); } + + /** + * Gets an option value from a directive based on attribute configuration. + * + * Looks up the option in the directive and returns its value converted to the + * appropriate type based on the Option attribute defined on this directive class. + * If the option is not present in the directive, returns the default value from the attribute. + * + * @param Directive $directive The directive containing the options + * @param string $optionName The name of the option to retrieve + * + * @return mixed The option value converted to the appropriate type, or the default value + */ + final protected function readOption(Directive $directive, string $optionName): mixed + { + $optionAttribute = $this->findOptionAttribute($optionName); + + return $this->getOptionValue($directive, $optionAttribute); + } + + final protected function readAllOptions(Directive $directive): array + { + $this->initialize(); + + return array_map( + fn (Option $option) => $this->getOptionValue($directive, $option), + $this->optionAttributeCache + ); + } + + private function getOptionValue(Directive $directive, Option|null $option): mixed + { + if ($option === null) { + return null; + } + + if (!$directive->hasOption($option->name)) { + return $option->default; + } + + $directiveOption = $directive->getOption($option->name); + $value = $directiveOption->getValue(); + + return match ($option->type) { + OptionType::Integer => (int) $value, + OptionType::Boolean => $value === null || filter_var($value, FILTER_VALIDATE_BOOL), + OptionType::String => (string) $value, + OptionType::Array => (array) $value, + default => $value, + }; + } + + + /** + * Finds the Option attribute for the given option name on the current class. + * + * @param string $optionName The option name to look for + * + * @return Option|null The Option attribute if found, null otherwise + */ + private function findOptionAttribute(string $optionName): ?Option + { + $this->initialize(); + + return $this->optionAttributeCache[$optionName] ?? null; + } + + private function initialize(): void + { + if (isset($this->optionAttributeCache)) { + return; + } + + $reflection = new \ReflectionClass($this); + $attributes = $reflection->getAttributes(Option::class); + $this->optionAttributeCache = []; + foreach ($attributes as $attribute) { + $option = $attribute->newInstance(); + $this->optionAttributeCache[$option->name] = $option; + } + } } + + diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/ConfvalDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/ConfvalDirective.php index 64d36df2f..a741291e6 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/ConfvalDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/ConfvalDirective.php @@ -16,6 +16,7 @@ use phpDocumentor\Guides\Nodes\CollectionNode; use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Nodes\ConfvalNode; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; @@ -32,6 +33,11 @@ * * https://sphinx-toolbox.readthedocs.io/en/stable/extensions/confval.html */ +#[Option(name: 'name', description: 'Id of the configuration value, used for linking to it.')] +#[Option(name: 'type', description: 'Type of the configuration value, e.g. "string", "int", etc.')] +#[Option(name: 'required', type: OptionType::Boolean, default: false, description: 'Whether the configuration value is required or not.')] +#[Option(name: 'default', description: 'Default value of the configuration value, if any.')] +#[Option(name: 'noindex', type: OptionType::Boolean, default: false, description: 'Whether the configuration value should not be indexed.')] final class ConfvalDirective extends SubDirective { public const NAME = 'confval'; @@ -80,16 +86,16 @@ protected function processSub( } if ($directive->hasOption('type')) { - $type = $this->inlineParser->parse($directive->getOptionString('type'), $blockContext); + $type = $this->inlineParser->parse($this->readOption($directive, 'type'), $blockContext); } - $required = $directive->getOptionBool('required'); + $required = $this->readOption($directive, 'required'); if ($directive->hasOption('default')) { - $default = $this->inlineParser->parse($directive->getOptionString('default'), $blockContext); + $default = $this->inlineParser->parse($this->readOption($directive, 'default'), $blockContext); } - $noindex = $directive->getOptionBool('noindex'); + $noindex = $this->readOption($directive, 'noindex'); foreach ($directive->getOptions() as $option) { if (in_array($option->getName(), ['type', 'required', 'default', 'noindex', 'name'], true)) { diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/ContentsDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/ContentsDirective.php index 201208e3d..7905fbb45 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/ContentsDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/ContentsDirective.php @@ -17,6 +17,7 @@ use phpDocumentor\Guides\Nodes\Menu\SectionMenuEntryNode; use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; @@ -25,6 +26,8 @@ * * Displays a table of content of the current page */ +#[Option(name: 'local', type: OptionType::Boolean, description: 'If set, the table of contents will only include sections that are local to the current document.', default: false)] +#[Option(name: 'depth', description: 'The maximum depth of the table of contents.')] final class ContentsDirective extends BaseDirective { public function __construct( @@ -51,6 +54,6 @@ public function process( return (new ContentMenuNode([new SectionMenuEntryNode($absoluteUrl)])) ->withOptions($this->optionsToArray($options)) ->withCaption($directive->getDataNode()) - ->withLocal($directive->hasOption('local')); + ->withLocal($this->readOption($directive, 'local')); } } diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/CsvTableDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/CsvTableDirective.php index f747d1a96..3e1256bae 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/CsvTableDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/CsvTableDirective.php @@ -20,6 +20,7 @@ use phpDocumentor\Guides\Nodes\Table\TableColumn; use phpDocumentor\Guides\Nodes\Table\TableRow; use phpDocumentor\Guides\Nodes\TableNode; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; use phpDocumentor\Guides\RestructuredText\Parser\Productions\RuleContainer; diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/DocumentBlockDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/DocumentBlockDirective.php index b61d74e09..e501f9eab 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/DocumentBlockDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/DocumentBlockDirective.php @@ -16,9 +16,11 @@ use phpDocumentor\Guides\Nodes\CollectionNode; use phpDocumentor\Guides\Nodes\DocumentBlockNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; +#[Option(name: 'identifier', description: 'The identifier of the document block')] final class DocumentBlockDirective extends SubDirective { public function getName(): string @@ -39,7 +41,7 @@ protected function processSub( return new DocumentBlockNode( $collectionNode->getChildren(), - $identifier, + $this->readOption($directive, 'identifier'), ); } } diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/FigureDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/FigureDirective.php index 0e18dae88..04f9b9ed5 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/FigureDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/FigureDirective.php @@ -18,6 +18,7 @@ use phpDocumentor\Guides\Nodes\ImageNode; use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule; @@ -33,6 +34,14 @@ * * Here is an awesome caption */ +#[Option(name: 'width', description: 'Width of the image in pixels')] +#[Option(name: 'height', description: 'Height of the image in pixels')] +#[Option(name: 'alt', description: 'Alternative text for the image')] +#[Option(name: 'scale', description: 'Scale of the image, e.g. 0.5 for half size')] +#[Option(name: 'target', description: 'Target for the image, e.g. a link to the image')] +#[Option(name: 'class', description: 'CSS class to apply to the image')] +#[Option(name: 'name', description: 'Name of the image, used for references')] +#[Option(name: 'align', description: 'Alignment of the image, e.g. left, right, center')] final class FigureDirective extends SubDirective { public function __construct( diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/ImageDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/ImageDirective.php index c1252a38d..ecdee1109 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/ImageDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/ImageDirective.php @@ -20,6 +20,7 @@ use phpDocumentor\Guides\Nodes\Inline\ReferenceNode; use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; @@ -37,6 +38,14 @@ * :width: 100 * :title: An image */ +#[Option(name: 'width', description: 'Width of the image in pixels')] +#[Option(name: 'height', description: 'Height of the image in pixels')] +#[Option(name: 'alt', description: 'Alternative text for the image')] +#[Option(name: 'scale', description: 'Scale of the image, e.g. 0.5 for half size')] +#[Option(name: 'target', description: 'Target for the image, e.g. a link to the image')] +#[Option(name: 'class', description: 'CSS class to apply to the image')] +#[Option(name: 'name', description: 'Name of the image, used for references')] +#[Option(name: 'align', description: 'Alignment of the image, e.g. left, right, center')] final class ImageDirective extends BaseDirective { /** @see https://regex101.com/r/9dUrzu/3 */ @@ -67,8 +76,11 @@ public function processNode( ), ); if ($directive->hasOption('target')) { - $targetReference = (string) $directive->getOption('target')->getValue(); - $node->setTarget($this->resolveLinkTarget($targetReference)); + $node->setTarget( + $this->resolveLinkTarget( + $this->readOption($directive, 'target') + ), + ); } return $node; diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/IncludeDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/IncludeDirective.php index 123397189..2718aee26 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/IncludeDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/IncludeDirective.php @@ -17,6 +17,7 @@ use phpDocumentor\Guides\Nodes\CollectionNode; use phpDocumentor\Guides\Nodes\LiteralBlockNode; use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; use phpDocumentor\Guides\RestructuredText\Parser\Productions\DocumentRule; @@ -27,6 +28,8 @@ use function sprintf; use function str_replace; +#[Option(name: 'literal', description: 'If set, the contents will be rendered as a literal block.')] +#[Option(name: 'code', description: 'If set, the contents will be rendered as a code block with the specified language.')] final class IncludeDirective extends BaseDirective { public function __construct(private readonly DocumentRule $startingRule) diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/OptionType.php b/packages/guides-restructured-text/src/RestructuredText/Directives/OptionType.php new file mode 100644 index 000000000..1f0f57e5d --- /dev/null +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/OptionType.php @@ -0,0 +1,13 @@ +getData(), ); - return $node->withOptions( - array_filter( - [ - 'width' => $directive->getOption('width')->getValue() ?? 560, - 'title' => $directive->getOption('title')->getValue(), - 'height' => $directive->getOption('height')->getValue() ?? 315, - 'allow' => $directive->getOption('allow')->getValue() ?? 'encrypted-media; picture-in-picture; web-share', - 'allowfullscreen' => (bool) ($directive->getOption('allowfullscreen')->getValue() ?? true), - ], - ), - ); + return $node->withOptions($this->readAllOptions($directive)); } } From 0c1592238b3cd1ed94af0a57fcc2415c1fd484ff Mon Sep 17 00:00:00 2001 From: Jaapio Date: Tue, 24 Jun 2025 20:44:04 +0200 Subject: [PATCH 2/2] Redirect processing in compiler pass --- .../config/guides-restructured-text.php | 4 ++ .../Compiler/Passes/DirectiveProcessPass.php | 64 +++++++++++++++++++ .../Directives/Attributes/Directive.php | 18 ++++++ .../Directives/BaseDirective.php | 56 +++++++++++++++- .../Directives/NoteDirective.php | 1 + .../Directives/WarningDirective.php | 1 + .../Directives/YoutubeDirective.php | 8 +++ .../RestructuredText/Nodes/DirectiveNode.php | 22 +++++++ .../Parser/Productions/DirectiveRule.php | 8 ++- 9 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 packages/guides-restructured-text/src/RestructuredText/Compiler/Passes/DirectiveProcessPass.php create mode 100644 packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Directive.php create mode 100644 packages/guides-restructured-text/src/RestructuredText/Nodes/DirectiveNode.php diff --git a/packages/guides-restructured-text/resources/config/guides-restructured-text.php b/packages/guides-restructured-text/resources/config/guides-restructured-text.php index 6b81f0566..74233c92a 100644 --- a/packages/guides-restructured-text/resources/config/guides-restructured-text.php +++ b/packages/guides-restructured-text/resources/config/guides-restructured-text.php @@ -372,6 +372,10 @@ ->set(GlobSearcher::class) ->set(ToctreeBuilder::class) ->set(InlineMarkupRule::class) + + ->set(\phpDocumentor\Guides\RestructuredText\Compiler\Passes\DirectiveProcessPass::class) + ->arg('$directives', tagged_iterator('phpdoc.guides.directive')) + ->tag('phpdoc.guides.compiler.nodeTransformers') ->set(DefaultCodeNodeOptionMapper::class) ->alias(CodeNodeOptionMapper::class, DefaultCodeNodeOptionMapper::class); }; diff --git a/packages/guides-restructured-text/src/RestructuredText/Compiler/Passes/DirectiveProcessPass.php b/packages/guides-restructured-text/src/RestructuredText/Compiler/Passes/DirectiveProcessPass.php new file mode 100644 index 000000000..7d051576a --- /dev/null +++ b/packages/guides-restructured-text/src/RestructuredText/Compiler/Passes/DirectiveProcessPass.php @@ -0,0 +1,64 @@ + */ + private array $directives; + + /** @param iterable $directives */ + public function __construct( + private readonly LoggerInterface $logger, + private readonly GeneralDirective $generalDirective, + iterable $directives = [], + ) { + foreach ($directives as $directive) { + $this->registerDirective($directive); + } + } + + private function registerDirective(DirectiveHandler $directive): void + { + $this->directives[strtolower($directive->getName())] = $directive; + foreach ($directive->getAliases() as $alias) { + $this->directives[strtolower($alias)] = $directive; + } + } + + public function enterNode(Node $node, CompilerContext $compilerContext): Node + { + return $node; + } + + public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null + { + return $this->getDirectiveHandler($node->getDirective())->createNode($node->getDirective()); + } + + private function getDirectiveHandler(Directive $directive): DirectiveHandler + { + return $this->directives[strtolower($directive->getName())] ?? $this->generalDirective; + } + + public function supports(Node $node): bool + { + return $node instanceof DirectiveNode; + } + + public function getPriority(): int + { + return PHP_INT_MAX; + } +} diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Directive.php b/packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Directive.php new file mode 100644 index 000000000..2366f17e0 --- /dev/null +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/Attributes/Directive.php @@ -0,0 +1,18 @@ + Cache of Option attributes indexed by option name */ private array $optionAttributeCache; + private string $name; + + private array $aliases; + /** * Get the directive name */ - abstract public function getName(): string; + public function getName(): string + { + if (isset($this->name)) { + return $this->name; + } + + $reflection = new \ReflectionClass($this); + $attributes = $reflection->getAttributes(Attributes\Directive::class); + + if (count($attributes) === 0) { + throw new \LogicException('Directive class must have a Directive attribute'); + } + + $this->name = $attributes[0]->newInstance()->name; + return $this->name; + } /** * Allow a directive to be registered under multiple names. @@ -54,7 +73,35 @@ abstract public function getName(): string; */ public function getAliases(): array { - return []; + if (isset($this->aliases)) { + return $this->aliases; + } + + $reflection = new \ReflectionClass($this); + $attributes = $reflection->getAttributes(Attributes\Directive::class); + $this->aliases = []; + if (count($attributes) !== 0) { + $this->aliases = $attributes[0]->newInstance()->aliases; + } + + return $this->aliases; + } + + /** + * Returns whether this directive has been upgraded to a new version. + * + * In the new version of directives, the processing is done during the compile phase. + * This method only exists to allow for backward compatibility with directives that + * were written before the upgrade. + * + * @internal + */ + final public function isUpgraded(): bool + { + $reflection = new \ReflectionClass($this); + $attributes = $reflection->getAttributes(Attributes\Directive::class); + + return count($attributes) === 1; } /** @@ -89,6 +136,11 @@ public function processNode( return new GenericNode($directive->getVariable(), $directive->getData()); } + public function createNode(Directive $directive): Node|null + { + return null; + } + /** * @param DirectiveOption[] $options * diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/NoteDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/NoteDirective.php index 953386a21..66f2449ef 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/NoteDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/NoteDirective.php @@ -26,6 +26,7 @@ * This is a note admonition. * ``` */ +#[Attributes\Directive(name: 'note')] final class NoteDirective extends AbstractAdmonitionDirective { public function __construct(protected Rule $startingRule) diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/WarningDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/WarningDirective.php index b3bbdb8c5..ee9a3a8a1 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/WarningDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/WarningDirective.php @@ -26,6 +26,7 @@ * This is a warning admonition. * ``` */ +#[Attributes\Directive(name: 'warning')] final class WarningDirective extends AbstractAdmonitionDirective { public function __construct(protected Rule $startingRule) diff --git a/packages/guides-restructured-text/src/RestructuredText/Directives/YoutubeDirective.php b/packages/guides-restructured-text/src/RestructuredText/Directives/YoutubeDirective.php index 16b578a9c..b44348973 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Directives/YoutubeDirective.php +++ b/packages/guides-restructured-text/src/RestructuredText/Directives/YoutubeDirective.php @@ -14,6 +14,8 @@ namespace phpDocumentor\Guides\RestructuredText\Directives; use phpDocumentor\Guides\Nodes\EmbeddedFrame; +use phpDocumentor\Guides\Nodes\Node; +use phpDocumentor\Guides\RestructuredText\Directives\Attributes; use phpDocumentor\Guides\RestructuredText\Directives\Attributes\Option; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Directive; @@ -37,6 +39,7 @@ * - string allow The allow attribute of the iframe, default is 'encrypted-media; picture-in-picture; web-share' * - bool allowfullscreen Whether the video should be allowed to go fullscreen, default is true */ +#[Attributes\Directive(name: 'youtube')] #[Option('width', type: OptionType::Integer, default: 560, description: 'Width of the video')] #[Option('title', type: OptionType::String, description: 'Title of the video')] #[Option('height', type: OptionType::Integer, default: 315, description: 'Height of the video')] @@ -53,6 +56,11 @@ public function process( BlockContext $blockContext, Directive $directive, ): EmbeddedFrame { + return $this->createNode($directive); + } + + public function createNode(Directive $directive): EmbeddedFrame + { $node = new EmbeddedFrame( 'https://www.youtube-nocookie.com/embed/' . $directive->getData(), ); diff --git a/packages/guides-restructured-text/src/RestructuredText/Nodes/DirectiveNode.php b/packages/guides-restructured-text/src/RestructuredText/Nodes/DirectiveNode.php new file mode 100644 index 000000000..3a9a44f96 --- /dev/null +++ b/packages/guides-restructured-text/src/RestructuredText/Nodes/DirectiveNode.php @@ -0,0 +1,22 @@ + */ +final class DirectiveNode extends AbstractNode +{ + public function __construct(private Directive $directive) + { + + } + + public function getDirective(): Directive + { + return $this->directive; + } +} diff --git a/packages/guides-restructured-text/src/RestructuredText/Parser/Productions/DirectiveRule.php b/packages/guides-restructured-text/src/RestructuredText/Parser/Productions/DirectiveRule.php index 178eb004a..ab7637189 100644 --- a/packages/guides-restructured-text/src/RestructuredText/Parser/Productions/DirectiveRule.php +++ b/packages/guides-restructured-text/src/RestructuredText/Parser/Productions/DirectiveRule.php @@ -18,6 +18,7 @@ use phpDocumentor\Guides\Nodes\Node; use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective as DirectiveHandler; use phpDocumentor\Guides\RestructuredText\Directives\GeneralDirective; +use phpDocumentor\Guides\RestructuredText\Nodes\DirectiveNode; use phpDocumentor\Guides\RestructuredText\Parser\BlockContext; use phpDocumentor\Guides\RestructuredText\Parser\Buffer; use phpDocumentor\Guides\RestructuredText\Parser\Directive; @@ -84,10 +85,15 @@ public function apply(BlockContext $blockContext, CompoundNode|null $on = null): } $this->parseDirectiveContent($directive, $blockContext); + $this->interpretDirectiveOptions($documentIterator, $directive); $directiveHandler = $this->getDirectiveHandler($directive); + if ($directiveHandler->isUpgraded()) { + //What do we do with the content of the directive? + // Child nodes need to be parsed. and the content needs to be collected as a string. + return new DirectiveNode($directive); + } - $this->interpretDirectiveOptions($documentIterator, $directive); $buffer = $this->collectDirectiveContents($documentIterator); // Processing the Directive, the handler is responsible for adding the right Nodes to the document.