refactor: Cleanup git state - commit all staged changes

Major refactoring cleanup:
- Add new controller architecture (class-controller-*.php)
- Add new settings-v2 UI (views/settings-v2/)
- Add new CSS architecture (agentic-sidebar.css, tokens)
- Add esbuild build pipeline (scripts/build.js, package.json)
- Add composer dependencies (vendor/)
- Add frontend src directory (assets/js/src/index.jsx)
- Add documentation files
- Remove old/obsolete files (class-settings.php, old CSS)

This commits all pending changes from previous refactoring efforts.
This commit is contained in:
Dwindi Ramadhana
2026-06-17 05:27:58 +07:00
parent d3f142222c
commit 690991c526
7963 changed files with 941566 additions and 67372 deletions

View File

@@ -0,0 +1,18 @@
# Contribute to the phpDocumentor Guides
## Go to the mono-repository
This project is developed in the mono-repository `phpDocumentor Guides <https://github.com/phpDocumentor/guides>`__.
The repository you are currently in gets auto-created by splitting the mono-repository. You **must not** contribute
to this repository directly but always to the mono-repository linked above.
## Create Issues
* If you find something missing or something is wrong in this library, you are welcome to write an issue
describing the problem: `Issues on GitHub <https://github.com/phpDocumentor/guides/issues>`__.
* If you can, please try to fix the problem yourself.
# Make changes (create pull requests)
See the `Contribution chapter <https://docs.phpdoc.org/components/guides/guides/contributions/index.html>`__ in the
`Documentation` <https://docs.phpdoc.org/components/guides/guides/index.html>`__.

View File

@@ -0,0 +1,23 @@
======================================
Contribute to the phpDocumentor Guides
======================================
Go to the mono-repository
=========================
This project is developed in the mono-repository `phpDocumentor Guides <https://github.com/phpDocumentor/guides>`__.
The repository you are currently in gets auto-created by splitting the mono-repository. You **must not** contribute
to this repository directly but always to the mono-repository linked above.
Create Issues
=============
* If you find something missing or something is wrong in this library, you are welcome to write an issue
describing the problem: `Issues on GitHub <https://github.com/phpDocumentor/guides/issues>`__.
* If you can, please try to fix the problem yourself.
Make changes (create pull requests)
===================================
See the `Contribution chapter <https://docs.phpdoc.org/components/guides/guides/contributions/index.html>`__ in the
`Documentation` <https://docs.phpdoc.org/components/guides/guides/index.html>`__.

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2010 Mike van Riel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,37 @@
.. image:: https://poser.pugx.org/phpdocumentor/guides-restructured-text/require/php
:alt: PHP Version Require
:target: https://packagist.org/packages/phpdocumentor/guides-restructured-text
.. image:: https://poser.pugx.org/phpdocumentor/guides-restructured-text/v/stable
:alt: Latest Stable Version
:target: https://packagist.org/packages/phpdocumentor/guides-restructured-text
.. image:: https://poser.pugx.org/phpdocumentor/guides-restructured-text/v/unstable
:alt: Latest Unstable Version
:target: https://packagist.org/packages/phpdocumentor/guides-restructured-text
.. image:: https://poser.pugx.org/phpdocumentor/guides-restructured-text/d/total
:alt: Total Downloads
:target: https://packagist.org/packages/phpdocumentor/guides-restructured-text
.. image:: https://poser.pugx.org/phpdocumentor/guides-restructured-text/d/monthly
:alt: Monthly Downloads
:target: https://packagist.org/packages/phpdocumentor/guides-restructured-text
====================
phpDocumentor Guides
====================
This repository is part of `phpDocumentor's Guides library <https://github.com/phpDocumentor/guides>`__, a framework
designed to take hand-written documentation in code repositories and create an AST (abstract syntax tree) from it.
This AST is then fed to a renderer, which produces the desired output, such as HTML.
The package `phpdocumentor/guides-restructured-text <https://packagist.org/packages/phpdocumentor/guides-restructured-text>`__ adds
`reStructuredText Markup <https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html>`__ support to the
phpDocumentor's Guides library.
:Mono-Repository: https://github.com/phpDocumentor/guides
:Documentation: https://docs.phpdoc.org/components/guides/guides/index.html
:Packagist: https://packagist.org/packages/phpdocumentor/guides-restructured-text
:Contribution: https://github.com/phpDocumentor/guides/tree/main/CONTRIBUTING.rst

View File

@@ -0,0 +1,35 @@
{
"name": "phpdocumentor/guides-restructured-text",
"description": "Adds reStructuredText Markup support to the phpDocumentor's Guides library.",
"type": "library",
"license": "MIT",
"homepage": "https://www.phpdoc.org",
"config": {
"sort-packages": true
},
"autoload": {
"psr-4": {
"phpDocumentor\\Guides\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"phpDocumentor\\Guides\\": [
"tests/unit/"
]
}
},
"minimum-stability": "stable",
"require": {
"php": "^8.1",
"doctrine/lexer": "^3.0",
"phpdocumentor/guides": "^1.0 || ^2.0",
"webmozart/assert": "^1.11",
"league/csv": "^9.27.0"
},
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
}
}

View File

