diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..bd214f7b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# https://help.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 + +updates: + - + commit-message: + include: "scope" + prefix: "github-actions" + directory: "/" + labels: + - "dependency" + open-pull-requests-limit: 5 + package-ecosystem: "github-actions" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2641d0f9..f85af4b3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,13 +14,13 @@ jobs: steps: - name: 'Checkout code' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Install PHP' uses: shivammathur/setup-php@v2 with: coverage: none - php-version: 7.4 + php-version: 8.3 tools: composer:v2 ini-values: date.timezone=UTC @@ -28,17 +28,20 @@ jobs: run: composer validate --strict stable-tests: - name: Stable Tests - runs-on: ubuntu-latest + name: "${{ matrix.operating-system }} / PHP ${{ matrix.php-version }}" + runs-on: ${{ matrix.operating-system }} strategy: fail-fast: false matrix: - php-version: ['7.4', '8.0', '8.1', '8.2'] + # add here only the PHP versions and OS used in GitHub CI (for tests) + # and on the symfony.com server (where the Symfony Docs are built) + operating-system: ['ubuntu-latest'] + php-version: ['8.3', '8.4'] steps: - name: 'Checkout code' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Install PHP' uses: shivammathur/setup-php@v2 diff --git a/composer.json b/composer.json index 9416e835..8e66adac 100644 --- a/composer.json +++ b/composer.json @@ -14,23 +14,23 @@ } }, "require": { - "php": ">=7.4", + "php": ">=8.3", "ext-json": "*", "ext-curl": "*", "doctrine/rst-parser": "^0.5", - "scrivo/highlight.php": "^9.12.0", - "symfony/filesystem": "^5.2 || ^6.0", - "symfony/finder": "^5.2 || ^6.0", - "symfony/dom-crawler": "^5.2 || ^6.0", - "symfony/css-selector": "^5.2 || ^6.0", - "symfony/console": "^5.2 || ^6.0", - "symfony/http-client": "^5.2 || ^6.0", + "scrivo/highlight.php": "^9.18.1", + "symfony/filesystem": "^5.2 || ^6.0 || ^7.0", + "symfony/finder": "^5.2 || ^6.0 || ^7.0", + "symfony/dom-crawler": "^5.2 || ^6.0 || ^7.0", + "symfony/css-selector": "^5.2 || ^6.0 || ^7.0", + "symfony/console": "^5.2 || ^6.0 || ^7.0", + "symfony/http-client": "^5.2 || ^6.0 || ^7.0", "twig/twig": "^2.14 || ^3.3" }, "require-dev": { "gajus/dindent": "^2.0", - "symfony/phpunit-bridge": "^5.2 || ^6.0", - "symfony/process": "^5.2 || ^6.0", + "symfony/phpunit-bridge": "^5.2 || ^6.0 || ^7.0", + "symfony/process": "^5.2 || ^6.0 || ^7.0", "masterminds/html5": "^2.7" }, "bin": ["bin/docs-builder"] diff --git a/notes.md b/notes.md index 3380b087..f255945d 100644 --- a/notes.md +++ b/notes.md @@ -21,10 +21,10 @@ Notes - /!\ tip / caution / node / etc... with nested reference - no more `colgroup` in tables ?? - add ` ` instead of simple space to lines in code blocks -- test if there is only one

