Skip to content

Commit ccd58c9

Browse files
linawolfjaapio
authored andcommitted
[FEATURE] Allow inline styles in code-block captions
For this I had to change the format of the caption from string to InlineCompoundNode, a breaking change for projects with custom templates. Additionally also make the caption available for literalinclude code blocks
1 parent efa5b75 commit ccd58c9

File tree

21 files changed

+209
-93
lines changed

21 files changed

+209
-93
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
use phpDocumentor\Guides\RestructuredText\Directives\NoteDirective;
3939
use phpDocumentor\Guides\RestructuredText\Directives\OptionDirective;
4040
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\CodeNodeOptionMapper;
41+
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\DefaultCodeNodeOptionMapper;
4142
use phpDocumentor\Guides\RestructuredText\Directives\PullQuoteDirective;
4243
use phpDocumentor\Guides\RestructuredText\Directives\RawDirective;
4344
use phpDocumentor\Guides\RestructuredText\Directives\ReplaceDirective;
@@ -172,9 +173,7 @@
172173
->set(ClassDirective::class)
173174
->set(CodeBlockDirective::class)
174175
->args([
175-
'$codeNodeOptionMapper' => service(
176-
CodeNodeOptionMapper::class,
177-
),
176+
'$codeNodeOptionMapper' => service(CodeNodeOptionMapper::class),
178177
])
179178
->set(ConfvalDirective::class)
180179
->set(ConfigurationBlockDirective::class)
@@ -352,5 +351,7 @@
352351
->arg('$inlineRules', tagged_iterator('phpdoc.guides.parser.rst.inline_rule'))
353352
->set(GlobSearcher::class)
354353
->set(ToctreeBuilder::class)
355-
->set(CodeNodeOptionMapper::class);
354+
->set(InlineMarkupRule::class)
355+
->set(DefaultCodeNodeOptionMapper::class)
356+
->alias(CodeNodeOptionMapper::class, DefaultCodeNodeOptionMapper::class);
356357
};

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

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
use phpDocumentor\Guides\RestructuredText\Directives\OptionMapper\CodeNodeOptionMapper;
1919
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
2020
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
21-
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
2221
use Psr\Log\LoggerInterface;
2322

24-
use function preg_match;
2523
use function trim;
2624

