Skip to content

Commit de05e4d

Browse files
authored
Merge pull request #514 from phpDocumentor/feature-role-directive
[FEATURE] Enable configurable custom and default text roles
2 parents 146a3af + 4d69875 commit de05e4d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+418
-114
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use phpDocumentor\Guides\RestructuredText\Directives\ContainerDirective;
1414
use phpDocumentor\Guides\RestructuredText\Directives\ContentsDirective;
1515
use phpDocumentor\Guides\RestructuredText\Directives\DangerDirective;
16+
use phpDocumentor\Guides\RestructuredText\Directives\DefaultRoleDirective;
1617
use phpDocumentor\Guides\RestructuredText\Directives\DeprecatedDirective;
1718
use phpDocumentor\Guides\RestructuredText\Directives\DocumentBlockDirective;
1819
use phpDocumentor\Guides\RestructuredText\Directives\ErrorDirective;
@@ -90,6 +91,7 @@
9091
use phpDocumentor\Guides\RestructuredText\TextRoles\LiteralTextRole;
9192
use phpDocumentor\Guides\RestructuredText\TextRoles\MathTextRole;
9293
use phpDocumentor\Guides\RestructuredText\TextRoles\ReferenceTextRole;
94+
use phpDocumentor\Guides\RestructuredText\TextRoles\SpanTextRole;
9395
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRole;
9496
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
9597
use phpDocumentor\Guides\RestructuredText\Toc\GlobSearcher;
@@ -135,6 +137,7 @@
135137
->set(AbbreviationTextRole::class)
136138
->set(MathTextRole::class)
137139
->set(LiteralTextRole::class)
140+
->set(SpanTextRole::class)
138141

139142
->set(AdmonitionDirective::class)
140143
->set(AttentionDirective::class)
@@ -150,6 +153,7 @@
150153
->set(ContentsDirective::class)
151154
->arg('$urlGenerator', service(UrlGeneratorInterface::class))
152155
->set(DangerDirective::class)
156+
->set(DefaultRoleDirective::class)
153157
->set(DeprecatedDirective::class)
154158
->set(DocumentBlockDirective::class)
155159
->set(ErrorDirective::class)
@@ -189,6 +193,7 @@
189193

190194
->set(DefaultTextRoleFactory::class, DefaultTextRoleFactory::class)
191195
->arg('$genericTextRole', inline_service(GenericTextRole::class))
196+
->arg('$defaultTextRole', inline_service(LiteralTextRole::class))
192197
->arg('$textRoles', tagged_iterator('phpdoc.guides.parser.rst.text_role'))
193198
->alias(TextRoleFactory::class, DefaultTextRoleFactory::class)
194199

packages/guides-restructured-text/src/RestructuredText/DependencyInjection/Compiler/TextRolePass.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace phpDocumentor\Guides\RestructuredText\DependencyInjection\Compiler;
66