in each page +- test if there is only one `

` in each page - we're assuming there is only one toctree per page... - `rst` does not exist in highlight php - `varnish` = C ? (highlight php) - `RoleDirective` ? - `IndexDirective` ? -- toctree maxdepth ? \ No newline at end of file +- toctree maxdepth ? diff --git a/src/Directive/ConfigurationBlockDirective.php b/src/Directive/ConfigurationBlockDirective.php index 8b0c8cb9..83850ce4 100644 --- a/src/Directive/ConfigurationBlockDirective.php +++ b/src/Directive/ConfigurationBlockDirective.php @@ -18,6 +18,7 @@ class ConfigurationBlockDirective extends SubDirective { private const LANGUAGE_LABELS = [ + 'caddy' => 'Caddy', 'env' => 'Bash', 'html+jinja' => 'Twig', 'html+php' => 'PHP', @@ -65,6 +66,7 @@ public function processSub(Parser $parser, ?Node $document, string $variable, st 'directives/configuration-block.html.twig', [ 'blocks' => $blocks, + 'title' => 'Configuration formats', ] ); diff --git a/src/Directive/TabDirective.php b/src/Directive/TabDirective.php new file mode 100644 index 00000000..81e1c27f --- /dev/null +++ b/src/Directive/TabDirective.php @@ -0,0 +1,36 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SymfonyDocsBuilder\Directive; + +use Doctrine\RST\Directives\SubDirective; +use Doctrine\RST\Nodes\Node; +use Doctrine\RST\Parser; +use SymfonyDocsBuilder\Node\TabNode; + +/** + * Directive that only appears within the "tabs" directive. + */ +class TabDirective extends SubDirective +{ + public function getName(): string + { + return 'tab'; + } + + public function processSub(Parser $parser, ?Node $document, string $variable, string $data, array $options): ?Node + { + $tabName = $data; + if (!$tabName) { + throw new \RuntimeException(sprintf('The "tab" directive requires a tab name: ".. tab:: Tab Name".')); + } + + return new TabNode($document->getNodes(), $data); + } +} diff --git a/src/Directive/TabsDirective.php b/src/Directive/TabsDirective.php new file mode 100644 index 00000000..0a8c25e6 --- /dev/null +++ b/src/Directive/TabsDirective.php @@ -0,0 +1,60 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SymfonyDocsBuilder\Directive; + +use Doctrine\RST\Directives\SubDirective; +use Doctrine\RST\Nodes\Node; +use Doctrine\RST\Parser; +use SymfonyDocsBuilder\Node\TabNode; + +class TabsDirective extends SubDirective +{ + public function getName(): string + { + return 'tabs'; + } + + public function processSub(Parser $parser, ?Node $document, string $variable, string $data, array $options): ?Node + { + $tabsTitle = $data; + if (!$tabsTitle) { + throw new \RuntimeException(sprintf('The "tabs" directive requires a title: ".. tabs:: Title".')); + } + + $blocks = []; + foreach ($document->getNodes() as $tabNode) { + if (!$tabNode instanceof TabNode) { + throw new \RuntimeException(sprintf('Only ".. tab::" content can appear within the "tabs" directive.')); + } + + $content = ''; + foreach ($tabNode->getNodes() as $node) { + $content .= $node->render(); + } + + $blocks[] = [ + 'hash' => hash('sha1', $tabNode->getTabName()), + 'language_label' => $tabNode->getTabName(), + 'language' => $tabNode->getSluggedTabName(), + 'code' => $content, + ]; + } + + $wrapperDiv = $parser->renderTemplate( + 'directives/configuration-block.html.twig', + [ + 'blocks' => $blocks, + 'title' => $tabsTitle, + ] + ); + + return $parser->getNodeFactory()->createWrapperNode(null, $wrapperDiv, ''); + } +} diff --git a/src/DocsKernel.php b/src/DocsKernel.php index 1f234425..8ffbedd7 100644 --- a/src/DocsKernel.php +++ b/src/DocsKernel.php @@ -20,6 +20,7 @@ use SymfonyDocsBuilder\Listener\AdmonitionListener; use SymfonyDocsBuilder\Listener\AssetsCopyListener; use SymfonyDocsBuilder\Listener\CopyImagesListener; +use SymfonyDocsBuilder\Listener\DuplicatedHeaderIdListener; class DocsKernel extends Kernel { @@ -49,6 +50,11 @@ private function initializeListeners(EventManager $eventManager, ErrorManager $e new AdmonitionListener() ); + $eventManager->addEventListener( + PreParseDocumentEvent::PRE_PARSE_DOCUMENT, + new DuplicatedHeaderIdListener() + ); + $eventManager->addEventListener( PreNodeRenderEvent::PRE_NODE_RENDER, new CopyImagesListener($this->buildConfig, $errorManager) diff --git a/src/Generator/JsonGenerator.php b/src/Generator/JsonGenerator.php index 7d429be7..51de0dee 100644 --- a/src/Generator/JsonGenerator.php +++ b/src/Generator/JsonGenerator.php @@ -69,7 +69,7 @@ public function generateJson(string $masterDocument = 'index'): array $crawler = new Crawler(file_get_contents($this->buildConfig->getOutputDir().'/'.$filename.'.html')); // happens when some doc is a partial included in other doc an it doesn't have any titles - $toc = false === current($metaEntry->getTitles()) ? [] : $this->generateToc($metaEntry, current($metaEntry->getTitles())[1]); + $toc = $this->generateToc($metaEntry, $crawler); $next = $this->determineNext($parserFilename, $flattenedTocTree, $masterDocument); $prev = $this->determinePrev($parserFilename, $flattenedTocTree); $data = [ @@ -102,26 +102,35 @@ public function setOutput(SymfonyStyle $output) $this->output = $output; } - private function generateToc(MetaEntry $metaEntry, ?array $titles, int $level = 1): array + private function generateToc(MetaEntry $metaEntry, Crawler $crawler): array { - if (null === $titles) { - return []; + $flatTocTree = []; + + foreach ($crawler->filter('h2, h3') as $heading) { + $headerId = $heading->getAttribute('id') ?? Environment::slugify($heading->textContent); + + // this tocTree stores items sequentially (h2, h2, h3, h3, h2, h3, etc.) + $flatTocTree[] = [ + 'level' => 'h2' === $heading->tagName ? 1 : 2, + 'url' => sprintf('%s#%s', $metaEntry->getUrl(), $headerId), + 'page' => u($metaEntry->getUrl())->beforeLast('.html')->toString(), + 'fragment' => $headerId, + 'title' => $heading->textContent, + 'children' => [], + ]; } - $tocTree = []; - - foreach ($titles as $title) { - $tocTree[] = [ - 'level' => $level, - 'url' => sprintf('%s#%s', $metaEntry->getUrl(), Environment::slugify($title[0])), - 'page' => u($metaEntry->getUrl())->beforeLast('.html'), - 'fragment' => Environment::slugify($title[0]), - 'title' => $title[0], - 'children' => $this->generateToc($metaEntry, $title[1], $level + 1), - ]; + // this tocTree stores items nested by level (h2, h2[h3, h3], h2[h3], etc.) + $nestedTocTree = []; + foreach ($flatTocTree as $tocItem) { + if (1 === $tocItem['level']) { + $nestedTocTree[] = $tocItem; + } else { + $nestedTocTree[\count($nestedTocTree) - 1]['children'][] = $tocItem; + } } - return $tocTree; + return $nestedTocTree; } private function determineNext(string $parserFilename, array $flattenedTocTree): ?array diff --git a/src/KernelFactory.php b/src/KernelFactory.php index 8d640d8a..2feb16a3 100644 --- a/src/KernelFactory.php +++ b/src/KernelFactory.php @@ -21,9 +21,6 @@ use SymfonyDocsBuilder\Twig\TocExtension; use function Symfony\Component\String\u; -/** - * Class KernelFactory. - */ final class KernelFactory { public static function createKernel(BuildConfig $buildConfig, ?UrlChecker $urlChecker = null): Kernel @@ -91,6 +88,8 @@ private static function getDirectives(): array new SymfonyDirectives\ScreencastDirective(), new SymfonyDirectives\SeeAlsoDirective(), new SymfonyDirectives\SidebarDirective(), + new SymfonyDirectives\TabDirective(), + new SymfonyDirectives\TabsDirective(), new SymfonyDirectives\TipDirective(), new SymfonyDirectives\TopicDirective(), new SymfonyDirectives\WarningDirective(), diff --git a/src/Listener/DuplicatedHeaderIdListener.php b/src/Listener/DuplicatedHeaderIdListener.php new file mode 100644 index 00000000..b0d139bf --- /dev/null +++ b/src/Listener/DuplicatedHeaderIdListener.php @@ -0,0 +1,25 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SymfonyDocsBuilder\Listener; + +use Doctrine\RST\Event\PreParseDocumentEvent; +use SymfonyDocsBuilder\Renderers\TitleNodeRenderer; + +final class DuplicatedHeaderIdListener +{ + public function preParseDocument(PreParseDocumentEvent $event): void + { + // needed because we only need to handle duplicated headers within + // the same file, not across all the files being generated + TitleNodeRenderer::resetHeaderIdCache(); + } +} diff --git a/src/Node/TabNode.php b/src/Node/TabNode.php new file mode 100644 index 00000000..620cc750 --- /dev/null +++ b/src/Node/TabNode.php @@ -0,0 +1,41 @@ +nodes = $nodes; + $this->tabName = $tabName; + + parent::__construct(); + } + + public function getNodes(): array + { + return $this->nodes; + } + + public function getTabName(): string + { + return $this->tabName; + } + + public function getSluggedTabName(): string + { + return strtolower(str_replace(' ', '-', $this->tabName)); + } +} diff --git a/src/Reference/MethodReference.php b/src/Reference/MethodReference.php index baacc468..b068f334 100644 --- a/src/Reference/MethodReference.php +++ b/src/Reference/MethodReference.php @@ -30,19 +30,19 @@ public function getName(): string public function resolve(Environment $environment, string $data): ResolvedReference { - $className = explode('::', $data)[0]; - $className = str_replace('\\\\', '\\', $className); - - if (!u($data)->containsAny('::')) { + $data = u($data); + if (!$data->containsAny('::')) { throw new \RuntimeException(sprintf('Malformed method reference "%s" in file "%s"', $data, $environment->getCurrentFileName())); } - $methodName = explode('::', $data)[1]; + [$className, $methodName] = $data->split('::', 2); + $className = $className->replace('\\\\', '\\'); + $scrollTextFragment = sprintf('#:~:text=%s', rawurlencode('function '.$methodName)); return new ResolvedReference( $environment->getCurrentFileName(), $methodName.'()', - sprintf('%s/%s.php#method_%s', $this->symfonyRepositoryUrl, str_replace('\\', '/', $className), $methodName), + sprintf('%s/%s.php%s', $this->symfonyRepositoryUrl, $className->replace('\\', '/'), $scrollTextFragment), [], [ 'title' => sprintf('%s::%s()', $className, $methodName), diff --git a/src/Reference/PhpClassReference.php b/src/Reference/PhpClassReference.php index 4e104e17..fbaabe44 100644 --- a/src/Reference/PhpClassReference.php +++ b/src/Reference/PhpClassReference.php @@ -12,6 +12,7 @@ use Doctrine\RST\Environment; use Doctrine\RST\References\Reference; use Doctrine\RST\References\ResolvedReference; +use function Symfony\Component\String\u; class PhpClassReference extends Reference { @@ -29,13 +30,15 @@ public function getName(): string public function resolve(Environment $environment, string $data): ResolvedReference { + $className = u($data)->replace('\\\\', '\\'); + return new ResolvedReference( $environment->getCurrentFileName(), - $data, - sprintf('%s/class.%s.php', $this->phpDocUrl, strtolower($data)), + $className->afterLast('\\'), + sprintf('%s/class.%s.php', $this->phpDocUrl, $className->replace('\\', '-')->lower()), [], [ - 'title' => $data, + 'title' => $className, ] ); } diff --git a/src/Reference/PhpMethodReference.php b/src/Reference/PhpMethodReference.php index ac537440..9a85a918 100644 --- a/src/Reference/PhpMethodReference.php +++ b/src/Reference/PhpMethodReference.php @@ -12,6 +12,7 @@ use Doctrine\RST\Environment; use Doctrine\RST\References\Reference; use Doctrine\RST\References\ResolvedReference; +use function Symfony\Component\String\u; class PhpMethodReference extends Reference { @@ -29,15 +30,21 @@ public function getName(): string public function resolve(Environment $environment, string $data): ResolvedReference { - [$class, $method] = explode('::', $data, 2); + $data = u($data); + if (!$data->containsAny('::')) { + throw new \RuntimeException(sprintf('Malformed method reference "%s" in file "%s"', $data, $environment->getCurrentFileName())); + } + + [$className, $methodName] = $data->split('::', 2); + $className = $className->replace('\\\\', '\\'); return new ResolvedReference( $environment->getCurrentFileName(), - $data.'()', - sprintf('%s/%s.%s.php', $this->phpDocUrl, strtolower($class), strtolower($method)), + $methodName.'()', + sprintf('%s/%s.%s.php', $this->phpDocUrl, $className->replace('\\', '-')->lower(), $methodName->lower()), [], [ - 'title' => $class, + 'title' => sprintf('%s::%s()', $className, $methodName), ] ); } diff --git a/src/Renderers/CodeNodeRenderer.php b/src/Renderers/CodeNodeRenderer.php index fc54b415..d5e771ee 100644 --- a/src/Renderers/CodeNodeRenderer.php +++ b/src/Renderers/CodeNodeRenderer.php @@ -21,6 +21,7 @@ class CodeNodeRenderer implements NodeRenderer private static $isHighlighterConfigured = false; private const LANGUAGES_MAPPING = [ + 'caddy' => 'plaintext', 'env' => 'bash', 'html+jinja' => 'twig', 'html+twig' => 'twig', @@ -69,9 +70,6 @@ public function render(): string $highLighter = new Highlighter(); $highlightedCode = $highLighter->highlight($languageMapping, $code)->value; - - // this allows to highlight the $ in PHP variable names - $highlightedCode = str_replace('$', '$', $highlightedCode); } if ('terminal' === $language) { @@ -123,6 +121,7 @@ private function configureHighlighter() if (false === self::$isHighlighterConfigured) { Highlighter::registerLanguage('php', __DIR__.'/../Templates/highlight.php/php.json', true); Highlighter::registerLanguage('twig', __DIR__.'/../Templates/highlight.php/twig.json', true); + Highlighter::registerLanguage('yaml', __DIR__.'/../Templates/highlight.php/yaml.json', true); } self::$isHighlighterConfigured = true; diff --git a/src/Renderers/SpanNodeRenderer.php b/src/Renderers/SpanNodeRenderer.php index 65598bd2..e1e77755 100644 --- a/src/Renderers/SpanNodeRenderer.php +++ b/src/Renderers/SpanNodeRenderer.php @@ -32,7 +32,7 @@ public function __construct( SpanNode $span, BaseSpanNodeRenderer $decoratedSpanNodeRenderer, ?UrlChecker $urlChecker = null, - string $symfonyVersion = null + ?string $symfonyVersion = null ) { parent::__construct($environment, $span); @@ -42,6 +42,24 @@ public function __construct( $this->symfonyVersion = $symfonyVersion; } + public function render(): string + { + // Work around "~" being parsed as non-breaking space by rst-parser, + // while this is not part of the specification. + $spanValue = $this->span->getValue(); + + if (str_contains($spanValue, '__TILDE__')) { + throw new \Exception('Cannot render content containing the text "__TILDE__" as it is used as a special placeholder in the build.'); + } + + $spanValue = str_replace('~', '__TILDE__', $spanValue); + $this->span->setValue($spanValue); + + $rendered = parent::render(); + + return str_replace('__TILDE__', '~', $rendered); + } + /** @inheritDoc */ public function link(?string $url, string $title, array $attributes = []): string { diff --git a/src/Renderers/TitleNodeRenderer.php b/src/Renderers/TitleNodeRenderer.php new file mode 100644 index 00000000..10042c5d --- /dev/null +++ b/src/Renderers/TitleNodeRenderer.php @@ -0,0 +1,60 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SymfonyDocsBuilder\Renderers; + +use Doctrine\RST\Environment; +use Doctrine\RST\Nodes\TitleNode; +use Doctrine\RST\Renderers\NodeRenderer; +use Doctrine\RST\Templates\TemplateRenderer; + +class TitleNodeRenderer implements NodeRenderer +{ + /** @var TitleNode */ + private $titleNode; + + /** @var TemplateRenderer */ + private $templateRenderer; + + private static $idUsagesCountByFilename = []; + + public function __construct(TitleNode $titleNode, TemplateRenderer $templateRenderer) + { + $this->titleNode = $titleNode; + $this->templateRenderer = $templateRenderer; + } + + public static function resetHeaderIdCache(): void + { + self::$idUsagesCountByFilename = []; + } + + public function render(): string + { + $filename = $this->titleNode->getEnvironment()->getCurrentFileName(); + $id = $this->titleNode->getId(); + + $idUsagesCount = self::$idUsagesCountByFilename[$filename][$id] ?? 0; + + if (0 === $idUsagesCount) { + $computedId = $this->titleNode->getId(); + } else { + $computedId = Environment::slugify($this->titleNode->getValue()->getText().'-'.$idUsagesCount); + } + + self::$idUsagesCountByFilename[$filename][$id] = $idUsagesCount + 1; + + return $this->templateRenderer->render('header-title.html.twig', [ + 'titleNode' => $this->titleNode, + 'id' => $computedId, + ]); + } +} diff --git a/src/SymfonyHTMLFormat.php b/src/SymfonyHTMLFormat.php index 6dcb04b5..16ea957d 100644 --- a/src/SymfonyHTMLFormat.php +++ b/src/SymfonyHTMLFormat.php @@ -14,15 +14,13 @@ use Doctrine\RST\Formats\Format; use Doctrine\RST\Nodes\CodeNode; use Doctrine\RST\Nodes\SpanNode; +use Doctrine\RST\Nodes\TitleNode; use Doctrine\RST\Renderers\CallableNodeRendererFactory; use Doctrine\RST\Renderers\NodeRendererFactory; use Doctrine\RST\Templates\TemplateRenderer; use SymfonyDocsBuilder\CI\UrlChecker; use Doctrine\RST\HTML\Renderers\SpanNodeRenderer as BaseSpanNodeRenderer; -/** - * Class SymfonyHTMLFormat. - */ final class SymfonyHTMLFormat implements Format { protected $templateRenderer; @@ -31,7 +29,7 @@ final class SymfonyHTMLFormat implements Format private $urlChecker; private $symfonyVersion; - public function __construct(TemplateRenderer $templateRenderer, Format $HTMLFormat, ?UrlChecker $urlChecker = null, string $symfonyVersion = null) + public function __construct(TemplateRenderer $templateRenderer, Format $HTMLFormat, ?UrlChecker $urlChecker = null, ?string $symfonyVersion = null) { $this->templateRenderer = $templateRenderer; $this->htmlFormat = $HTMLFormat; @@ -77,6 +75,15 @@ function (SpanNode $node) { } ); + $nodeRendererFactories[TitleNode::class] = new CallableNodeRendererFactory( + function (TitleNode $node) { + return new Renderers\TitleNodeRenderer( + $node, + $this->templateRenderer + ); + } + ); + return $nodeRendererFactories; } } diff --git a/src/Templates/default/html/directives/admonition.html.twig b/src/Templates/default/html/directives/admonition.html.twig index f32f3275..9fb03270 100644 --- a/src/Templates/default/html/directives/admonition.html.twig +++ b/src/Templates/default/html/directives/admonition.html.twig @@ -5,9 +5,9 @@ {% elseif name in ['hint', 'tip'] %} - {% elseif name in ['attention', 'important', 'warning'] %} + {% elseif name in ['attention', 'caution', 'important', 'warning'] %} - {% elseif name in ['caution', 'danger', 'error'] %} + {% elseif name in ['danger', 'error'] %} {% elseif name in ['versionadded', 'deprecated'] %} {# don't show an icon for these directives #} diff --git a/src/Templates/default/html/directives/configuration-block.html.twig b/src/Templates/default/html/directives/configuration-block.html.twig index 03a48d3e..47ceaf62 100644 --- a/src/Templates/default/html/directives/configuration-block.html.twig +++ b/src/Templates/default/html/directives/configuration-block.html.twig @@ -1,5 +1,5 @@
-
+
{% for block in blocks %} + +
+ +
+

Webpack Encore stuff!

+
+
+
1
+
I am yaml:
+
+
+
+ +
diff --git a/tests/fixtures/expected/blocks/nodes/literal.html b/tests/fixtures/expected/blocks/nodes/literal.html index 1c12afae..28eb3bf8 100644 --- a/tests/fixtures/expected/blocks/nodes/literal.html +++ b/tests/fixtures/expected/blocks/nodes/literal.html @@ -12,8 +12,8 @@ namespace Symfony\Component\Routing\Loader\Configurator; return function (RoutingConfigurator $routes) { - $routes->add('about_us', ['nl' => '/over-ons', 'en' => '/about-us']) - ->controller('App\Controller\CompanyController::about'); + $routes->add('about_us', ['nl' => '/over-ons', 'en' => '/about-us']) + ->controller('App\Controller\CompanyController::about'); };
diff --git a/tests/fixtures/expected/blocks/nodes/text.html b/tests/fixtures/expected/blocks/nodes/text.html new file mode 100644 index 00000000..afc3337c --- /dev/null +++ b/tests/fixtures/expected/blocks/nodes/text.html @@ -0,0 +1,7 @@ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut enim ad minim +veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea +commodo consequat. Duis aute irure dolor in reprehenderit in voluptate +velit esse cillum dolore eu fugiat nulla pariatur.

+ +

Excepteur ~1,000 sint! Occaecat cupidatat non proident, sunt in culpa qui officia +deserunt mollit anim id est laborum. áàâäãåéèêëíìîïóòôöõúùûüñçÿ

diff --git a/tests/fixtures/expected/blocks/nodes/title.html b/tests/fixtures/expected/blocks/nodes/title.html index 5ba7a4bb..2106a603 100644 --- a/tests/fixtures/expected/blocks/nodes/title.html +++ b/tests/fixtures/expected/blocks/nodes/title.html @@ -10,5 +10,8 @@

Перевірка робочого середовища

Lorem ipsum dolor sit amet, consectetur adipisicing elit.

+ diff --git a/tests/fixtures/expected/blocks/references/method.html b/tests/fixtures/expected/blocks/references/method.html index 4c58d318..6046098e 100644 --- a/tests/fixtures/expected/blocks/references/method.html +++ b/tests/fixtures/expected/blocks/references/method.html @@ -1 +1 @@ -

getCurrentRequest()

+

getCurrentRequest()

diff --git a/tests/fixtures/expected/blocks/references/php-class.html b/tests/fixtures/expected/blocks/references/php-class.html index 271b968c..a91b1fd2 100644 --- a/tests/fixtures/expected/blocks/references/php-class.html +++ b/tests/fixtures/expected/blocks/references/php-class.html @@ -1 +1,2 @@

ArrayAccess

+

Number

diff --git a/tests/fixtures/expected/blocks/references/php-method.html b/tests/fixtures/expected/blocks/references/php-method.html index e86c5717..59323ada 100644 --- a/tests/fixtures/expected/blocks/references/php-method.html +++ b/tests/fixtures/expected/blocks/references/php-method.html @@ -1 +1,2 @@ -

Locale::getDefault()

+

getDefault()

+

add()

diff --git a/tests/fixtures/expected/build-pdf/book.html b/tests/fixtures/expected/build-pdf/book.html index a2144636..4454f945 100644 --- a/tests/fixtures/expected/build-pdf/book.html +++ b/tests/fixtures/expected/build-pdf/book.html @@ -8,8 +8,8 @@

Book

Here is a link to the main index

diff --git a/tests/fixtures/expected/main/datetime.html b/tests/fixtures/expected/main/datetime.html index c75b859e..d6892fa9 100644 --- a/tests/fixtures/expected/main/datetime.html +++ b/tests/fixtures/expected/main/datetime.html @@ -76,10 +76,10 @@

Tip

This is a little tip about something! We an also talk about specific -methods: doRequest(). +methods: doRequest(). Or a namespace: Constraints. Or a PHP function: parse_ini_file. -Or a PHP method! Locale::getDefault().

+Or a PHP method! getDefault().

@@ -143,12 +143,10 @@

$ builder - -> - add( + -> + add( 'startDateTime' - , DateTimeType - :: - class, + , DateTimeType::class, array ( 'placeholder' @@ -215,9 +213,7 @@

- - - + Caution

Using too many sidebars or caution directives can be distracting!

@@ -419,8 +415,8 @@

$ container - -> - loadFromExtension( + -> + loadFromExtension( 'framework' , array diff --git a/tests/fixtures/expected/main/index.html b/tests/fixtures/expected/main/index.html index 13463739..628cc916 100644 --- a/tests/fixtures/expected/main/index.html +++ b/tests/fixtures/expected/main/index.html @@ -10,7 +10,7 @@

Some Test Docs!

- +

A header

diff --git a/tests/fixtures/expected/toctree/index.html b/tests/fixtures/expected/toctree/index.html index e07a28e7..676005ad 100644 --- a/tests/fixtures/expected/toctree/index.html +++ b/tests/fixtures/expected/toctree/index.html @@ -9,11 +9,11 @@ diff --git a/tests/fixtures/source/blocks/code-blocks/php-attributes.rst b/tests/fixtures/source/blocks/code-blocks/php-attributes.rst new file mode 100644 index 00000000..808e6c45 --- /dev/null +++ b/tests/fixtures/source/blocks/code-blocks/php-attributes.rst @@ -0,0 +1,67 @@ +.. code-block:: php-attributes + + // src/SomePath/SomeClass.php + namespace App\SomePath; + + use Symfony\Component\Validator\Constraints as Assert; + + class SomeClass + { + #[AttributeName] + private $property1; + + #[AttributeName()] + private $property2; + + #[AttributeName('value')] + private $property3; + + #[AttributeName('value', option: 'value')] + private $property4; + + #[AttributeName(['value' => 'value'])] + private $property5; + + #[AttributeName( + 'value', + option: 'value' + )] + private $property6; + + #[Assert\AttributeName('value')] + private $property7; + + #[Assert\AttributeName( + 'value', + option: 'value' + )] + private $property8; + + #[Route('/blog/{page<\d+>}', name: 'blog_list')] + private $property9; + + #[Assert\GreaterThanOrEqual( + value: 18, + )] + private $property10; + + #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')] + private $property11; + + #[Assert\AtLeastOneOf([ + new Assert\Regex('/#/'), + new Assert\Length(min: 10), + ])] + private $property12; + + public function __construct( + #[TaggedIterator('app.handlers')] + iterable $handlers, + ) { + } + + #[AsController] + public function someAction(#[CurrentUser] User $user) + { + } + } diff --git a/tests/fixtures/source/blocks/code-blocks/php-nested-comments.rst b/tests/fixtures/source/blocks/code-blocks/php-nested-comments.rst new file mode 100644 index 00000000..ea8dc0f4 --- /dev/null +++ b/tests/fixtures/source/blocks/code-blocks/php-nested-comments.rst @@ -0,0 +1,12 @@ +You can do that by adding a "stamp" to your message:: + + use Symfony\Component\Messenger\MessageBusInterface; + use Symfony\Component\Messenger\Stamp\DelayStamp; + + public function index(MessageBusInterface $bus) + { + $bus->dispatch(new SmsNotification('...'), [ + // wait 5 seconds before processing + new DelayStamp(5000), + ]); + } diff --git a/tests/fixtures/source/blocks/code-blocks/php.rst b/tests/fixtures/source/blocks/code-blocks/php.rst index e9d65938..33ed08e6 100644 --- a/tests/fixtures/source/blocks/code-blocks/php.rst +++ b/tests/fixtures/source/blocks/code-blocks/php.rst @@ -1,9 +1,56 @@ - .. code-block:: php + // config/routes.php namespace Symfony\Component\Routing\Loader\Configurator; - return function (RoutingConfigurator $routes) { + use App\Controller\CompanyController; + + return static function (RoutingConfigurator $routes): void { $routes->add('about_us', ['nl' => '/over-ons', 'en' => '/about-us']) - ->controller('App\Controller\CompanyController::about'); + ->controller(CompanyController::class.'::about'); }; + +.. code-block:: php + + enum TextAlign: string implements TranslatableInterface + { + case Left = 'Left aligned'; + case Center = 'Center aligned'; + case Right = 'Right aligned'; + + public function trans(TranslatorInterface $translator, ?string $locale = null): string + { + // Translate enum using custom labels + return match ($this) { + self::Left => $translator->trans('text_align.left.label', locale: $locale), + self::Center => $translator->trans('text_align.center.label', locale: $locale), + self::Right => $translator->trans('text_align.right.label', locale: $locale), + }; + } + } + +.. code-block:: php + + public function getUserBadgeFrom(string $accessToken): UserBadge + { + // get the data from the token + $payload = ...; + + return new UserBadge( + $payload->getUserId(), + fn (string $userIdentifier): User => new User($userIdentifier, $payload->getRoles()) + ); + + // or + return new UserBadge( + $payload->getUserId(), + $this->loadUser(...) + ); + } + +.. code-block:: php + + public function __construct( + private string $username + ) { + } diff --git a/tests/fixtures/source/blocks/code-blocks/twig.rst b/tests/fixtures/source/blocks/code-blocks/twig.rst index 01d06851..229041d1 100644 --- a/tests/fixtures/source/blocks/code-blocks/twig.rst +++ b/tests/fixtures/source/blocks/code-blocks/twig.rst @@ -1,3 +1,11 @@ - .. code-block:: twig + {# some code #} + {% + set some_var = 'some value' # some inline comment + %} + {{ + # another inline comment + 'Lorem Ipsum'|uppercase + # final inline comment + }} diff --git a/tests/fixtures/source/blocks/code-blocks/yaml.rst b/tests/fixtures/source/blocks/code-blocks/yaml.rst index 45ce98d5..f56652d3 100644 --- a/tests/fixtures/source/blocks/code-blocks/yaml.rst +++ b/tests/fixtures/source/blocks/code-blocks/yaml.rst @@ -1,3 +1,12 @@ .. code-block:: yaml # some code + parameters: + credit_card_number: 1234_5678_9012_3456 + long_number: 10_000_000_000 + pi: 3.14159_26535_89793 + hex_words: 0x_CAFE_F00D + canonical: 2001-12-15T02:59:43.1Z + iso8601: 2001-12-14t21:59:43.10-05:00 + spaced: 2001-12-14 21:59:43.10 -5 + date: 2002-12-14 diff --git a/tests/fixtures/source/blocks/directives/danger.rst b/tests/fixtures/source/blocks/directives/danger.rst new file mode 100644 index 00000000..dfc42179 --- /dev/null +++ b/tests/fixtures/source/blocks/directives/danger.rst @@ -0,0 +1,4 @@ + +.. danger:: + + This message is about security risk or data integrity threat. diff --git a/tests/fixtures/source/blocks/directives/tabs.rst b/tests/fixtures/source/blocks/directives/tabs.rst new file mode 100644 index 00000000..555a7328 --- /dev/null +++ b/tests/fixtures/source/blocks/directives/tabs.rst @@ -0,0 +1,16 @@ + +.. tabs:: UX Installation + + .. tab:: Webpack Encore + + Webpack Encore stuff! + + .. code-block:: yaml + + I am yaml: + + .. tab:: AssetMapper + + AssetMapper stuff! + + And another paragraph. diff --git a/tests/fixtures/source/blocks/nodes/text.rst b/tests/fixtures/source/blocks/nodes/text.rst new file mode 100644 index 00000000..f5dbb33e --- /dev/null +++ b/tests/fixtures/source/blocks/nodes/text.rst @@ -0,0 +1,7 @@ +**Lorem ipsum** dolor sit amet, consectetur adipisicing elit. Ut enim ad minim +veniam, quis nostrud *exercitation ullamco laboris* nisi ut aliquip ex ea +commodo consequat. ``Duis aute irure dolor`` in reprehenderit in voluptate +velit esse cillum dolore eu fugiat nulla pariatur. + +Excepteur ~1,000 sint! Occaecat cupidatat non proident, sunt in culpa qui officia +deserunt mollit anim id est laborum. áàâäãåéèêëíìîïóòôöõúùûüñçÿ diff --git a/tests/fixtures/source/blocks/nodes/title.rst b/tests/fixtures/source/blocks/nodes/title.rst index 7014b146..60269213 100644 --- a/tests/fixtures/source/blocks/nodes/title.rst +++ b/tests/fixtures/source/blocks/nodes/title.rst @@ -12,3 +12,6 @@ Lorem ipsum dolor sit amet, consectetur adipisicing elit. ----------------------------- Lorem ipsum dolor sit amet, consectetur adipisicing elit. + +``Checking your Work Environment`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/tests/fixtures/source/blocks/references/php-class.rst b/tests/fixtures/source/blocks/references/php-class.rst index 5708c647..284a9018 100644 --- a/tests/fixtures/source/blocks/references/php-class.rst +++ b/tests/fixtures/source/blocks/references/php-class.rst @@ -1,2 +1,4 @@ :phpclass:`ArrayAccess` + +:phpclass:`BcMath\\Number` diff --git a/tests/fixtures/source/blocks/references/php-method.rst b/tests/fixtures/source/blocks/references/php-method.rst index 11705cd5..26d782d5 100644 --- a/tests/fixtures/source/blocks/references/php-method.rst +++ b/tests/fixtures/source/blocks/references/php-method.rst @@ -1,2 +1,4 @@ :phpmethod:`Locale::getDefault` + +:phpmethod:`BcMath\\Number::add` diff --git a/tests/fixtures/source/json/design.rst b/tests/fixtures/source/json/design.rst index b9a4537e..2711ac4a 100644 --- a/tests/fixtures/source/json/design.rst +++ b/tests/fixtures/source/json/design.rst @@ -11,11 +11,17 @@ The toctree below should affects the next/prev. The first entry is effectively ignored, as it was already included by the toctree in index.rst (which is parsed first). -Subsection 1 -~~~~~~~~~~~~ +Some subsection +~~~~~~~~~~~~~~~ This is a subsection of the first section. That's all. +Some subsection +~~~~~~~~~~~~~~~ + +This sub-section uses the same title as before to test that the tool +never generated two or more headings with the same ID. + Section 2 --------- @@ -23,6 +29,12 @@ However, crud (which is ALSO included in the toctree in index.rst), WILL be read here, as the "crud" in index.rst has not been read yet (design comes first). Also, design/sub-page WILL be considered. +Some subsection +~~~~~~~~~~~~~~~ + +This sub-section also uses the same title as in the previous section +to test that the tool never generated two or more headings with the same ID. + .. toctree:: :maxdepth: 1