@@ -0,0 +1,382 @@
<?php
declare(strict_types=1);
use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface;
use phpDocumentor\Guides\RestructuredText\Directives\AdmonitionDirective;
use phpDocumentor\Guides\RestructuredText\Directives\AttentionDirective;
use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective;
use phpDocumentor\Guides\RestructuredText\Directives\BreadcrumbDirective;
use phpDocumentor\Guides\RestructuredText\Directives\CautionDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ClassDirective;
use phpDocumentor\Guides\RestructuredText\Directives\CodeBlockDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ConfigurationBlockDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ConfvalDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ContainerDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ContentsDirective;
use phpDocumentor\Guides\RestructuredText\Directives\CsvTableDirective;
use phpDocumentor\Guides\RestructuredText\Directives\DangerDirective;
use phpDocumentor\Guides\RestructuredText\Directives\DefaultRoleDirective;
use phpDocumentor\Guides\RestructuredText\Directives\DeprecatedDirective;
use phpDocumentor\Guides\RestructuredText\Directives\DocumentBlockDirective;
use phpDocumentor\Guides\RestructuredText\Directives\EpigraphDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ErrorDirective;
use phpDocumentor\Guides\RestructuredText\Directives\FigureDirective;
use phpDocumentor\Guides\RestructuredText\Directives\GeneralDirective;
use phpDocumentor\Guides\RestructuredText\Directives\HighlightDirective;
use phpDocumentor\Guides\RestructuredText\Directives\HighlightsDirective;
use phpDocumentor\Guides\RestructuredText\Directives\HintDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ImageDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ImportantDirective;
use phpDocumentor\Guides\RestructuredText\Directives\IncludeDirective;
use phpDocumentor\Guides\RestructuredText\Directives\IndexDirective;
use phpDocumentor\Guides\RestructuredText\Directives\LaTeXMain;
use phpDocumentor\Guides\RestructuredText\Directives\ListTableDirective;
use phpDocumentor\Guides\RestructuredText\Directives\LiteralincludeDirective;
use phpDocumentor\Guides\RestructuredText\Directives\MathDirective;
use phpDocumentor\Guides\RestructuredText\Directives\MenuDirective;
use phpDocumentor\Guides\RestructuredText\Directives\MetaDirective;
use phpDocumentor\Guides\RestructuredText\Directives\NoteDirective;
use phpDocumentor\Guides\RestructuredText\Directives\OptionDirective;
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\CodeNodeOptionMapper;
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\DefaultCodeNodeOptionMapper;
use phpDocumentor\Guides\RestructuredText\Directives\PullQuoteDirective;
use phpDocumentor\Guides\RestructuredText\Directives\RawDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ReplaceDirective;
use phpDocumentor\Guides\RestructuredText\Directives\RoleDirective;
use phpDocumentor\Guides\RestructuredText\Directives\SectionauthorDirective;
use phpDocumentor\Guides\RestructuredText\Directives\SeeAlsoDirective;
use phpDocumentor\Guides\RestructuredText\Directives\SidebarDirective;
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TabDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TableDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TabsDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TestLoggerDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TipDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TitleDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ToctreeDirective;
use phpDocumentor\Guides\RestructuredText\Directives\TodoDirective;
use phpDocumentor\Guides\RestructuredText\Directives\VersionAddedDirective;
use phpDocumentor\Guides\RestructuredText\Directives\VersionChangedDirective;
use phpDocumentor\Guides\RestructuredText\Directives\WarningDirective;
use phpDocumentor\Guides\RestructuredText\Directives\YoutubeDirective;
use phpDocumentor\Guides\RestructuredText\MarkupLanguageParser;
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContextFactory;
use phpDocumentor\Guides\RestructuredText\Parser\InlineParser;
use phpDocumentor\Guides\RestructuredText\Parser\Interlink\DefaultInterlinkParser;
use phpDocumentor\Guides\RestructuredText\Parser\Interlink\InterlinkParser;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\AnnotationRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\BlockQuoteRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\CommentRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\DefinitionListRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\DirectiveContentRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\DirectiveRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\DocumentRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\EnumeratedListRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\AbstractFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\AddressFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\AuthorFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\AuthorsFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\ContactFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\CopyrightFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\DateFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\DedicationFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\FieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\NavigationTitleFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\NocommentsFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\NosearchFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\OrganizationFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\OrphanFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\ProjectFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\RevisionFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\TocDepthFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldList\VersionFieldListItemRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\FieldListRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\GridTableRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\InlineMarkupRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\InlineRules\InlineRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\LineBlockRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\LinkRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\ListRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\LiteralBlockRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\ParagraphRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\RuleContainer;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\SectionRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\SimpleTableRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Table\GridTableBuilder;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\TitleRule;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\TransitionRule;
use phpDocumentor\Guides\RestructuredText\TextRoles\AbbreviationTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\ApiClassTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\DefaultTextRoleFactory;
use phpDocumentor\Guides\RestructuredText\TextRoles\DocReferenceTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericLinkProvider;
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericReferenceTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\LiteralTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\MathTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\NbspTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\ReferenceTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\SpanTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
use phpDocumentor\Guides\RestructuredText\Toc\GlobSearcher;
use phpDocumentor\Guides\RestructuredText\Toc\ToctreeBuilder;
use phpDocumentor\Guides\Settings\SettingsManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\inline_service;
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
return static function (ContainerConfigurator $container): void {
$container->services()
->defaults()
->autowire()
->autoconfigure()
->bind('$bodyElements', service('phpdoc.guides.parser.rst.body_elements'))
->bind('$structuralElements', service('phpdoc.guides.parser.rst.structural_elements'))
->instanceof(BaseDirective::class)
->tag('phpdoc.guides.directive')
->instanceof(FieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->instanceof(InlineRule::class)
->tag('phpdoc.guides.parser.rst.inline_rule')
->instanceof(TextRole::class)
->tag('phpdoc.guides.parser.rst.text_role')
->instanceof(SubDirective::class)
->bind('$startingRule', service(DirectiveContentRule::class))
->load(
'phpDocumentor\\Guides\RestructuredText\\Parser\\Productions\\InlineRules\\',
'../../src/RestructuredText/Parser/Productions/InlineRules',
)
->load(
'phpDocumentor\\Guides\RestructuredText\\NodeRenderers\\Html\\',
'../../src/RestructuredText/NodeRenderers/Html',
)
->tag('phpdoc.guides.noderenderer.html')
->load(
'phpDocumentor\\Guides\RestructuredText\\NodeRenderers\\LaTeX\\',
'../../src/RestructuredText/NodeRenderers/LaTeX',
)
->tag('phpdoc.guides.noderenderer.tex')
->set(GenericLinkProvider::class)
->set(DirectiveContentRule::class)
->set(DocReferenceTextRole::class)
->set(GenericReferenceTextRole::class)
->set(ReferenceTextRole::class)
->set(AbbreviationTextRole::class)
->set(ApiClassTextRole::class)
->set(MathTextRole::class)
->set(LiteralTextRole::class)
->set(NbspTextRole::class)
->set(SpanTextRole::class)
->set(GeneralDirective::class)
->set(AdmonitionDirective::class)
->set(AttentionDirective::class)
->set(BreadcrumbDirective::class)
->set(CautionDirective::class)
->set(ClassDirective::class)
->set(CodeBlockDirective::class)
->args([
'$codeNodeOptionMapper' => service(CodeNodeOptionMapper::class),
])
->set(ConfvalDirective::class)
->set(ConfigurationBlockDirective::class)
->args([
'$languageLabels' => param('phpdoc.rst.code_language_labels'),
])
->set(ContainerDirective::class)
->set(ContentsDirective::class)
->arg('$documentNameResolver', service(DocumentNameResolverInterface::class))
->set(CsvTableDirective::class)
->arg('$productions', service('phpdoc.guides.parser.rst.body_elements'))
->set(DangerDirective::class)
->set(DefaultRoleDirective::class)
->set(DeprecatedDirective::class)
->set(DocumentBlockDirective::class)
->set(EpigraphDirective::class)
->set(ErrorDirective::class)
->set(FigureDirective::class)
->set(HighlightDirective::class)
->set(HighlightsDirective::class)
->set(HintDirective::class)
->set(ImageDirective::class)
->set(ImportantDirective::class)
->set(IncludeDirective::class)
->arg('$startingRule', service(DocumentRule::class))
->set(IndexDirective::class)
->set(LaTeXMain::class)
->set(ListTableDirective::class)
->set(LiteralincludeDirective::class)
->args([
'$codeNodeOptionMapper' => service(
CodeNodeOptionMapper::class,
),
])
->set(MathDirective::class)
->set(MetaDirective::class)
->set(NoteDirective::class)
->set(OptionDirective::class)
->set(PullQuoteDirective::class)
->set(RawDirective::class)
->set(ReplaceDirective::class)
->set(RoleDirective::class)
->set(SectionauthorDirective::class)
->set(SeeAlsoDirective::class)
->set(SidebarDirective::class)
->set(TableDirective::class)
->set(TestLoggerDirective::class)
->set(TipDirective::class)
->set(TabDirective::class)
->set(TabsDirective::class)
->set(TitleDirective::class)
->set(ToctreeDirective::class)
->bind('$startingRule', service(InlineMarkupRule::class))
->set(MenuDirective::class)
->set(TodoDirective::class)
->set(VersionAddedDirective::class)
->set(VersionChangedDirective::class)
->set(WarningDirective::class)
->set(YoutubeDirective::class)
->set(GenericTextRole::class, GenericTextRole::class)
->arg('$settingsManager', inline_service(SettingsManager::class))
->set(DefaultTextRoleFactory::class, DefaultTextRoleFactory::class)
->arg('$genericTextRole', service(GenericTextRole::class))
->arg('$defaultTextRole', inline_service(LiteralTextRole::class))
->arg('$textRoles', tagged_iterator('phpdoc.guides.parser.rst.text_role'))
->alias(TextRoleFactory::class, DefaultTextRoleFactory::class)
->set('phpdoc.guides.parser.rst.body_elements', RuleContainer::class)
->set('phpdoc.guides.parser.rst.structural_elements', RuleContainer::class)
->set(InterlinkParser::class, DefaultInterlinkParser::class)
->set(AnnotationRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => AnnotationRule::PRIORITY])
->set(LinkRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => LinkRule::PRIORITY])
->set(LiteralBlockRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => LiteralBlockRule::PRIORITY])
->set(BlockQuoteRule::class)
->arg('$startingRule', service(DirectiveContentRule::class))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => BlockQuoteRule::PRIORITY])
->set(ListRule::class)
->arg('$productions', service('phpdoc.guides.parser.rst.body_elements'))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => ListRule::PRIORITY])
->set(EnumeratedListRule::class)
->arg('$productions', service('phpdoc.guides.parser.rst.body_elements'))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => EnumeratedListRule::PRIORITY])
->set(LineBlockRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => ParagraphRule::PRIORITY + 1])
->set(DirectiveRule::class)
->arg('$directives', tagged_iterator('phpdoc.guides.directive'))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => DirectiveRule::PRIORITY])
->set(CommentRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => CommentRule::PRIORITY])
->set(GridTableRule::class)
->arg('$productions', service('phpdoc.guides.parser.rst.body_elements'))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => GridTableRule::PRIORITY])
->set(GridTableBuilder::class)
->set(SimpleTableRule::class)
->arg('$productions', service('phpdoc.guides.parser.rst.body_elements'))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => SimpleTableRule::PRIORITY])
->set(DefinitionListRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => DefinitionListRule::PRIORITY])
->set(FieldListRule::class)
->arg('$productions', service('phpdoc.guides.parser.rst.body_elements'))
->arg('$fieldListItemRules', tagged_iterator('phpdoc.guides.parser.rst.fieldlist'))
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => FieldListRule::PRIORITY])
->set(ParagraphRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => ParagraphRule::PRIORITY])
->set(TransitionRule::class)
->tag('phpdoc.guides.parser.rst.body_element', ['priority' => TransitionRule::PRIORITY])
->set(InlineMarkupRule::class)
->set(TitleRule::class)
->set(AbstractFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(AddressFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(AuthorFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(AuthorsFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(ContactFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(CopyrightFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(DateFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(DedicationFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(NavigationTitleFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(NocommentsFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(NosearchFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(OrganizationFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(OrphanFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(ProjectFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->args([
'$logger' => service(LoggerInterface::class),
])
->set(RevisionFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(TocDepthFieldListItemRule::class)
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(VersionFieldListItemRule::class)
->args([
'$logger' => service(LoggerInterface::class),
])
->tag('phpdoc.guides.parser.rst.fieldlist')
->set(SectionRule::class)
->tag('phpdoc.guides.parser.rst.structural_element', ['priority' => SectionRule::PRIORITY])
->set(DocumentParserContextFactory::class)
->set(MarkupLanguageParser::class)
->args([
'$startingRule' => service(DocumentRule::class),
])
->tag('phpdoc.guides.parser.markupLanguageParser')
->set(DocumentRule::class)
->set(InlineParser::class)
->arg('$inlineRules', tagged_iterator('phpdoc.guides.parser.rst.inline_rule'))
->arg('$disableLegacyTilde', false)
->set(GlobSearcher::class)
->set(ToctreeBuilder::class)
->set(InlineMarkupRule::class)
->set(DefaultCodeNodeOptionMapper::class)
->alias(CodeNodeOptionMapper::class, DefaultCodeNodeOptionMapper::class);
};

View File

@@ -0,0 +1,21 @@
<dl class="confval">
<dt {%- if node.isNoindex == false %} id="{{ node.anchor }}"{% endif %}>
<code class="sig-name descname"><span class="pre">{{ node.plainContent }}</span></code></dt>
<dd>
<div class="line-block">
{% if node.type != null %} <div class="line"><strong>Type:</strong> {{ renderNode(node.type) }}</div>
{% endif -%}
{%- if node.required %} <div class="line"><strong>Required:</strong> true</div>
{% endif -%}
{% if node.default != null %} <div class="line"><strong>Type:</strong> {{ renderNode(node.default) }}</div>
{% endif -%}
{%- for key, option in node.additionalOptions -%}
<div class="line"><strong>{{ key }}:</strong> {{ renderNode(option) }}</div>
{% endfor -%}
</div>
<div class="confval-description">
{{ renderNode(node.value) }}
</div>
</dd>
</dl>

View File

@@ -0,0 +1 @@
<div class="{{ node.glossary }}">{{ renderNode(node.value) }}</div>

View File

@@ -0,0 +1,3 @@
<div class="hlist columns-{{ node.option('columns') }}">
{{ renderNode(node.value) }}
</div>

View File

@@ -0,0 +1,3 @@
.. {{ node.name }}:: {{ node.plainContent }}
{{ renderNode(node.value) }}

View File

@@ -0,0 +1,3 @@
{%- if 'html' in node.plainContent -%}
{{ renderNode(node.value) }}
{%- endif -%}

View File

@@ -0,0 +1,9 @@
{%- for id in node.additionalIds %}
<a id="{{ id }}"></a>
{% endfor -%}
<dl class="domain-default-option">
<dt class="domain-default-option-name" id="{{ node.id }}">{{ node.plainContent }}</dt>
<dd class="domain-default-option-description">
{{ renderNode(node.value) }}
</dd>
</dl>

View File

@@ -0,0 +1 @@
<div class="rubric{% if node.classes %} {{ node.classesString }}{% endif %}">{{ renderNode(node.content) }}</div>

View File

@@ -0,0 +1 @@
{{ renderNode(tab.value) }}

View File

@@ -0,0 +1,14 @@
<div class="tabs">
<ul>
{% for tab in node.tabs -%}
<li><button type="button" data-tabs="{{ node.key }}" data-target="{{ node.key }}-{{ tab.key }}" class="{{ tab.active ? 'active' }}">{{ renderNode(tab.content) }}</button></li>
{%- endfor %}
</ul>
<div class="tab-content" id="tab-content-{{ node.key }}">
{% for tab in node.tabs -%}
<div class="tab {{- tab.active ? ' active' }}" id="{{ node.key }}-{{ tab.key }}">
{{- renderNode(tab.value) -}}
</div>
{%- endfor %}
</div>
</div>

View File

@@ -0,0 +1,20 @@
{#
A topic is like a block quote with a title, or a self-contained section with no subsections. Use the "topic" directive to indicate a self-contained idea that is separate from the flow of the document. Topics may occur anywhere a section or transition may occur. Body elements and topics may not contain nested topics.
The directive's sole argument is interpreted as the topic title; the next line
must be blank. All subsequent lines make up the topic body, interpreted as body
elements. For example:
.. topic:: Topic Title
Subsequent indented lines comprise
the body of the topic, and are
interpreted as body elements.
https://docutils.sourceforge.io/docs/ref/rst/directives.html#topic
#}
<div class="topic{% if node.classes %} {{ node.classesString }}{% endif %}">
<p class="topic-title">{{ renderNode(node.content) }}</p>
{{ renderNode(node.value) }}
</div>

View File

@@ -0,0 +1,5 @@
{%- if node.classes %}
<div class="{{ node.classesString }}">{{ renderNode(node.value) }}</div>
{% else %}
{{ renderNode(node.value) }}
{% endif -%}

View File

@@ -0,0 +1,6 @@
<div class="versionchange {{ node.type }}">
<p class="versionmodified">{{ node.versionLabel }}</p>
<article>
{{ renderNode(node.value) }}
</article>
</div>

View File

@@ -0,0 +1,16 @@
\begin{description}
\item[\texttt{{ node.plainContent }}]
\hfill \\
\begin{itemize}
\item[\textbf{Type:}] {{ renderNode(node.type) }}
\item[\textbf{Required:}] {% if node.required %}true{% else %}false{% endif %}
\item[\textbf{Default:}] {% if node.default != null %}{{ renderNode(node.default) }}{% else %}N/A{% endif %}
{%- for key, option in node.additionalOptions -%}
\item[\textbf{{ key }}:] {{ renderNode(option) }}
{% endfor -%}
\end{itemize}
\item[Description]
\hfill \\
{{ renderNode(node.value) }}
\end{description}

View File

@@ -0,0 +1,3 @@
{%- if 'tex' in node.plainContent -%}
{{ renderNode(node.value) }}
{%- endif -%}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\DependencyInjection\Compiler;
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
final class TextRolePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$textRoleFactory = $container->findDefinition(TextRoleFactory::class);
$domains = [];
$textRoles = [];
foreach ($container->findTaggedServiceIds('phpdoc.guides.parser.rst.text_role') as $id => $tags) {
foreach ($tags as $tag) {
if (isset($tag['domain'])) {
$domains[$tag['domain']][] = new Reference($id);
continue;
}
$textRoles[] = new Reference($id);
}
}
$textRoleFactory->setArgument('$textRoles', $textRoles);
$textRoleFactory->setArgument('$domains', $domains);
}
}

View File

@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\DependencyInjection;
use phpDocumentor\Guides\RestructuredText\DependencyInjection\Compiler\TextRolePass;
use phpDocumentor\Guides\RestructuredText\Nodes\ConfvalNode;
use phpDocumentor\Guides\RestructuredText\Nodes\OptionNode;
use phpDocumentor\Guides\RestructuredText\Nodes\TabNode;
use phpDocumentor\Guides\RestructuredText\Nodes\TabsNode;
use phpDocumentor\Guides\RestructuredText\Nodes\VersionChangeNode;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use function assert;
use function dirname;
use function phpDocumentor\Guides\DependencyInjection\template;
final class ReStructuredTextExtension extends Extension implements
PrependExtensionInterface,
CompilerPassInterface,
ConfigurationInterface
{
/** @param mixed[] $configs */
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$loader = new PhpFileLoader(
$container,
new FileLocator(dirname(__DIR__, 3) . '/resources/config'),
);
$normalizedLanguageLabels = [];
foreach ($config['code_language_labels'] ?? [] as $item) {
$normalizedLanguageLabels[$item['language']] = $item['label'];
}
$container->setParameter('phpdoc.rst.code_language_labels', $normalizedLanguageLabels);
$loader->load('guides-restructured-text.php');
}
public function prepend(ContainerBuilder $container): void
{
$container->prependExtensionConfig(
'guides',
[
'base_template_paths' => [
dirname(__DIR__, 3) . '/resources/template/html',
dirname(__DIR__, 3) . '/resources/template/latex',
],
'templates' => [
template(ConfvalNode::class, 'body/directive/confval.html.twig'),
template(VersionChangeNode::class, 'body/version-change.html.twig'),
template(ConfvalNode::class, 'body/directive/confval.tex.twig', 'tex'),
template(OptionNode::class, 'body/directive/option.html.twig'),
template(TabNode::class, 'body/directive/tab.html.twig'),
template(TabsNode::class, 'body/directive/tabs.html.twig'),
],
],
);
}
public function process(ContainerBuilder $container): void
{
(new TextRolePass())->process($container);
}
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('rst');
$rootNode = $treeBuilder->getRootNode();
assert($rootNode instanceof ArrayNodeDefinition);
$rootNode
->fixXmlConfig('code_language_label', 'code_language_labels')
->children()
->arrayNode('code_language_labels')
->arrayPrototype()
->children()
->scalarNode('language')
->isRequired()
->end()
->scalarNode('label')
->isRequired()
->end()
->end()
->end()
->end()
->end();
return $treeBuilder;
}
/** @param mixed[] $config */
public function getConfiguration(array $config, ContainerBuilder $container): static
{
return $this;
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\AdmonitionNode;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\ParagraphNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use function array_unshift;
abstract class AbstractAdmonitionDirective extends SubDirective
{
public function __construct(protected Rule $startingRule, private readonly string $name, private readonly string $text)
{
parent::__construct($startingRule);
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
final protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$children = $collectionNode->getChildren();
if ($directive->getDataNode() !== null) {
array_unshift($children, new ParagraphNode([$directive->getDataNode()]));
}
return new AdmonitionNode(
$this->name,
null,
$this->text,
$children,
);
}
final public function getName(): string
{
return $this->name;
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Nodes\VersionChangeNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/** @see https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-versionadded */
abstract class AbstractVersionChangeDirective extends SubDirective
{
public function __construct(protected Rule $startingRule, private readonly string $type, private readonly string $label)
{
parent::__construct($startingRule);
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
final protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return new VersionChangeNode(
$this->type,
$this->label,
$directive->getData(),
$collectionNode->getChildren(),
);
}
final public function getName(): string
{
return $this->type;
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Extend this class to create a directive that does some actions, for example on the parser context, without
* creating a node.
*/
abstract class ActionDirective extends BaseDirective
{
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
$this->processAction($blockContext, $directive);
return null;
}
/**
* @param BlockContext $blockContext the current document context with the content
* of the directive
* @param Directive $directive parsed directive containing options and variable
*/
abstract public function processAction(
BlockContext $blockContext,
Directive $directive,
): void;
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\AdmonitionNode;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function preg_replace;
use function strtolower;
use function trim;
/**
* A generic admonition.
*
* This node allows defining new admonitions from reStructuredText files:
*
* .. admonition:: Screencast
*
* Watch this document in video.
*
* @see https://docutils.sourceforge.io/docs/ref/rst/directives.html#generic-admonition
*/
final class AdmonitionDirective extends SubDirective
{
public function getName(): string
{
return 'admonition';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
// The title argument is required per the RST spec.
// Skip rendering if no title is provided.
if ($directive->getData() === '') {
return null;
}
$name = trim(
preg_replace('/[^0-9a-zA-Z]+/', '-', strtolower($directive->getData())) ?? '',
'-',
);
return new AdmonitionNode(
$name,
$directive->getDataNode(),
$directive->getData(),
$collectionNode->getChildren(),
true,
);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create an attention admonition.
*
* Example:
*
* ```rest
* .. attention::
*
* This is an attention admonition.
* ```
*/
final class AttentionDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'attention', 'Attention');
}
}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\GenericNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
use function array_map;
/**
* A directive is like a function you can call or apply to a block
* It looks like:
*
* .. function:: main
* :arg1: value
* :arg2: otherValue
*
* Some block !
*
* The directive can define variables, create special nodes or change
* the node that directly follows it
*/
abstract class BaseDirective
{
/**
* Get the directive name
*/
abstract public function getName(): string;
/**
* Allow a directive to be registered under multiple names.
*
* Aliases can be used for directives whose name has been deprecated or allows for multiple spellings.
*
* @return string[]
*/
public function getAliases(): array
{
return [];
}
/**
* This is the function called by the parser to process the directive, it can be overloaded
* to do anything with the document, like tweaking nodes or change the parser context
*
* The node that directly follows the directive is also passed to it
*
* @param BlockContext $blockContext the current document context with the content
* of the directive
* @param Directive $directive parsed directive containing options and variable
*/
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
return $this->processNode($blockContext, $directive)
// Ensure options are always available
->withKeepExistingOptions($this->optionsToArray($directive->getOptions()));
}
/**
* This can be overloaded to write a directive that just create one node for the
* document, which is common
*
* The arguments are the same that process
*/
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
return new GenericNode($directive->getVariable(), $directive->getData());
}
/**
* @param DirectiveOption[] $options
*
* @return array<string, scalar|null>
*/
protected function optionsToArray(array $options): array
{
return array_map(static fn (DirectiveOption $option): bool|float|int|string|null => $option->getValue(), $options);
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\BreadCrumbNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* The "breadcrumb" directive displays a breadcrumb of the current. It does not exist in Sphinx or the
* reST standard yet.
*
* It takes neither arguments nor content.
*
* Usage:
*
* ```
* .. breadcrumb::
* ```
*/
final class BreadcrumbDirective extends BaseDirective
{
public function getName(): string
{
return 'breadcrumb';
}
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
return new BreadCrumbNode();
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a caution admonition.
*
* Example:
*
* ```rest
* .. caution::
*
* This is a caution admonition.
* ```
*/
final class CautionDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'caution', 'Caution');
}
}

View File

@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\ClassNode;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use Symfony\Component\String\Slugger\AsciiSlugger;
use function array_map;
use function array_merge;
use function explode;
final class ClassDirective extends SubDirective
{
public function getName(): string
{
return 'class';
}
/**
* When the default domain contains a class directive, this directive will be shadowed. Therefore, Sphinx re-exports it as rst-class.
*
* See https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rstclass
*
* @return string[]
*/
public function getAliases(): array
{
return ['rst-class'];
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$classes = explode(' ', $directive->getData());
$normalizedClasses = array_map(
static fn (string $class): string => (new AsciiSlugger())->slug($class)->lower()->toString(),
$classes,
);
$collectionNode->setClasses($normalizedClasses);
if ($collectionNode->getChildren() === []) {
$classNode = new ClassNode($directive->getData());
$classNode->setClasses($classes);
return $classNode;
}
$this->setNodesClasses($collectionNode->getChildren(), $classes);
return new CollectionNode($collectionNode->getChildren());
}
/**
* @param Node[] $nodes
* @param string[] $classes
*/
private function setNodesClasses(array $nodes, array $classes): void
{
foreach ($nodes as $node) {
$node->setClasses(array_merge($node->getClasses(), $classes));
if (!($node instanceof DocumentNode)) {
continue;
}
$this->setNodesClasses($node->getNodes(), $classes);
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\CodeNodeOptionMapper;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use Psr\Log\LoggerInterface;
use function trim;
/**
* Renders a code block, example:
*
* .. code-block:: php
*
* <?php
*
* echo "Hello world!\n";
*
* @link https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-code-block
*/
final class CodeBlockDirective extends BaseDirective
{
public function __construct(
private readonly LoggerInterface $logger,
private readonly CodeNodeOptionMapper $codeNodeOptionMapper,
) {
}
public function getName(): string
{
return 'code-block';
}
/** {@inheritDoc} */
public function getAliases(): array
{
return ['code', 'parsed-literal'];
}
/** {@inheritDoc} */
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
if ($blockContext->getDocumentIterator()->isEmpty()) {
$this->logger->warning('The code-block has no content. Did you properly indent the code? ', $blockContext->getLoggerInformation());
return null;
}
$node = new CodeNode(
$blockContext->getDocumentIterator()->toArray(),
);
if (trim($directive->getData()) !== '') {
$node->setLanguage(trim($directive->getData()));
} else {
$node->setLanguage($blockContext->getDocumentParserContext()->getCodeBlockDefaultLanguage());
}
$this->codeNodeOptionMapper->apply($node, $directive->getOptions(), $blockContext);
if ($directive->getVariable() !== '') {
$document = $blockContext->getDocumentParserContext()->getDocument();
$document->addVariable($directive->getVariable(), $node);
return null;
}
return $node;
}
}

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Configuration\ConfigurationBlockNode;
use phpDocumentor\Guides\Nodes\Configuration\ConfigurationTab;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use Psr\Log\LoggerInterface;
use Symfony\Component\String\Slugger\AsciiSlugger;
use Symfony\Component\String\Slugger\SluggerInterface;
use function assert;
use function get_debug_type;
use function sprintf;
final class ConfigurationBlockDirective extends SubDirective
{
private SluggerInterface $slugger;
/**
* @param Rule<CollectionNode> $startingRule
* @param array<string, string> $languageLabels
*/
public function __construct(
private LoggerInterface $logger,
Rule $startingRule,
private readonly array $languageLabels = [],
) {
parent::__construct($startingRule);
$this->slugger = new AsciiSlugger();
}
public function getName(): string
{
return 'configuration-block';
}
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$tabs = [];
foreach ($collectionNode->getValue() as $child) {
if (!$child instanceof CodeNode) {
$this->logger->warning(
sprintf('The ".. configuration-block::" directive only supports code blocks, "%s" given.', get_debug_type($child)),
$blockContext->getLoggerInformation(),
);
continue;
}
$language = $child->getLanguage();
assert($language !== null);
$label = $this->languageLabels[$language] ?? $this->slugger->slug($language, ' ')->title()->toString();
$tabs[] = new ConfigurationTab(
$label,
$this->slugger->slug($label)->lower()->toString(),
$child,
);
}
return new ConfigurationBlockNode($tabs);
}
}

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer;
use phpDocumentor\Guides\RestructuredText\Nodes\ConfvalNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\InlineParser;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericLinkProvider;
use Psr\Log\LoggerInterface;
use function in_array;
use function trim;
/**
* The confval directive configuration values.
*
* https://sphinx-toolbox.readthedocs.io/en/stable/extensions/confval.html
*/
final class ConfvalDirective extends SubDirective
{
public const NAME = 'confval';
/** @param Rule<CollectionNode> $startingRule */
public function __construct(
protected Rule $startingRule,
GenericLinkProvider $genericLinkProvider,
private readonly AnchorNormalizer $anchorReducer,
private readonly InlineParser $inlineParser,
private readonly LoggerInterface|null $logger = null,
) {
parent::__construct($startingRule);
$genericLinkProvider->addGenericLink(self::NAME, ConfvalNode::LINK_TYPE, ConfvalNode::LINK_PREFIX);
}
public function getName(): string
{
return self::NAME;
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$id = $directive->getData();
if ($directive->hasOption('name')) {
$id = (string) $directive->getOption('name')->getValue();
}
$id = $this->anchorReducer->reduceAnchor($id);
$type = null;
$required = false;
$default = null;
$additionalOptions = [];
if (trim($directive->getData()) === '') {
if ($this->logger !== null) {
$this->logger->warning('A directive must have a title: .. confval:: [some_title]', $blockContext->getLoggerInformation());
}
}
if ($directive->hasOption('type')) {
$type = $this->inlineParser->parse($directive->getOptionString('type'), $blockContext);
}
$required = $directive->getOptionBool('required');
if ($directive->hasOption('default')) {
$default = $this->inlineParser->parse($directive->getOptionString('default'), $blockContext);
}
$noindex = $directive->getOptionBool('noindex');
foreach ($directive->getOptions() as $option) {
if (in_array($option->getName(), ['type', 'required', 'default', 'noindex', 'name'], true)) {
continue;
}
$additionalOptions[$option->getName()] = $this->inlineParser->parse($option->toString(), $blockContext);
}
return new ConfvalNode($id, $directive->getData(), $type, $required, $default, $additionalOptions, $collectionNode->getChildren(), $noindex);
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Nodes\ContainerNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Divs a sub document in a div with a given class or set of classes.
*
* @link https://docutils.sourceforge.io/docs/ref/rst/directives.html#container
*/
final class ContainerDirective extends SubDirective
{
public function getName(): string
{
return 'container';
}
/** {@inheritDoc} */
public function getAliases(): array
{
return ['div'];
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return (new ContainerNode($collectionNode->getChildren()))->withOptions(['class' => $directive->getData()]);
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\Menu\ContentMenuNode;
use phpDocumentor\Guides\Nodes\Menu\SectionMenuEntryNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Standarad rst `contents` directive
*
* Displays a table of content of the current page
*/
final class ContentsDirective extends BaseDirective
{
public function __construct(
private readonly DocumentNameResolverInterface $documentNameResolver,
) {
}
public function getName(): string
{
return 'contents';
}
/** {@inheritDoc} */
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
$options = $directive->getOptions();
$absoluteUrl = $this->documentNameResolver->absoluteUrl(
$blockContext->getDocumentParserContext()->getContext()->getDirName(),
$blockContext->getDocumentParserContext()->getContext()->getCurrentFileName(),
);
return (new ContentMenuNode([new SectionMenuEntryNode($absoluteUrl)]))
->withOptions($this->optionsToArray($options))
->withCaption($directive->getDataNode())
->withLocal($directive->hasOption('local'));
}
}

View File

@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use League\Csv\Reader;
use phpDocumentor\Guides\Nodes\GenericNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\ParagraphNode;
use phpDocumentor\Guides\Nodes\Table\TableColumn;
use phpDocumentor\Guides\Nodes\Table\TableRow;
use phpDocumentor\Guides\Nodes\TableNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\RuleContainer;
use Psr\Log\LoggerInterface;
use function array_filter;
use function array_map;
use function array_merge;
use function assert;
use function count;
use function explode;
use function implode;
use function is_string;
use function strval;
use function trim;
/**
* Render csv file as table
*
* .. csv-table:: Table Title
* :file: CSV file path and name
* :widths: 30, 70
* :header-rows: 1
*/
final class CsvTableDirective extends BaseDirective
{
public function __construct(
private RuleContainer $productions,
private LoggerInterface $logger,
) {
}
public function getName(): string
{
return 'csv-table';
}
/** {@inheritDoc} */
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
$options = $this->optionsToArray($directive->getOptions());
if ($directive->hasOption('file')) {
$csvStream = $blockContext->getDocumentParserContext()
->getContext()
->getOrigin()
->readStream((string) $directive->getOption('file')->getValue());
if ($csvStream === false) {
$this->logger->error(
'Unable to read CSV file {file}',
array_merge(['file' => $directive->getOption('file')->getValue()], $blockContext->getLoggerInformation()),
);
return new GenericNode('csv-table');
}
$csv = Reader::from($csvStream);
} else {
$lines = $blockContext->getDocumentIterator()->toArray();
$csv = Reader::fromString(implode("\n", $lines));
}
if ($directive->getOption('header-rows')->getValue() !== null) {
$csv->setHeaderOffset((int) ($directive->getOption('header-rows')->getValue()) - 1);
}
$header = null;
if ($directive->hasOption('header')) {
$headerCsv = Reader::fromString($directive->getOption('header')->toString());
$header = new TableRow();
foreach ($headerCsv->first() as $column) {
$columnNode = new TableColumn($column, 1, []);
$header->addColumn($this->buildColumn($columnNode, $blockContext, $this->productions));
}
} elseif (empty($csv->getHeader()) === false) {
$header = new TableRow();
foreach ($csv->getHeader() as $column) {
$columnNode = new TableColumn($column, 1, []);
$header->addColumn($this->buildColumn($columnNode, $blockContext, $this->productions));
}
}
$rows = [];
foreach ($csv->getRecords() as $record) {
$tableRow = new TableRow();
foreach ($record as $column) {
assert(is_string($column) || $column === null);
$columnNode = new TableColumn($column ?? '', 1, []);
$tableRow->addColumn($this->buildColumn($columnNode, $blockContext, $this->productions));
}
$rows[] = $tableRow;
}
$tableNode = new TableNode($rows, array_filter([$header]));
if (isset($options['widths']) && $options['widths'] !== 'auto' && $options['widths'] !== 'grid') {
$colWidths = array_map('intval', explode(',', strval($options['widths'])));
// A list of integers is used instead of the input column widths. Implies "grid".
$options['widths'] = 'grid';
$tableNode = $tableNode->withColumnWidth($colWidths);
}
$tableNode = $tableNode->withOptions($options);
return $tableNode;
}
private function buildColumn(
TableColumn $col,
BlockContext $blockContext,
RuleContainer $productions,
): TableColumn {
$content = $col->getContent();
$subContext = new BlockContext($blockContext->getDocumentParserContext(), $content, false, $blockContext->getDocumentIterator()->key());
while ($subContext->getDocumentIterator()->valid()) {
$productions->apply($subContext, $col);
}
$nodes = $col->getChildren();
if (count($nodes) > 1) {
return $col;
}
// the list item offset is determined by the offset of the first text
$firstNode = $nodes[0] ?? null;
if ($firstNode instanceof ParagraphNode) {
return new TableColumn(trim($content), $col->getColSpan(), $firstNode->getChildren(), $col->getRowSpan());
}
return $col;
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a danger admonition.
*
* Example:
*
* ```rest
* .. danger::
*
* This is a danger admonition.
* ```
*/
class DangerDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'danger', 'Danger');
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* sets the default interpreted text role, the role that is used for interpreted text without an explicit role.
*
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#default-role
*/
final class DefaultRoleDirective extends ActionDirective
{
public function getName(): string
{
return 'default-role';
}
public function processAction(
BlockContext $blockContext,
Directive $directive,
): void {
$name = $directive->getData();
$blockContext->getDocumentParserContext()->getTextRoleFactoryForDocument()->setDefaultTextRole($name);
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
final class DeprecatedDirective extends AbstractVersionChangeDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'deprecated', 'Deprecated since version %s');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\DocumentBlockNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
final class DocumentBlockDirective extends SubDirective
{
public function getName(): string
{
return 'documentblock';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$identifier = ((string) $directive->getOption('identifier')->getValue());
return new DocumentBlockNode(
$collectionNode->getChildren(),
$identifier,
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\QuoteNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* An epigraph is an apposite (suitable, apt, or pertinent) short inscription,
* often a quotation or poem, at the beginning of a document or section.
* The "epigraph" directive produces an "epigraph"-class block quote.
*
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#epigraph
*/
final class EpigraphDirective extends SubDirective
{
public function getName(): string
{
return 'epigraph';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return new QuoteNode($collectionNode->getChildren(), ['epigraph']);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a error admonition.
*
* Example:
*
* ```rest
* .. error::
*
* This is an error admonition.
* ```
*/
final class ErrorDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'error', 'Error');
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\FigureNode;
use phpDocumentor\Guides\Nodes\ImageNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use function dirname;
/**
* Renders an image, example :
*
* .. figure:: image.jpg
* :width: 100
* :alt: An image
*
* Here is an awesome caption
*/
final class FigureDirective extends SubDirective
{
public function __construct(
private readonly DocumentNameResolverInterface $documentNameResolver,
protected Rule $startingRule,
) {
parent::__construct($startingRule);
}
public function getName(): string
{
return 'figure';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$image = new ImageNode($this->documentNameResolver->absoluteUrl(
dirname($blockContext->getDocumentParserContext()->getContext()->getCurrentAbsolutePath()),
$directive->getData(),
));
$scalarOptions = $this->optionsToArray($directive->getOptions());
$image = $image->withOptions([
'width' => $scalarOptions['width'] ?? null,
'height' => $scalarOptions['height'] ?? null,
'alt' => $scalarOptions['alt'] ?? null,
'scale' => $scalarOptions['scale'] ?? null,
'target' => $scalarOptions['target'] ?? null,
'class' => $scalarOptions['class'] ?? null,
'name' => $scalarOptions['name'] ?? null,
'align' => $scalarOptions['align'] ?? null,
]);
return new FigureNode($image, new CollectionNode($collectionNode->getChildren()));
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Nodes\GeneralDirectiveNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use phpDocumentor\Guides\Settings\SettingsManager;
use function explode;
use function in_array;
use function str_contains;
/**
* A catch-all directive, the content is treated as content, the options passed on
*/
final class GeneralDirective extends SubDirective
{
/** @param Rule<CollectionNode> $startingRule */
public function __construct(
Rule $startingRule,
private readonly SettingsManager $settingsManager,
) {
parent::__construct($startingRule);
}
public function getName(): string
{
return '';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
if (str_contains($directive->getName(), ':')) {
[$domainName, $directiveName] = explode(':', $directive->getName());
if (in_array($domainName, $this->settingsManager->getProjectSettings()->getIgnoredDomains(), true)) {
return $collectionNode;
}
}
return new GeneralDirectiveNode(
$directive->getName(),
$directive->getData(),
$directive->getDataNode() ?? new InlineCompoundNode(),
$collectionNode->getChildren(),
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function trim;
/**
* https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-highlight
*/
final class HighlightDirective extends ActionDirective
{
public function getName(): string
{
return 'highlight';
}
public function processAction(
BlockContext $blockContext,
Directive $directive,
): void {
$blockContext->getDocumentParserContext()->setCodeBlockDefaultLanguage(trim($directive->getData()));
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\QuoteNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Highlights summarize the main points of a document or section, often consisting of a list.
* The "highlights" directive produces a "highlights"-class block quote.
*
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#highlights
*/
final class HighlightsDirective extends SubDirective
{
public function getName(): string
{
return 'highlights';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return new QuoteNode($collectionNode->getChildren(), ['highlights']);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a hint admonition.
*
* Example:
*
* ```rest
* .. hint::
*
* This is a hint admonition.
* ```
*/
final class HintDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'hint', 'Hint');
}
}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\ImageNode;
use phpDocumentor\Guides\Nodes\Inline\DocReferenceNode;
use phpDocumentor\Guides\Nodes\Inline\HyperLinkNode;
use phpDocumentor\Guides\Nodes\Inline\LinkInlineNode;
use phpDocumentor\Guides\Nodes\Inline\ReferenceNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\ReferenceResolvers\DocumentNameResolverInterface;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function dirname;
use function filter_var;
use function preg_match;
use const FILTER_VALIDATE_EMAIL;
use const FILTER_VALIDATE_URL;
/**
* Renders an image, example :
*
* .. image:: image.jpg
* :width: 100
* :title: An image
*/
final class ImageDirective extends BaseDirective
{
/** @see https://regex101.com/r/9dUrzu/3 */
public const REFERENCE_REGEX = '/^([a-zA-Z0-9-_]+)_$/';
/** @see https://regex101.com/r/6vPoiA/2 */
public const REFERENCE_ESCAPED_REGEX = '/^`([^`]+)`_$/';
public function __construct(
private readonly DocumentNameResolverInterface $documentNameResolver,
) {
}
public function getName(): string
{
return 'image';
}
/** {@inheritDoc} */
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
$node = new ImageNode(
$this->documentNameResolver->absoluteUrl(
dirname($blockContext->getDocumentParserContext()->getContext()->getCurrentAbsolutePath()),
$directive->getData(),
),
);
if ($directive->hasOption('target')) {
$targetReference = (string) $directive->getOption('target')->getValue();
$node->setTarget($this->resolveLinkTarget($targetReference));
}
return $node;
}
private function resolveLinkTarget(string $targetReference): LinkInlineNode
{
if (filter_var($targetReference, FILTER_VALIDATE_EMAIL)) {
return new HyperLinkNode([], $targetReference);
}
if (filter_var($targetReference, FILTER_VALIDATE_URL)) {
return new HyperLinkNode([], $targetReference);
}
if (preg_match(self::REFERENCE_REGEX, $targetReference, $matches)) {
return new ReferenceNode($matches[1]);
}
if (preg_match(self::REFERENCE_ESCAPED_REGEX, $targetReference, $matches)) {
return new ReferenceNode($matches[1]);
}
return new DocReferenceNode($targetReference);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a important admonition.
*
* Example:
*
* ```rest
* .. important::
*
* This is a important admonition.
* ```
*/
final class ImportantDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'important', 'Important');
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\LiteralBlockNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\DocumentRule;
use RuntimeException;
use function array_key_exists;
use function explode;
use function sprintf;
use function str_replace;
final class IncludeDirective extends BaseDirective
{
public function __construct(private readonly DocumentRule $startingRule)
{
}
public function getName(): string
{
return 'include';
}
/** {@inheritDoc} */
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
$parserContext = $blockContext->getDocumentParserContext()->getParser()->getParserContext();
$path = $parserContext->absoluteRelativePath($directive->getData());
$origin = $parserContext->getOrigin();
if (!$origin->has($path)) {
throw new RuntimeException(
sprintf('Include "%s" (%s) does not exist or is not readable.', $directive->getData(), $path),
);
}
$contents = $origin->read($path);
if ($contents === false) {
throw new RuntimeException(sprintf('Could not load file from path %s', $path));
}
if (array_key_exists('literal', $directive->getOptions())) {
$contents = str_replace("\r\n", "\n", $contents);
return new LiteralBlockNode($contents);
}
if (array_key_exists('code', $directive->getOptions())) {
$contents = str_replace("\r\n", "\n", $contents);
$codeNode = new CodeNode(
explode('\n', $contents),
);
$codeNode->setLanguage((string) $directive->getOption('code')->getValue());
return $codeNode;
}
$currentDocument = $blockContext->getDocumentParserContext()->getDocument();
$subContext = new BlockContext($blockContext->getDocumentParserContext(), $contents);
$document = $this->startingRule->apply($subContext);
//Reset the document, as it was changed by the apply method.
$blockContext->getDocumentParserContext()->setDocument($currentDocument);
return new CollectionNode($document->getChildren());
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
final class IndexDirective extends SubDirective
{
public function getName(): string
{
return 'index';
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\MainNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Marks the document as LaTeX main
*/
final class LaTeXMain extends BaseDirective
{
public function getName(): string
{
return 'latex-main';
}
/** {@inheritDoc} */
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
return new MainNode($directive->getData());
}
}

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\ListItemNode;
use phpDocumentor\Guides\Nodes\ListNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\Table\TableColumn;
use phpDocumentor\Guides\Nodes\Table\TableRow;
use phpDocumentor\Guides\Nodes\TableNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use Psr\Log\LoggerInterface;
use function array_map;
use function array_shift;
use function assert;
use function count;
use function explode;
use function sprintf;
use function strval;
class ListTableDirective extends SubDirective
{
public function __construct(
protected Rule $startingRule,
private readonly LoggerInterface $logger,
) {
parent::__construct($startingRule);
}
public function getName(): string
{
return 'list-table';
}
/** {@inheritDoc} */
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$options = $this->optionsToArray($directive->getOptions());
if (count($collectionNode->getChildren()) === 0) {
$this->logger->warning('The list-table directive is missing its content. It has to contain exactly one list with sub-lists of equal count. ', $blockContext->getLoggerInformation());
return null;
}
if (count($collectionNode->getChildren()) > 1) {
$this->logger->warning(
sprintf('The list-table must have exactly one list as sub-content. %s nodes found.', count($collectionNode->getChildren())),
$blockContext->getLoggerInformation(),
);
}
$subNode = $collectionNode->getChildren()[0];
if (!$subNode instanceof ListNode) {
$this->logger->warning(
sprintf('The list-table must have exactly one list as sub-content. A node of type %s found.', $subNode::class),
$blockContext->getLoggerInformation(),
);
return null;
}
$tableData = [];
foreach ($subNode->getChildren() as $listItemNode) {
assert($listItemNode instanceof ListItemNode);
$tableRow = new TableRow();
foreach ($listItemNode->getChildren() as $subListNode) {
if (!$subListNode instanceof ListNode) {
$this->logger->warning(
sprintf('The list-table must have a nested list of 2 levels. A node of type %s was found on level 2.', $subListNode::class),
$blockContext->getLoggerInformation(),
);
continue;
}
foreach ($subListNode->getChildren() as $subListItemNode) {
assert($subListItemNode instanceof ListItemNode);
$tableRow->addColumn(new TableColumn('', 1, $subListItemNode->getChildren()));
}
}
$tableData[] = $tableRow;
}
$headerRows = [];
if ($directive->getOption('header-rows')->getValue() !== null) {
for ($i = $directive->getOption('header-rows')->getValue(); $i > 0; $i--) {
if (empty($tableData)) {
break;
}
$headerRows[] = array_shift($tableData);
}
}
$tableNode = new TableNode($tableData, $headerRows);
if (isset($options['widths']) && $options['widths'] !== 'auto' && $options['widths'] !== 'grid') {
$colWidths = array_map('intval', explode(',', strval($options['widths'])));
// A list of integers is used instead of the input column widths. Implies "grid".
$options['widths'] = 'grid';
$tableNode = $tableNode->withColumnWidth($colWidths);
}
$tableNode = $tableNode->withOptions($options);
return $tableNode;
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\CodeNodeOptionMapper;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use RuntimeException;
use function explode;
use function sprintf;
final class LiteralincludeDirective extends BaseDirective
{
public function __construct(private readonly CodeNodeOptionMapper $codeNodeOptionMapper)
{
}
public function getName(): string
{
return 'literalinclude';
}
/** {@inheritDoc} */
public function processNode(
BlockContext $blockContext,
Directive $directive,
): Node {
$parser = $blockContext->getDocumentParserContext()->getParser();
$parserContext = $parser->getParserContext();
$path = $parserContext->absoluteRelativePath($directive->getData());
$origin = $parserContext->getOrigin();
if (!$origin->has($path)) {
throw new RuntimeException(
sprintf('Include "%s" (%s) does not exist or is not readable.', $directive->getData(), $path),
);
}
$contents = $origin->read($path);
if ($contents === false) {
throw new RuntimeException(sprintf('Could not load file from path %s', $path));
}
$codeNode = new CodeNode(explode("\n", $contents));
$this->codeNodeOptionMapper->apply($codeNode, $directive->getOptions(), $blockContext);
return $codeNode;
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\MathNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use Psr\Log\LoggerInterface;
/**
* Renders a code block, example:
*
* .. code-block:: php
*
* <?php
*
* echo "Hello world!\n";
*
* @link https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-code-block
*/
final class MathDirective extends BaseDirective
{
public function __construct(
private readonly LoggerInterface $logger,
) {
}
public function getName(): string
{
return 'math';
}
/** {@inheritDoc} */
public function getAliases(): array
{
return [];
}
/** {@inheritDoc} */
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
if ($blockContext->getDocumentIterator()->isEmpty()) {
$this->logger->warning('The math directive has no content. Did you properly indent the code? ', $blockContext->getLoggerInformation());
return null;
}
return new MathNode(
$blockContext->getDocumentIterator()->toArray(),
);
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\Menu\GlobMenuEntryNode;
use phpDocumentor\Guides\Nodes\Menu\NavMenuNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
use phpDocumentor\Guides\RestructuredText\Toc\ToctreeBuilder;
use phpDocumentor\Guides\Settings\ProjectSettings;
use phpDocumentor\Guides\Settings\SettingsManager;
use function count;
/**
* A menu directives displays a menu in the page. In opposition to a toctree directive the menu
* is for display only. It does not change the position of document in the document tree and can therefore be included
* all pages as navigation.
*
* By default, it displays a menu of the pages on level 1 up to level 2.
*/
final class MenuDirective extends BaseDirective
{
private SettingsManager $settingsManager;
public function __construct(
private readonly ToctreeBuilder $toctreeBuilder,
SettingsManager|null $settingsManager = null,
) {
// if for backward compatibility reasons no settings manager was passed, use the defaults
$this->settingsManager = $settingsManager ?? new SettingsManager(new ProjectSettings());
}
public function getName(): string
{
return 'menu';
}
/** {@inheritDoc} */
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
$parserContext = $blockContext->getDocumentParserContext()->getParser()->getParserContext();
$options = $directive->getOptions();
$options['glob'] = new DirectiveOption('glob', true);
$indexName = $this->settingsManager->getProjectSettings()->getIndexName();
$options['globExclude'] ??= new DirectiveOption('globExclude', $indexName);
$toctreeFiles = $this->toctreeBuilder->buildToctreeEntries(
$parserContext,
$blockContext->getDocumentIterator(),
$options,
);
if (count($toctreeFiles) === 0) {
$toctreeFiles[] = new GlobMenuEntryNode('/*');
}
return (new NavMenuNode($toctreeFiles))->withOptions($this->optionsToArray($options));
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\Metadata\MetaNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Add a meta information:
*
* .. meta::
* :key: value
*/
final class MetaDirective extends ActionDirective
{
public function getName(): string
{
return 'meta';
}
public function processAction(BlockContext $blockContext, Directive $directive): void
{
$document = $blockContext->getDocumentParserContext()->getDocument();
foreach ($directive->getOptions() as $option) {
$document->addHeaderNode(new MetaNode($option->getName(), (string) $option->getValue()));
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a note admonition.
*
* Example:
*
* ```rest
* .. note::
*
* This is a note admonition.
* ```
*/
final class NoteDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'note', 'Note');
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer;
use phpDocumentor\Guides\RestructuredText\Nodes\OptionNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericLinkProvider;
use function array_map;
use function explode;
use function preg_replace;
use function str_contains;
/**
* Describes a command line argument or switch. Option argument names should be enclosed in angle brackets.
*
* https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-option
*/
final class OptionDirective extends SubDirective
{
public const NAME = 'option';
/** @param Rule<CollectionNode> $startingRule */
public function __construct(
protected Rule $startingRule,
GenericLinkProvider $genericLinkProvider,
private readonly AnchorNormalizer $anchorReducer,
) {
parent::__construct($startingRule);
$genericLinkProvider->addGenericLink(self::NAME, OptionNode::LINK_TYPE);
}
public function getName(): string
{
return self::NAME;
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$additionalIds = $this->getAdditionalIds($directive);
$id = $this->anchorReducer->reduceAnchor($directive->getData());
return new OptionNode($id, $directive->getData(), $additionalIds, $collectionNode->getChildren());
}
/** @return string[] */
private function getAdditionalIds(Directive $directive): array
{
$additionalIds = [];
if (str_contains($directive->getData(), ',')) {
$additionalIds = explode(',', $directive->getData());
$additionalIds = array_map('trim', $additionalIds);
$additionalIds = array_map(function ($item) {
// remove additional information in brackets like <module>
$pattern = '/<([^>]+)>/';
$item = preg_replace($pattern, '', $item);
// only keep allowed signs
return $this->anchorReducer->reduceAnchor($item ?? '');
}, $additionalIds);
}
return $additionalIds;
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives\OptionMapper;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
/**
* The directives `code-block` and `literalinclude` both create a CodeNode with the same possible options.
* This common mapper is used by both Directives.
*/
interface CodeNodeOptionMapper
{
/** @param DirectiveOption[] $directiveOptions */
public function apply(
CodeNode $codeNode,
array $directiveOptions,
BlockContext $blockContext,
): void;
}

View File

@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives\OptionMapper;
use phpDocumentor\Guides\Nodes\CodeNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
use phpDocumentor\Guides\RestructuredText\Parser\InlineParser;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\InlineMarkupRule;
use Psr\Log\LoggerInterface;
use function preg_match;
use function sprintf;
use function strval;
use function trim;
/**
* The directives `code-block` and `literalinclude` both create a CodeNode with the same possible options.
* This common mapper is used by both Directives.
*/
final class DefaultCodeNodeOptionMapper implements CodeNodeOptionMapper
{
/** @see https://regex101.com/r/czvfnV/2 */
public const LINE_NUMBER_RANGES_REGEX = '/^\d+(-\d*)?(?:,\s*\d+(-\d*)?)*$/';
public function __construct(
private readonly LoggerInterface $logger,
protected InlineMarkupRule $startingRule,
private readonly InlineParser $inlineParser,
) {
}
/** @param DirectiveOption[] $directiveOptions */
public function apply(
CodeNode $codeNode,
array $directiveOptions,
BlockContext $blockContext,
): void {
if (isset($directiveOptions['language'])) {
$codeNode->setLanguage(trim((string) $directiveOptions['language']->getValue()));
}
$this->setStartingLineNumberBasedOnOptions($directiveOptions, $codeNode);
$this->setCaptionBasedOnOptions($directiveOptions, $codeNode, $blockContext);
$this->setEmphasizeLinesBasedOnOptions($directiveOptions, $codeNode, $blockContext);
$this->setStartingLineNumberBasedOnOptions($directiveOptions, $codeNode);
}
/** @param DirectiveOption[] $options */
private function setCaptionBasedOnOptions(
array $options,
CodeNode $node,
BlockContext $blockContext,
): void {
$caption = null;
if (isset($options['caption'])) {
$caption = $this->inlineParser->parse(strval($options['caption']->getValue()), $blockContext);
}
$node->setCaption($caption);
}
/** @param DirectiveOption[] $options */
private function setEmphasizeLinesBasedOnOptions(array $options, CodeNode $node, BlockContext $blockContext): void
{
$emphasizeLines = null;
if (isset($options['emphasize-lines'])) {
$emphasizeLines = (string) $options['emphasize-lines']->getValue();
if (!preg_match(self::LINE_NUMBER_RANGES_REGEX, $emphasizeLines)) {
// Input does not fit the pattern, log a warning
$this->logger->warning(
sprintf('Invalid value for option emphasize-lines: "%s". Expected format: \'1-5, 7, 33\'', $emphasizeLines),
$blockContext->getLoggerInformation(),
);
}
}
$node->setEmphasizeLines($emphasizeLines);
}
/** @param array<string, DirectiveOption> $options */
private function setStartingLineNumberBasedOnOptions(array $options, CodeNode $node): void
{
$startingLineNumber = null;
if (isset($options['linenos']) || isset($options['number-lines'])) {
$startingLineNumber = 1;
}
if (isset($options['number-lines'])) {
$startingLineNumber = $options['number-lines']->getValue() ?? $startingLineNumber;
} elseif (isset($options['lineno-start'])) {
$startingLineNumber = $options['lineno-start']->getValue() ?? $startingLineNumber;
}
if ($startingLineNumber === null) {
return;
}
$node->setStartingLineNumber((int) $startingLineNumber);
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\QuoteNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* A pull-quote is a small selection of text "pulled out and quoted", typically in
* a larger typeface. Pull-quotes are used to attract attention, especially in long articles.
* The "pull-quote" directive produces a "pull-quote"-class block quote.
*
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#pull-quote
*/
final class PullQuoteDirective extends SubDirective
{
public function getName(): string
{
return 'pull-quote';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return new QuoteNode($collectionNode->getChildren(), ['pull-quote']);
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\RawNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function implode;
/**
* Renders a raw block, example:
*
* .. raw::
*
* <u>Underlined!</u>
*
* @link https://docutils.sourceforge.io/docs/ref/rst/directives.html#raw-data-pass-through
*/
final class RawDirective extends BaseDirective
{
public function getName(): string
{
return 'raw';
}
/** {@inheritDoc} */
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
return new RawNode(
implode("\n", $blockContext->getDocumentIterator()->toArray()),
$directive->getData(),
);
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\ParagraphNode;
use phpDocumentor\Guides\Nodes\ReplacementNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function count;
/**
* The Replace directive will set the variables for the spans
*
* .. |test| replace:: The Test String!
*/
final class ReplaceDirective extends SubDirective
{
public function getName(): string
{
return 'replace';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
final protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
/** @var array<InlineCompoundNode> $children */
$children = $collectionNode->getChildren();
$data = $directive->getDataNode();
if ($data !== null) {
if (count($children) > 0) {
$children[] = new ParagraphNode([$data]);
} else {
$children[] = $data;
}
}
return new ReplacementNode(
$children,
);
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\TextRoles\BaseTextRole;
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericTextRole;
use Psr\Log\LoggerInterface;
use function is_string;
use function preg_match;
use function sprintf;
use function trim;
/**
* The "role" directive dynamically creates a custom interpreted text role and registers it with the parser.
*
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#role
*/
final class RoleDirective extends ActionDirective
{
public function __construct(
private readonly LoggerInterface $logger,
) {
}
public function getName(): string
{
return 'role';
}
public function processAction(
BlockContext $blockContext,
Directive $directive,
): void {
$name = $directive->getData();
$role = 'span';
if (preg_match('/^([A-Za-z-]*)\(([A-Za-z-]*)\)$/', trim($name), $match) === 1) {
$name = $match[1];
$role = $match[2];
}
$baseRole = $blockContext->getDocumentParserContext()->getTextRoleFactoryForDocument()->getTextRole($role);
if (!$baseRole instanceof BaseTextRole) {
$this->logger->error(
sprintf('Text role "%s", class %s cannot be extended. ', $role, $baseRole::class),
$blockContext->getLoggerInformation(),
);
return;
}
$customRole = $baseRole->withName($name);
if (is_string($directive->getOption('class')->getValue())) {
$customRole->setClass($directive->getOption('class')->getValue());
} else {
$customRole->setClass($name);
}
if ($customRole instanceof GenericTextRole) {
$customRole->setBaseRole($role);
}
$blockContext->getDocumentParserContext()->getTextRoleFactoryForDocument()->replaceTextRole($customRole);
}
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\AuthorNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use Psr\Log\LoggerInterface;
use function preg_match;
final class SectionauthorDirective extends BaseDirective
{
/** @see https://regex101.com/r/vGy4Uu/1 */
public const NAME_EMAIL_REGEX = '/^(?P<name>[\w\s]+)(?: <(?P<email>[^>]+)>)?$/';
public function __construct(
private readonly LoggerInterface $logger,
) {
}
public function getName(): string
{
return 'sectionauthor';
}
/**
* When the default domain contains a class directive, this directive will be shadowed. Therefore, Sphinx re-exports it as rst-class.
*
* See https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rstclass
*
* @return string[]
*/
public function getAliases(): array
{
return ['codeauthor'];
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
$input = $directive->getData();
$directiveName = $directive->getName();
if ($input === '') {
$this->logger->warning('`.. ' . $directiveName . ' ::` directive could not be parsed: `' . $input . '`', $blockContext->getLoggerInformation());
return null;
}
if (!preg_match(self::NAME_EMAIL_REGEX, $input, $matches)) {
$this->logger->warning('Content of `.. ' . $directiveName . ':: name <email>` must specify a name and can also specify an email', $blockContext->getLoggerInformation());
return null;
}
$name = $matches['name'];
$email = $matches['email'] ?? null;
$context = match ($directiveName) {
'sectionauthor' => AuthorNode::CONTEXT_SECTION,
default => AuthorNode::CONTEXT_CODE,
};
return new AuthorNode($name, [], $context, $email);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a seealso admonition.
*
* Example:
*
* ```rest
* .. seealso::
*
* This is a seealso admonition.
* ```
*/
final class SeeAlsoDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'seealso', 'See also');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Nodes\SidebarNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Divs a sub document in a div with a given class or set of classes.
*
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#sidebar
*/
final class SidebarDirective extends SubDirective
{
public function getName(): string
{
return 'sidebar';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return new SidebarNode(
$directive->getDataNode() ?? InlineCompoundNode::getPlainTextInlineNode($directive->getData()),
$collectionNode->getChildren(),
);
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* A directive that parses the sub block and call the processSub that can
* be overloaded, like :
*
* .. sub-directive::
* Some block of code
*
* You can imagine anything here, like adding *emphasis*, lists or
* titles
*/
abstract class SubDirective extends BaseDirective
{
/** @param Rule<CollectionNode> $startingRule */
public function __construct(protected Rule $startingRule)
{
}
/** {@inheritDoc} */
final public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
$collectionNode = $this->getStartingRule()->apply($blockContext);
if ($collectionNode === null) {
return null;
}
$node = $this->processSub($blockContext, $collectionNode, $directive);
if ($node === null) {
return null;
}
return $node->withKeepExistingOptions($this->optionsToArray($directive->getOptions()));
}
/** @return Rule<CollectionNode> */
protected function getStartingRule(): Rule
{
return $this->startingRule;
}
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
return null;
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Nodes\TabNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function class_alias;
use function class_exists;
use function is_string;
use function preg_replace;
use function str_replace;
use function strtolower;
final class TabDirective extends SubDirective
{
public function getName(): string
{
return 'tab';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
if (is_string($directive->getOption('key')->getValue())) {
$key = strtolower($directive->getOption('key')->getValue());
} else {
$key = strtolower($directive->getData());
}
$key = str_replace(' ', '-', $key);
$key = (string) (preg_replace('/[^a-zA-Z0-9\-_]/', '', $key));
$active = $directive->hasOption('active');
return new TabNode(
'tab',
$directive->getData(),
$directive->getDataNode() ?? new InlineCompoundNode(),
$key,
$active,
$collectionNode->getChildren(),
);
}
}
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Directives\TabDirective::class, false)) {
class_alias(TabDirective::class, \phpDocumentor\Guides\Bootstrap\Directives\TabDirective::class);
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\TableNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use Psr\Log\LoggerInterface;
use function array_map;
use function count;
use function explode;
use function sprintf;
use function strval;
/**
* Applies more options to a table
*
* .. table:: Table Title
* :widths: 30,70
* :align: center
* :class: custom-table
*
* +-----------------+-----------------+
* | Header 1 | Header 2 |
* +=================+=================+
* | Row 1, Column 1 | Row 1, Column 2 |
* +-----------------+-----------------+
* | Row 2, Column 1 | Row 2, Column 2 |
* +-----------------+-----------------+
*/
final class TableDirective extends SubDirective
{
public function __construct(
protected Rule $startingRule,
private LoggerInterface $logger,
) {
parent::__construct($startingRule);
}
public function getName(): string
{
return 'table';
}
/** {@inheritDoc} */
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
if (count($collectionNode->getChildren()) !== 1) {
$this->logger->warning(
sprintf('The table directive may contain exactly one table. %s children found', count($collectionNode->getChildren())),
$blockContext->getLoggerInformation(),
);
return $collectionNode;
}
$tableNode = $collectionNode->getChildren()[0];
if (!$tableNode instanceof TableNode) {
$this->logger->warning(
sprintf('The table directive may contain exactly one table. A node of type %s was found. ', $tableNode::class),
$blockContext->getLoggerInformation(),
);
return $collectionNode;
}
$options = $this->optionsToArray($directive->getOptions());
if (isset($options['widths']) && $options['widths'] !== 'auto' && $options['widths'] !== 'grid') {
$colWidths = array_map('intval', explode(',', strval($options['widths'])));
// A list of integers is used instead of the input column widths. Implies "grid".
$options['widths'] = 'grid';
$tableNode = $tableNode->withColumnWidth($colWidths);
}
return $tableNode->withOptions($options);
}
}

View File

@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer;
use phpDocumentor\Guides\RestructuredText\Nodes\AbstractTabNode;
use phpDocumentor\Guides\RestructuredText\Nodes\TabsNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use Psr\Log\LoggerInterface;
use function class_alias;
use function class_exists;
use function is_string;
final class TabsDirective extends SubDirective
{
private int $tabsCounter = 0;
/** @param Rule<CollectionNode> $startingRule */
public function __construct(
protected Rule $startingRule,
private readonly LoggerInterface $logger,
private readonly AnchorNormalizer $anchorReducer,
) {
parent::__construct($startingRule);
}
public function getName(): string
{
return 'tabs';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$tabs = [];
$hasActive = false;
foreach ($collectionNode->getChildren() as $child) {
if ($child instanceof AbstractTabNode) {
if ($child->isActive()) {
if (!$hasActive) {
$hasActive = true;
} else {
// There may only be one active child, first wins
$child->setActive(false);
}
}
$tabs[] = $child;
} else {
$this->logger->warning(
'The "tabs" directive may only contain children of type "tab". The following node was found: ' . $child::class,
$blockContext->getLoggerInformation(),
);
}
}
if (!$hasActive && isset($tabs[0])) {
$tabs[0]->setActive(true);
}
if (is_string($directive->getOption('key')->getValue())) {
$key = $this->anchorReducer->reduceAnchor($directive->getOption('key')->getValue());
} else {
$this->tabsCounter++;
$key = 'tabs-' . $this->tabsCounter;
}
return new TabsNode(
'tabs',
$directive->getData(),
$directive->getDataNode() ?? new InlineCompoundNode(),
$key,
$tabs,
);
}
}
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Directives\TabsDirective::class, false)) {
class_alias(TabsDirective::class, \phpDocumentor\Guides\Bootstrap\Directives\TabsDirective::class);
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Nodes\ContainerNode;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use Psr\Log\LoggerInterface;
/**
* Divs a sub document in a div with a given class or set of classes.
*
* @link https://docutils.sourceforge.io/docs/ref/rst/directives.html#container
*/
final class TestLoggerDirective extends SubDirective
{
public function __construct(
protected Rule $startingRule,
private readonly LoggerInterface $logger,
) {
parent::__construct($startingRule);
}
public function getName(): string
{
return 'testlogger';
}
/** {@inheritDoc}
*
* @param Directive $directive
*/
protected function processSub(
BlockContext $blockContext,
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$this->logger->warning('Test logging in directives', $blockContext->getLoggerInformation());
return (new ContainerNode($collectionNode->getChildren()))->withOptions(['class' => $directive->getData()]);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a tip admonition.
*
* Example:
*
* ```rest
* .. tip::
*
* This is a tip admonition.
* ```
*/
final class TipDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'tip', 'Tip');
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Add a meta title to the document
*
* .. title:: Page title
*/
final class TitleDirective extends ActionDirective
{
public function getName(): string
{
return 'title';
}
public function processAction(BlockContext $blockContext, Directive $directive): void
{
$document = $blockContext->getDocumentParserContext()->getDocument();
$document->setMetaTitle($directive->getData());
}
}

View File

@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Menu\TocNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use phpDocumentor\Guides\RestructuredText\Toc\ToctreeBuilder;
use phpDocumentor\Guides\Settings\ProjectSettings;
use phpDocumentor\Guides\Settings\SettingsManager;
/**
* Sphinx based Toctree directive.
*
* This directive has an issue, as the related documents are resolved on parse, but during the rendering
* we are using the {@see Metas} to collect the titles of those documents. There is some step missing in our process
* which could be resolved by using https://github.com/phpDocumentor/guides/pull/21?
*
* @link https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#table-of-contents
*
* Parameters:
*
* :caption:
* (Text with inline markup)
* Caption to be displayed above the menu
*
* :depth:
* (integer)
* Maximum depth of the menu to display. Does not affect other menus.
*
* :glob:
* (bool)
* If true glob patterns containing stars are considered during menu building.
* The entries are added to the Document Tree. Orphans are ignored
*
* :globExclude:
* (comma separated string list)
* paths to be excluded from the glob patterns
*
* :hidden:
* (bool)
* The menu will not be displayed within the content and is only used to
* change the global document tree.
*
* :reversed:
* (bool)
* Display documents in reversed order. They are also added to the document
* tree in reversed order and will be displayed in that order where ever a menu
* is displayed
*
* :titlesonly:
* Do not display the headlines of the current or sub documents, only display
* page titles.
*
* :maxdepth:
* Synonym of :depth:, depth prevails if both are set.
*/
final class ToctreeDirective extends BaseDirective
{
private SettingsManager $settingsManager;
/** @param Rule<InlineCompoundNode> $startingRule */
public function __construct(
private readonly ToctreeBuilder $toctreeBuilder,
private readonly Rule $startingRule,
SettingsManager|null $settingsManager = null,
) {
// if for backward compatibility reasons no settings manager was passed, use the defaults
$this->settingsManager = $settingsManager ?? new SettingsManager(new ProjectSettings());
}
public function getName(): string
{
return 'toctree';
}
/** {@inheritDoc} */
public function process(
BlockContext $blockContext,
Directive $directive,
): Node|null {
$parserContext = $blockContext->getDocumentParserContext()->getParser()->getParserContext();
$options = $directive->getOptions();
$indexName = $this->settingsManager->getProjectSettings()->getIndexName();
$options['globExclude'] ??= new DirectiveOption('globExclude', $indexName);
$toctreeFiles = $this->toctreeBuilder->buildToctreeEntries(
$parserContext,
$blockContext->getDocumentIterator(),
$options,
);
$tocNode = (new TocNode($toctreeFiles))->withOptions($this->optionsToArray($options));
if (isset($options['caption'])) {
$blockContextOfCaption = new BlockContext($blockContext->getDocumentParserContext(), (string) $options['caption']->getValue());
$inlineNode = $this->startingRule->apply($blockContextOfCaption);
$tocNode = $tocNode->withCaption($inlineNode);
}
if ($directive->getOptionBool('reversed')) {
$tocNode = $tocNode->withReversed(true);
}
return $tocNode;
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
/**
* Todo directives are treated as comments, omitting all content or options
*/
final class TodoDirective extends ActionDirective
{
public function getName(): string
{
return 'todo';
}
public function processAction(BlockContext $blockContext, Directive $directive): void
{
// Todo directives are treated as comments
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
final class VersionAddedDirective extends AbstractVersionChangeDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'versionadded', 'New in version %s');
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
final class VersionChangedDirective extends AbstractVersionChangeDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'versionchanged', 'Changed in version %s');
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
/**
* Directive to create a warning admonition.
*
* Example:
*
* ```rest
* .. warning::
*
* This is a warning admonition.
* ```
*/
final class WarningDirective extends AbstractAdmonitionDirective
{
public function __construct(protected Rule $startingRule)
{
parent::__construct($startingRule, 'warning', 'Warning');
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Directives;
use phpDocumentor\Guides\Nodes\EmbeddedFrame;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use function array_filter;
/**
* This directive is used to embed a youtube video in the document.
*
* Basic usage
*
* ```rst
* .. youtube:: dQw4w9WgXcQ
* ```
*
* Options:
*
* - string title The title of the video
* - int width The width of the video, default is 560
* - int height The height of the video, default is 315
* - 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
*/
final class YoutubeDirective extends BaseDirective
{
public function getName(): string
{
return 'youtube';
}
public function process(
BlockContext $blockContext,
Directive $directive,
): EmbeddedFrame {
$node = new EmbeddedFrame(
'https://www.youtube-nocookie.com/embed/' . $directive->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),
],
),
);
}
}

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText;
use InvalidArgumentException;
use phpDocumentor\Guides\MarkupLanguageParser as ParserInterface;
use phpDocumentor\Guides\Nodes\DocumentNode;
use phpDocumentor\Guides\ParserContext;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContextFactory;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use RuntimeException;
use Webmozart\Assert\Assert;
use function strtolower;
class MarkupLanguageParser implements ParserInterface
{
private ParserContext|null $parserContext = null;
private string|null $filename = null;
private DocumentParserContext|null $documentParser = null;
/** @param Rule<DocumentNode> $startingRule */
public function __construct(
private readonly Rule $startingRule,
private readonly DocumentParserContextFactory $documentParserContextFactory,
) {
}
public function supports(string $inputFormat): bool
{
return strtolower($inputFormat) === 'rst';
}
/** @deprecated one should use injected rules in a rule. Not subparsers */
public function getSubParser(): MarkupLanguageParser
{
return new MarkupLanguageParser(
$this->startingRule,
$this->documentParserContextFactory,
);
}
public function getParserContext(): ParserContext
{
if ($this->parserContext === null) {
throw new RuntimeException(
'A parser\'s Environment should not be consulted before parsing has started',
);
}
return $this->parserContext;
}
public function getDocument(): DocumentNode
{
if ($this->documentParser === null) {
throw new RuntimeException('Nothing has been parsed yet.');
}
return $this->documentParser->getDocument();
}
public function getFilename(): string
{
return $this->filename ?: '(unknown)';
}
public function parse(ParserContext $parserContext, string $contents): DocumentNode
{
$this->parserContext = $parserContext;
$this->documentParser = $this->documentParserContextFactory->create($this);
$blockContext = new BlockContext($this->documentParser, $contents);
if ($this->startingRule->applies($blockContext)) {
$document = $this->startingRule->apply($blockContext);
Assert::isInstanceOf($document, DocumentNode::class);
return $document;
}
throw new InvalidArgumentException('Content is not a valid document content');
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\Html;
final class AdmonitionNodeRenderer extends \phpDocumentor\Guides\NodeRenderers\Html\AdmonitionNodeRenderer
{
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\Html;
use InvalidArgumentException;
use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\CollectionNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\TemplateRenderer;
use function is_a;
/** @implements NodeRenderer<CollectionNode> */
final class CollectionNodeRenderer implements NodeRenderer
{
public function __construct(private readonly TemplateRenderer $renderer)
{
}
public function supports(string $nodeFqcn): bool
{
return $nodeFqcn === CollectionNode::class || is_a($nodeFqcn, CollectionNode::class, true);
}
public function render(Node $node, RenderContext $renderContext): string
{
if ($node instanceof CollectionNode === false) {
throw new InvalidArgumentException('Node must be an instance of ' . CollectionNode::class);
}
return $this->renderer->renderTemplate(
$renderContext,
'body/collection.html.twig',
[
'node' => $node->getValue(),
],
);
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\Html;
use InvalidArgumentException;
use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\RestructuredText\Nodes\ContainerNode;
use phpDocumentor\Guides\TemplateRenderer;
use function is_a;
use function trim;
/** @implements NodeRenderer<ContainerNode> */
final class ContainerNodeRenderer implements NodeRenderer
{
public function __construct(private readonly TemplateRenderer $renderer)
{
}
public function supports(string $nodeFqcn): bool
{
return $nodeFqcn === ContainerNode::class || is_a($nodeFqcn, ContainerNode::class, true);
}
public function render(Node $node, RenderContext $renderContext): string
{
if ($node instanceof ContainerNode === false) {
throw new InvalidArgumentException('Node must be an instance of ' . ContainerNode::class);
}
return $this->renderer->renderTemplate(
$renderContext,
'body/container.html.twig',
[
'class' => trim($node->getOption('class') . ' ' . $node->getClassesString()),
'id' => $node->getOption('name'),
'node' => $node->getValue(),
],
);
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\Html;
use InvalidArgumentException;
use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\RestructuredText\Nodes\GeneralDirectiveNode;
use phpDocumentor\Guides\TemplateRenderer;
use Psr\Log\LoggerInterface;
use function is_a;
use function preg_replace;
use function sprintf;
use function str_replace;
/** @implements NodeRenderer<GeneralDirectiveNode> */
final class GeneralDirectiveNodeRenderer implements NodeRenderer
{
public function __construct(
private readonly TemplateRenderer $renderer,
private readonly LoggerInterface $logger,
) {
}
public function supports(string $nodeFqcn): bool
{
return $nodeFqcn === GeneralDirectiveNode::class || is_a($nodeFqcn, GeneralDirectiveNode::class, true);
}
public function render(Node $node, RenderContext $renderContext): string
{
if ($node instanceof GeneralDirectiveNode === false) {
throw new InvalidArgumentException('Node must be an instance of ' . GeneralDirectiveNode::class);
}
$template = 'body/directive/' . $this->getTemplateName($node->getName()) . '.html.twig';
$data = ['node' => $node];
if ($this->renderer->isTemplateFound($renderContext, $template)) {
return $this->renderer->renderTemplate($renderContext, $template, $data);
}
$this->logger->warning(sprintf(
'No template found for rendering directive "%s". Expected template "%s"',
$node->getName(),
$template,
), $renderContext->getLoggerInformation());
$template = 'body/directive/not-found.html.twig';
return $this->renderer->renderTemplate($renderContext, $template, $data);
}
private function getTemplateName(string $directiveName): string
{
$directiveName = str_replace(':', '/', $directiveName);
$directiveName = preg_replace('/[^a-zA-Z0-9-_\/]/', '_', $directiveName);
return '' . $directiveName;
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\Html;
use InvalidArgumentException;
use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\RestructuredText\Nodes\SidebarNode;
use phpDocumentor\Guides\TemplateRenderer;
use function is_a;
/** @implements NodeRenderer<SidebarNode> */
final class SidebarNodeRenderer implements NodeRenderer
{
public function __construct(private readonly TemplateRenderer $renderer)
{
}
public function supports(string $nodeFqcn): bool
{
return $nodeFqcn === SidebarNode::class || is_a($nodeFqcn, SidebarNode::class, true);
}
public function render(Node $node, RenderContext $renderContext): string
{
if ($node instanceof SidebarNode === false) {
throw new InvalidArgumentException('Node must be an instance of ' . SidebarNode::class);
}
return $this->renderer->renderTemplate(
$renderContext,
'structure/sidebar.html.twig',
[
'title' => $node->getTitle(),
'node' => $node->getValue(),
],
);
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\Html;
use InvalidArgumentException;
use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\Menu\TocNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\RestructuredText\Nodes\TopicNode;
use phpDocumentor\Guides\TemplateRenderer;
use function is_a;
/** @implements NodeRenderer<TocNode> */
final class TopicNodeRenderer implements NodeRenderer
{
public function __construct(private readonly TemplateRenderer $renderer)
{
}
public function supports(string $nodeFqcn): bool
{
return $nodeFqcn === TopicNode::class || is_a($nodeFqcn, TopicNode::class, true);
}
public function render(Node $node, RenderContext $renderContext): string
{
if ($node instanceof TopicNode === false) {
throw new InvalidArgumentException('Node must be an instance of ' . TopicNode::class);
}
return $this->renderer->renderTemplate(
$renderContext,
'body/topic.html.twig',
[
'name' => $node->getName(),
'node' => $node->getValue(),
],
);
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\NodeRenderers\LaTeX;
use InvalidArgumentException;
use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\RestructuredText\Nodes\GeneralDirectiveNode;
use phpDocumentor\Guides\TemplateRenderer;
use Psr\Log\LoggerInterface;
use function is_a;
use function preg_replace;
use function sprintf;
use function str_replace;
/** @implements NodeRenderer<GeneralDirectiveNode> */
final class GeneralDirectiveNodeRenderer implements NodeRenderer
{
public function __construct(
private readonly TemplateRenderer $renderer,
private readonly LoggerInterface $logger,
) {
}
public function supports(string $nodeFqcn): bool
{
return $nodeFqcn === GeneralDirectiveNode::class || is_a($nodeFqcn, GeneralDirectiveNode::class, true);
}
public function render(Node $node, RenderContext $renderContext): string
{
if ($node instanceof GeneralDirectiveNode === false) {
throw new InvalidArgumentException('Node must be an instance of ' . GeneralDirectiveNode::class);
}
$template = 'body/directive/' . $this->getTemplateName($node->getName()) . '.tex.twig';
$data = ['node' => $node];
if ($this->renderer->isTemplateFound($renderContext, $template)) {
return $this->renderer->renderTemplate($renderContext, $template, $data);
}
$this->logger->warning(sprintf(
'No template found for rendering directive "%s". Expected template "%s"',
$node->getName(),
$template,
), $renderContext->getLoggerInformation());
$template = 'body/directive/not-found.html.twig';
return $this->renderer->renderTemplate($renderContext, $template, $data);
}
private function getTemplateName(string $directiveName): string
{
$directiveName = str_replace(':', '/', $directiveName);
$directiveName = preg_replace('/[^a-zA-Z0-9-_\/]/', '_', $directiveName);
return '' . $directiveName;
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use function class_alias;
use function class_exists;
abstract class AbstractTabNode extends GeneralDirectiveNode
{
/** @param list<Node> $value */
public function __construct(
string $name,
string $plainContent,
InlineCompoundNode $content,
private readonly string $key,
private bool $active,
array $value = [],
) {
parent::__construct($name, $plainContent, $content, $value);
}
public function getKey(): string
{
return $this->key;
}
public function isActive(): bool
{
return $this->active;
}
public function setActive(bool $active): void
{
$this->active = $active;
}
}
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Nodes\AbstractTabNode::class, false)) {
class_alias(AbstractTabNode::class, \phpDocumentor\Guides\Bootstrap\Nodes\AbstractTabNode::class);
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
class AdmonitionNode extends \phpDocumentor\Guides\Nodes\AdmonitionNode
{
}

View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\LinkTargetNode;
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\OptionalLinkTargetsNode;
use phpDocumentor\Guides\Nodes\PrefixedLinkTargetNode;
/**
* The confval directive configuration values.
*
* https://sphinx-toolbox.readthedocs.io/en/stable/extensions/confval.html
*
* @extends CompoundNode<Node>
*/
final class ConfvalNode extends CompoundNode implements LinkTargetNode, OptionalLinkTargetsNode, PrefixedLinkTargetNode
{
public const LINK_TYPE = 'std:confval';
public const LINK_PREFIX = 'confval-';
/**
* @param list<Node> $value
* @param array<string, InlineCompoundNode> $additionalOptions
*/
public function __construct(
private readonly string $id,
private readonly string $plainContent,
private readonly InlineCompoundNode|null $type = null,
private readonly bool $required = false,
private readonly InlineCompoundNode|null $default = null,
private readonly array $additionalOptions = [],
array $value = [],
private readonly bool $noindex = false,
) {
parent::__construct($value);
}
public function getPlainContent(): string
{
return $this->plainContent;
}
public function getLinkType(): string
{
return self::LINK_TYPE;
}
public function getId(): string
{
return $this->id;
}
public function getAnchor(): string
{
return self::LINK_PREFIX . $this->id;
}
public function getLinkText(): string
{
return $this->plainContent;
}
public function getType(): InlineCompoundNode|null
{
return $this->type;
}
public function isRequired(): bool
{
return $this->required;
}
public function getDefault(): InlineCompoundNode|null
{
return $this->default;
}
/** @return array<string,InlineCompoundNode> */
public function getAdditionalOptions(): array
{
return $this->additionalOptions;
}
public function getPrefix(): string
{
return self::LINK_PREFIX;
}
public function isNoindex(): bool
{
return $this->noindex;
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\Node;
/** @extends CompoundNode<Node> */
final class ContainerNode extends CompoundNode
{
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
/**
* A catch-all directive Node containing all information about the original directive in rst.
*
* @template TValue of Node = Node
* @extends CompoundNode<TValue>
*/
class GeneralDirectiveNode extends CompoundNode
{
/** @param list<TValue> $value */
public function __construct(
private readonly string $name,
private readonly string $plainContent,
private readonly InlineCompoundNode $content,
array $value = [],
) {
parent::__construct($value);
}
public function getName(): string
{
return $this->name;
}
public function getPlainContent(): string
{
return $this->plainContent;
}
public function getContent(): InlineCompoundNode
{
return $this->content;
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\LinkTargetNode;
use phpDocumentor\Guides\Nodes\MultipleLinkTargetsNode;
use phpDocumentor\Guides\Nodes\Node;
/**
* Describes a command line argument or switch. Option argument names should be enclosed in angle brackets.
*
* https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-option
*
* @extends CompoundNode<Node>
*/
final class OptionNode extends CompoundNode implements LinkTargetNode, MultipleLinkTargetsNode
{
public const LINK_TYPE = 'std:option';
/**
* @param list<Node> $value
* @param string[] $additionalIds
*/
public function __construct(
private readonly string $id,
private readonly string $plainContent,
private readonly array $additionalIds,
array $value = [],
) {
parent::__construct($value);
}
public function getPlainContent(): string
{
return $this->plainContent;
}
public function getLinkType(): string
{
return self::LINK_TYPE;
}
public function getId(): string
{
return $this->id;
}
public function getLinkText(): string
{
return $this->plainContent;
}
/** @return string[] */
public function getAdditionalIds(): array
{
return $this->additionalIds;
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use phpDocumentor\Guides\Nodes\Node;
/** @extends CompoundNode<Node> */
final class SidebarNode extends CompoundNode
{
/** {@inheritDoc} */
public function __construct(private readonly InlineCompoundNode $title, array $value)
{
parent::__construct($value);
}
public function getTitle(): InlineCompoundNode
{
return $this->title;
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use function class_alias;
use function class_exists;
final class TabNode extends AbstractTabNode
{
}
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Nodes\TabNode::class, false)) {
class_alias(TabNode::class, \phpDocumentor\Guides\Bootstrap\Nodes\TabNode::class);
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
use function class_alias;
use function class_exists;
/** @extends GeneralDirectiveNode<AbstractTabNode> */
final class TabsNode extends GeneralDirectiveNode
{
/** @param list<AbstractTabNode> $value */
public function __construct(
string $name,
string $plainContent,
InlineCompoundNode $content,
private readonly string $key,
array $value,
) {
parent::__construct($name, $plainContent, $content, $value);
}
/** @return list<AbstractTabNode> */
public function getTabs(): array
{
return $this->getChildren();
}
public function getKey(): string
{
return $this->key;
}
}
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Nodes\TabsNode::class, false)) {
class_alias(TabsNode::class, \phpDocumentor\Guides\Bootstrap\Nodes\TabsNode::class);
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Guides\RestructuredText\Nodes;
use phpDocumentor\Guides\Nodes\CompoundNode;
use phpDocumentor\Guides\Nodes\Node;
use function array_filter;
/** @extends CompoundNode<Node> */
final class TopicNode extends CompoundNode
{
/** {@inheritDoc} */
public function __construct(private readonly string $name, array $value)
{
parent::__construct(array_filter($value));
}
public function getName(): string
{
return $this->name;
}
}

Some files were not shown because too many files have changed in this diff Show More