2725
/**
@@ -37,9 +35,6 @@
3735
*/
3836
final class CodeBlockDirective extends BaseDirective
3937
{
40-
/** @see https://regex101.com/r/I3KttH/1 */
41-
public const LINE_NUMBER_RANGES_REGEX = '/^\d+(-\d+)?(?:,\s*\d+(-\d+)?)*$/';
42-
4338
public function __construct(
4439
private readonly LoggerInterface $logger,
4540
private readonly CodeNodeOptionMapper $codeNodeOptionMapper,
@@ -78,10 +73,7 @@ public function process(
7873
$node->setLanguage($blockContext->getDocumentParserContext()->getCodeBlockDefaultLanguage());
7974
}
8075

81-
$this->setStartingLineNumberBasedOnOptions($directive->getOptions(), $node);
82-
$this->setCaptionBasedOnOptions($directive->getOptions(), $node);
83-
$this->setEmphasizeLinesBasedOnOptions($blockContext, $directive->getOptions(), $node);
84-
$this->codeNodeOptionMapper->apply($node, $directive->getOptions());
76+
$this->codeNodeOptionMapper->apply($node, $directive->getOptions(), $blockContext);
8577

8678
if ($directive->getVariable() !== '') {
8779
$document = $blockContext->getDocumentParserContext()->getDocument();
@@ -92,51 +84,4 @@ public function process(
9284

9385
return $node;
9486
}
95-
96-
/** @param array<string, DirectiveOption> $options */
97-
private function setStartingLineNumberBasedOnOptions(array $options, CodeNode $node): void
98-
{
99-
$startingLineNumber = null;
100-
if (isset($options['linenos']) || isset($options['number-lines'])) {
101-
$startingLineNumber = 1;
102-
}
103-
104-
if (isset($options['number-lines'])) {
105-
$startingLineNumber = $options['number-lines']->getValue() ?? $startingLineNumber;
106-
} elseif (isset($options['lineno-start'])) {
107-
$startingLineNumber = $options['lineno-start']->getValue() ?? $startingLineNumber;
108-
}
109-
110-
if ($startingLineNumber === null) {
111-
return;
112-
}
113-
114-
$node->setStartingLineNumber((int) $startingLineNumber);
115-
}
116-
117-
/** @param DirectiveOption[] $options */
118-
private function setCaptionBasedOnOptions(array $options, CodeNode $node): void
119-
{
120-
$caption = null;
121-
if (isset($options['caption'])) {
122-
$caption = (string) $options['caption']->getValue();
123-
}
124-
125-
$node->setCaption($caption);
126-
}
127-
128-
/** @param DirectiveOption[] $options */
129-
private function setEmphasizeLinesBasedOnOptions(BlockContext $blockContext, array $options, CodeNode $node): void
130-
{
131-
$emphasizeLines = null;
132-
if (isset($options['emphasize-lines'])) {
133-
$emphasizeLines = (string) $options['emphasize-lines']->getValue();
134-
if (!preg_match(self::LINE_NUMBER_RANGES_REGEX, $emphasizeLines)) {
135-
// Input does not fit the pattern, log a warning
136-
$this->logger->warning('Invalid value for option emphasize-lines in code-block directive. Expected format: \'1-5, 7, 33\'', $blockContext->getLoggerInformation());
137-
}
138-
}
139-
140-
$node->setEmphasizeLines($emphasizeLines);
141-
}
14287
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function processNode(
5757
}
5858

5959
$codeNode = new CodeNode(explode("\n", $contents));
60-
$this->codeNodeOptionMapper->apply($codeNode, $directive->getOptions());
60+
$this->codeNodeOptionMapper->apply($codeNode, $directive->getOptions(), $blockContext);
6161

6262
return $codeNode;
6363
}

packages/guides-restructured-text/src/RestructuredText/Directives/OptionMapper/CodeNodeOptionMapper.php

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,19 @@
1414
namespace phpDocumentor\Guides\RestructuredText\Directives\OptionMapper;
1515

1616
use phpDocumentor\Guides\Nodes\CodeNode;
17+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
1718
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
1819

19-
use function trim;
20-
2120
/**
2221
* The directives `code-block` and `literalinclude` both create a CodeNode with the same possible options.
2322
* This common mapper is used by both Directives.
2423
*/
25-
final class CodeNodeOptionMapper
24+
interface CodeNodeOptionMapper
2625
{
2726
/** @param DirectiveOption[] $directiveOptions */
28-
public function apply(CodeNode $codeNode, array $directiveOptions): void
29-
{
30-
if (isset($directiveOptions['language'])) {
31-
$codeNode->setLanguage(trim((string) $directiveOptions['language']->getValue()));
32-
}
33-
34-
$this->setStartingLineNumberBasedOnOptions($directiveOptions, $codeNode);
35-
}
36-
37-
/** @param DirectiveOption[] $options */
38-
private function setStartingLineNumberBasedOnOptions(array $options, CodeNode $node): void
39-
{
40-
if (!isset($options['linenos'])) {
41-
return;
42-
}
43-
44-
$startingLineNumber = 1;
45-
46-
if (isset($options['lineno-start'])) {
47-
$startingLineNumber = (int) $options['lineno-start']->getValue();
48-
}
49-
50-
$node->setStartingLineNumber((int) $startingLineNumber);
51-
}
27+
public function apply(
28+
CodeNode $codeNode,
29+
array $directiveOptions,
30+
BlockContext $blockContext,
31+
): void;
5232
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Directives\OptionMapper;
15+
16+
use phpDocumentor\Guides\Nodes\CodeNode;
17+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
18+
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
19+
use phpDocumentor\Guides\RestructuredText\Parser\InlineParser;
20+
use phpDocumentor\Guides\RestructuredText\Parser\Productions\InlineMarkupRule;
21+
use Psr\Log\LoggerInterface;
22+
23+
use function preg_match;
24+
use function strval;
25+
use function trim;
26+
27+
/**
28+
* The directives `code-block` and `literalinclude` both create a CodeNode with the same possible options.
29+
* This common mapper is used by both Directives.
30+
*/
31+
final class DefaultCodeNodeOptionMapper implements CodeNodeOptionMapper
32+
{
33+
/** @see https://regex101.com/r/I3KttH/1 */
34+
public const LINE_NUMBER_RANGES_REGEX = '/^\d+(-\d+)?(?:,\s*\d+(-\d+)?)*$/';
35+
36+
public function __construct(
37+
private readonly LoggerInterface $logger,
38+
protected InlineMarkupRule $startingRule,
39+
private readonly InlineParser $inlineParser,
40+
) {
41+
}
42+
43+
/** @param DirectiveOption[] $directiveOptions */
44+
public function apply(
45+
CodeNode $codeNode,
46+
array $directiveOptions,
47+
BlockContext $blockContext,
48+
): void {
49+
if (isset($directiveOptions['language'])) {
50+
$codeNode->setLanguage(trim((string) $directiveOptions['language']->getValue()));
51+
}
52+
53+
$this->setStartingLineNumberBasedOnOptions($directiveOptions, $codeNode);
54+
$this->setCaptionBasedOnOptions($directiveOptions, $codeNode, $blockContext);
55+
$this->setEmphasizeLinesBasedOnOptions($directiveOptions, $codeNode, $blockContext);
56+
57+
$this->setStartingLineNumberBasedOnOptions($directiveOptions, $codeNode);
58+
}
59+
60+
/** @param DirectiveOption[] $options */
61+
private function setCaptionBasedOnOptions(
62+
array $options,
63+
CodeNode $node,
64+
BlockContext $blockContext,
65+
): void {
66+
$caption = null;
67+
if (isset($options['caption'])) {
68+
$caption = $this->inlineParser->parse(strval($options['caption']->getValue()), $blockContext);
69+
}
70+
71+
$node->setCaption($caption);
72+
}
73+
74+
/** @param DirectiveOption[] $options */
75+
private function setEmphasizeLinesBasedOnOptions(array $options, CodeNode $node, BlockContext $blockContext): void
76+
{
77+
$emphasizeLines = null;
78+
if (isset($options['emphasize-lines'])) {
79+
$emphasizeLines = (string) $options['emphasize-lines']->getValue();
80+
if (!preg_match(self::LINE_NUMBER_RANGES_REGEX, $emphasizeLines)) {
81+
// Input does not fit the pattern, log a warning
82+
$this->logger->warning('Invalid value for option emphasize-lines. Expected format: \'1-5, 7, 33\'', $blockContext->getLoggerInformation());
83+
}
84+
}
85+
86+
$node->setEmphasizeLines($emphasizeLines);
87+
}
88+
89+
/** @param array<string, DirectiveOption> $options */
90+
private function setStartingLineNumberBasedOnOptions(array $options, CodeNode $node): void
91+
{
92+
$startingLineNumber = null;
93+
if (isset($options['linenos']) || isset($options['number-lines'])) {
94+
$startingLineNumber = 1;
95+
}
96+
97+
if (isset($options['number-lines'])) {
98+
$startingLineNumber = $options['number-lines']->getValue() ?? $startingLineNumber;
99+
} elseif (isset($options['lineno-start'])) {
100+
$startingLineNumber = $options['lineno-start']->getValue() ?? $startingLineNumber;
101+
}
102+
103+
if ($startingLineNumber === null) {
104+
return;
105+
}
106+
107+
$node->setStartingLineNumber((int) $startingLineNumber);
108+
}
109+
}

packages/guides-restructured-text/tests/unit/Parser/Productions/DirectiveRuleTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,12 @@ public function testApplySetsOptionValueMultipleLines(): void
108108
#[DataProvider('codeBlockValueProvider')]
109109
public function testCodeBlockValue(string $input, string $expectedValue): void
110110
{
111+
$logger = new Logger('test');
111112
$this->rule = new DirectiveRule(
112113
$this->givenInlineMarkupRule(),
113114
new Logger('test'),
114115
new GeneralDirective(new DirectiveContentRule(new RuleContainer())),
115-
[$this->directiveHandler, new CodeBlockDirective(new Logger('test'), new CodeNodeOptionMapper())],
116+
[$this->directiveHandler, new CodeBlockDirective($logger, $this->createMock(CodeNodeOptionMapper::class))],
116117
);
117118
$context = $this->createContext($input);
118119
$node = $this->rule->apply($context);

packages/guides/resources/template/html/body/code.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{%- else -%}
55
{% if node.caption %}
66
<div class="code-block-caption">
7-
<span class="caption-text">{{ node.caption }}</span>
7+
<span class="caption-text">{{ renderNode(node.caption) }}</span>
88
</div>
99
{%- endif -%}
1010
<pre{% if node.classes %} class="{{ node.classesString }}"{% endif %}><code class="language-{{ node.language }}{{ node.startingLineNumber ? ' line-numbers' }}"

packages/guides/src/Nodes/CodeNode.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class CodeNode extends TextNode
2020
/** @var int|null The line number to start counting from and display, or null to hide line numbers */
2121
private int|null $startingLineNumber = null;
2222

23-
private string|null $caption = null;
23+
private InlineCompoundNode|null $caption = null;
2424

2525
private string|null $emphasizeLines = null;
2626

@@ -50,12 +50,12 @@ public function getStartingLineNumber(): int|null
5050
return $this->startingLineNumber;
5151
}
5252

53-
public function getCaption(): string|null
53+
public function getCaption(): InlineCompoundNode|null
5454
{
5555
return $this->caption;
5656
}
5757

58-
public function setCaption(string|null $caption): void
58+
public function setCaption(InlineCompoundNode|null $caption): void
5959
{
6060
$this->caption = $caption;
6161
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!-- content start -->
2+
<div class="section" id="code-block-with-caption">
3+
<h1>Code Block with caption</h1>
4+
5+
<div class="code-block-caption">
6+
<span class="caption-text"><span class="pre file">Documentation/guides.xml</span>, <em>excerpt</em></span>
7+
</div><pre><code class="language-xml">&lt;extension
8+
class=&quot;\T3Docs\Typo3DocsTheme\DependencyInjection\Typo3DocsThemeExtension&quot;
9+
typo3-core-preferred=&quot;8.7</code></pre>
10+
</div>
11+
12+
<!-- content end -->
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Code Block with caption
2+
=======================
3+
4+
.. code-block:: xml
5+
:caption: :file:`Documentation/guides.xml`, *excerpt*
6+
7+
<extension
8+
class="\T3Docs\Typo3DocsTheme\DependencyInjection\Typo3DocsThemeExtension"
9+
typo3-core-preferred="8.7

0 commit comments

Comments
 (0)