7-
use phpDocumentor\Guides\RestructuredText\TextRoles\DefaultTextRoleFactory;
7+
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
88
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
99
use Symfony\Component\DependencyInjection\ContainerBuilder;
1010
use Symfony\Component\DependencyInjection\Reference;
@@ -13,7 +13,7 @@ class TextRolePass implements CompilerPassInterface
1313
{
1414
public function process(ContainerBuilder $container): void
1515
{
16-
$textRoleFactory = $container->findDefinition(DefaultTextRoleFactory::class);
16+
$textRoleFactory = $container->findDefinition(TextRoleFactory::class);
1717
$domains = [];
1818
$textRoles = [];
1919

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\Directives;
6+
7+
use phpDocumentor\Guides\Nodes\Node;
8+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
9+
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
10+
11+
/**
12+
* Extend this class to create a directive that does some actions, for example on the parser context, without
13+
* creating a node.
14+
*/
15+
abstract class ActionDirective extends BaseDirective
16+
{
17+
public function process(
18+
DocumentParserContext $documentParserContext,
19+
Directive $directive,
20+
): Node|null {
21+
$this->processAction($documentParserContext, $directive);
22+
23+
return null;
24+
}
25+
26+
/**
27+
* @param DocumentParserContext $documentParserContext the current document context with the content
28+
* of the directive
29+
* @param Directive $directive parsed directive containing options and variable
30+
*/
31+
abstract public function processAction(
32+
DocumentParserContext $documentParserContext,
33+
Directive $directive,
34+
): void;
35+
}

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

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,6 @@ public function processNode(
7676
return new GenericNode($directive->getVariable(), $directive->getData());
7777
}
7878

79-
/**
80-
* This can be overloaded to write a directive that just do an action without changing
81-
* the nodes of the document
82-
*
83-
* The arguments are the same that process
84-
*
85-
* @param mixed[] $options
86-
*/
87-
public function processAction(
88-
DocumentParserContext $documentParserContext,
89-
string $variable,
90-
string $data,
91-
array $options,
92-
): void {
93-
}
94-
9579
/**
9680
* @param DirectiveOption[] $options
9781
*
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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;
15+
16+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
17+
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
18+
19+
/**
20+
* sets the default interpreted text role, the role that is used for interpreted text without an explicit role.
21+
*
22+
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#default-role
23+
*/
24+
class DefaultRoleDirective extends ActionDirective
25+
{
26+
public function getName(): string
27+
{
28+
return 'default-role';
29+
}
30+
31+
public function processAction(
32+
DocumentParserContext $documentParserContext,
33+
Directive $directive,
34+
): void {
35+
$name = $directive->getData();
36+
$documentParserContext->getTextRoleFactoryForDocument()->setDefaultTextRole($name);
37+
}
38+
}

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,62 @@
1313

1414
namespace phpDocumentor\Guides\RestructuredText\Directives;
1515

16-
class RoleDirective extends SubDirective
16+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
17+
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
18+
use phpDocumentor\Guides\RestructuredText\TextRoles\BaseTextRole;
19+
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericTextRole;
20+
use Psr\Log\LoggerInterface;
21+
22+
use function is_string;
23+
use function preg_match;
24+
use function trim;
25+
26+
/**
27+
* The "role" directive dynamically creates a custom interpreted text role and registers it with the parser.
28+
*
29+
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#role
30+
*/
31+
class RoleDirective extends ActionDirective
1732
{
33+
public function __construct(
34+
private readonly LoggerInterface $logger,
35+
) {
36+
}
37+
1838
public function getName(): string
1939
{
2040
return 'role';
2141
}
42+
43+
public function processAction(
44+
DocumentParserContext $documentParserContext,
45+
Directive $directive,
46+
): void {
47+
$name = $directive->getData();
48+
$role = 'span';
49+
if (preg_match('/^([A-Za-z-]*)\(([A-Za-z-]*)\)$/', trim($name), $match) > 0) {
50+
$name = $match[1];
51+
$role = $match[2];
52+
}
53+
54+
$baseRole = $documentParserContext->getTextRoleFactoryForDocument()->getTextRole($role);
55+
if (!$baseRole instanceof BaseTextRole) {
56+
$this->logger->error('Text role "' . $role . '", class ' . $baseRole::class . ' cannot be extended. ');
57+
58+
return;
59+
}
60+
61+
$customRole = $baseRole->withName($name);
62+
if (is_string($directive->getOption('class')->getValue())) {
63+
$customRole->setClass($directive->getOption('class')->getValue());
64+
} else {
65+
$customRole->setClass($name);
66+
}
67+
68+
if ($customRole instanceof GenericTextRole) {
69+
$customRole->setBaseRole($role);
70+
}
71+
72+
$documentParserContext->getTextRoleFactoryForDocument()->replaceTextRole($customRole);
73+
}
2274
}

packages/guides-restructured-text/src/RestructuredText/MarkupLanguageParser.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use phpDocumentor\Guides\ParserContext;
1111
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
1212
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
13+
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
1314
use RuntimeException;
1415
use Webmozart\Assert\Assert;
1516

@@ -26,6 +27,7 @@ class MarkupLanguageParser implements ParserInterface
2627
/** @param Rule<DocumentNode> $startingRule */
2728
public function __construct(
2829
private readonly Rule $startingRule,
30+
private readonly TextRoleFactory $textRoleFactory,
2931
) {
3032
}
3133

@@ -39,6 +41,7 @@ public function getSubParser(): MarkupLanguageParser
3941
{
4042
return new MarkupLanguageParser(
4143
$this->startingRule,
44+
$this->textRoleFactory,
4245
);
4346
}
4447

@@ -74,6 +77,7 @@ public function parse(ParserContext $parserContext, string $contents): DocumentN
7477
$this->documentParser = new DocumentParserContext(
7578
$contents,
7679
$parserContext,
80+
$this->textRoleFactory,
7781
$this,
7882
);
7983

packages/guides-restructured-text/src/RestructuredText/Parser/DocumentParserContext.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use phpDocumentor\Guides\Nodes\ProjectNode;
1818
use phpDocumentor\Guides\ParserContext;
1919
use phpDocumentor\Guides\RestructuredText\MarkupLanguageParser;
20+
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
2021
use RuntimeException;
2122

2223
/**
@@ -31,16 +32,21 @@ class DocumentParserContext
3132

3233
private LinesIterator $documentIterator;
3334
private int $currentTitleLevel;
35+
/* Each Document has its own text role factory as text roles can be changed on a per document base
36+
by directives */
37+
private TextRoleFactory $textRoleFactoryForDocument;
3438

3539
/** @var string[] */
3640
private array $titleLetters = [];
3741

3842
public function __construct(
3943
string $content,
4044
private readonly ParserContext $context,
45+
TextRoleFactory $textRoleFactory,
4146
private readonly MarkupLanguageParser $markupLanguageParser,
4247
) {
4348
$this->documentIterator = new LinesIterator();
49+
$this->textRoleFactoryForDocument = clone $textRoleFactory;
4450
$this->documentIterator->load($content);
4551
$this->currentTitleLevel = $context->getInitialHeaderLevel() - 1;
4652
}
@@ -115,4 +121,9 @@ public function withContentsPreserveSpace(string $contents): self
115121

116122
return $that;
117123
}
124+
125+
public function getTextRoleFactoryForDocument(): TextRoleFactory
126+
{
127+
return $this->textRoleFactoryForDocument;
128+
}
118129
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace phpDocumentor\Guides\RestructuredText\Parser\Productions\InlineRules;
66

77
use phpDocumentor\Guides\Nodes\Inline\InlineNode;
8-
use phpDocumentor\Guides\Nodes\Inline\LiteralInlineNode;
98
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
109
use phpDocumentor\Guides\RestructuredText\Parser\InlineLexer;
1110

@@ -36,7 +35,10 @@ public function apply(DocumentParserContext $documentParserContext, InlineLexer
3635

3736
$lexer->moveNext();
3837

39-
return new LiteralInlineNode($text);
38+
return $documentParserContext
39+
->getTextRoleFactoryForDocument()
40+
->getDefaultTextRole()
41+
->processNode($documentParserContext, '', $text, $text);
4042

4143
default:
4244
$text .= $token->value;

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use phpDocumentor\Guides\Nodes\Inline\InlineNode;
88
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
99
use phpDocumentor\Guides\RestructuredText\Parser\InlineLexer;
10-
use phpDocumentor\Guides\RestructuredText\TextRoles\TextRoleFactory;
1110

1211
use function substr;
1312

@@ -16,10 +15,6 @@
1615
*/
1716
class TextRoleRule extends AbstractInlineRule
1817
{
19-
public function __construct(private readonly TextRoleFactory $textRoleFactory)
20-
{
21-
}
22-
2318
public function applies(InlineLexer $lexer): bool
2419
{
2520
return $lexer->token?->type === InlineLexer::COLON;
@@ -54,7 +49,7 @@ public function apply(DocumentParserContext $documentParserContext, InlineLexer
5449
}
5550

5651
if ($inText) {
57-
$textRole = $this->textRoleFactory->getTextRole($role, $domain);
52+
$textRole = $documentParserContext->getTextRoleFactoryForDocument()->getTextRole($role, $domain);
5853
$fullRole = ($domain ? $domain . ':' : '') . $role;
5954
$lexer->moveNext();
6055

0 commit comments

Comments
 (0)