Skip to content

Commit 3d8482e

Browse files
committed
[FEATURE] Enable configurable custom text roles
See https://docutils.sourceforge.io/docs/ref/rst/directives.html#role
1 parent 146a3af commit 3d8482e

35 files changed

+267
-80
lines changed

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

Lines changed: 4 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)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
/**
17+
* sets the default interpreted text role, the role that is used for interpreted text without an explicit role.
18+
*
19+
* https://docutils.sourceforge.io/docs/ref/rst/directives.html#default-role
20+
*/
21+
class DefaultRoleDirective extends SubDirective
22+
{
23+
public function getName(): string
24+
{
25+
return 'default-role';
26+
}
27+
}

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

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

1414
namespace phpDocumentor\Guides\RestructuredText\Directives;
1515

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

packages/guides-restructured-text/src/RestructuredText/TextRoles/AbbreviationTextRole.php

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,15 @@
1212
use function preg_match;
1313
use function trim;
1414

15-
class AbbreviationTextRole implements TextRole
15+
class AbbreviationTextRole extends BaseTextRole
1616
{
17-
final public const NAME = 'abbreviation';
17+
protected string $name = 'abbreviation';
1818

1919
public function __construct(
2020
private readonly LoggerInterface $logger,
2121
) {
2222
}
2323

24-
public function getName(): string
25-
{
26-
return self::NAME;
27-
}
28-
29-
/** @inheritDoc */
30-
public function getAliases(): array
31-
{
32-
return [];
33-
}
34-
3524
/** @return AbbreviationInlineNode */
3625
public function processNode(
3726
DocumentParserContext $documentParserContext,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\TextRoles;
6+
7+
abstract class BaseTextRole implements TextRole
8+
{
9+
protected string $name;
10+
protected string $class = '';
11+
12+
public function getName(): string
13+
{
14+
return $this->name;
15+
}
16+
17+
/** @return string[] */
18+
public function getAliases(): array
19+
{
20+
return [];
21+
}
22+
23+
public function getClass(): string
24+
{
25+
return $this->class;
26+
}
27+
28+
public function setClass(string $class): void
29+
{
30+
$this->class = $class;
31+
}
32+
33+
public function withName(string $name): BaseTextRole
34+
{
35+
$role = clone $this;
36+
$role->name = $name;
37+
38+
return $role;
39+
}
40+
}

packages/guides-restructured-text/src/RestructuredText/TextRoles/DefaultTextRoleFactory.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,22 @@ public function __construct(
2323
$this->textRoles = [...$textRoles];
2424
}
2525

26-
public function registerTextRole(TextRole $textRoles): void
26+
public function registerTextRole(TextRole $textRole): void
2727
{
28-
$this->textRoles[] = $textRoles;
28+
$this->textRoles[] = $textRole;
29+
}
30+
31+
public function replaceTextRole(TextRole $newTextRole): void
32+
{
33+
foreach ($this->textRoles as &$textRole) {
34+
if ($textRole->getName() !== $newTextRole->getName()) {
35+
continue;
36+
}
37+
38+
$textRole = $newTextRole;
39+
}
40+
41+
$this->textRoles[] = $newTextRole;
2942
}
3043

3144
public function getTextRole(string $name, string|null $domain = null): TextRole

packages/guides-restructured-text/src/RestructuredText/TextRoles/GenericTextRole.php

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,27 @@
77
use phpDocumentor\Guides\Nodes\Inline\GenericTextRoleInlineNode;
88
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
99

10-
class GenericTextRole implements TextRole
10+
class GenericTextRole extends BaseTextRole
1111
{
12-
final public const NAME = 'default';
13-
14-
public function getName(): string
15-
{
16-
return self::NAME;
17-
}
18-
19-
/** @inheritDoc */
20-
public function getAliases(): array
21-
{
22-
return [];
23-
}
12+
protected string $name = 'default';
13+
protected string|null $baseRole = null;
2414

2515
public function processNode(
2616
DocumentParserContext $documentParserContext,
2717
string $role,
2818
string $content,
2919
string $rawContent,
3020
): GenericTextRoleInlineNode {
31-
return new GenericTextRoleInlineNode($role, $content);
21+
return new GenericTextRoleInlineNode($this->baseRole ?? $role, $content, $this->getClass());
22+
}
23+
24+
public function getBaseRole(): string|null
25+
{
26+
return $this->baseRole;
27+
}
28+
29+
public function setBaseRole(string|null $baseRole): void
30+
{
31+
$this->baseRole = $baseRole;
3232
}
3333
}

packages/guides-restructured-text/src/RestructuredText/TextRoles/LiteralTextRole.php

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,16 @@
77
use phpDocumentor\Guides\Nodes\Inline\GenericTextRoleInlineNode;
88
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
99

10-
class LiteralTextRole implements TextRole
10+
class LiteralTextRole extends BaseTextRole
1111
{
12-
final public const NAME = 'literal';
13-
14-
public function getName(): string
15-
{
16-
return self::NAME;
17-
}
18-
19-
/** @inheritDoc */
20-
public function getAliases(): array
21-
{
22-
return [];
23-
}
12+
protected string $name = 'literal';
2413

2514
public function processNode(
2615
DocumentParserContext $documentParserContext,
2716
string $role,
2817
string $content,
2918
string $rawContent,
3019
): GenericTextRoleInlineNode {
31-
return new GenericTextRoleInlineNode('literal', $rawContent);
20+
return new GenericTextRoleInlineNode('literal', $rawContent, $this->getClass());
3221
}
3322
}

packages/guides-restructured-text/src/RestructuredText/TextRoles/MathTextRole.php

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,16 @@
77
use phpDocumentor\Guides\Nodes\Inline\GenericTextRoleInlineNode;
88
use phpDocumentor\Guides\RestructuredText\Parser\DocumentParserContext;
99

10-
class MathTextRole implements TextRole
10+
class MathTextRole extends BaseTextRole
1111
{
12-
final public const NAME = 'math';
13-
14-
public function getName(): string
15-
{
16-
return self::NAME;
17-
}
18-
19-
/** @inheritDoc */
20-
public function getAliases(): array
21-
{
22-
return [];
23-
}
12+
protected string $name = 'math';
2413

2514
public function processNode(
2615
DocumentParserContext $documentParserContext,
2716
string $role,
2817
string $content,
2918
string $rawContent,
3019
): GenericTextRoleInlineNode {
31-
return new GenericTextRoleInlineNode('math', $rawContent);
20+
return new GenericTextRoleInlineNode('math', $rawContent, $this->getClass());
3221
}
3322
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\TextRoles;
6+
7+
use phpDocumentor\Guides\Nodes\Inline\GenericTextRoleInlineNode;
8+
use phpDocumentor\Guides\ParserContext;
9+
10+
/**
11+
* This text role is extended by custom text roles that do not feature a base text role:
12+
*
13+
* ```
14+
* .. role:: custom
15+
* :class: special
16+
*
17+
* :custom:`interpreted text`
18+
*
19+
* ```
20+
*/
21+
class SpanTextRole extends BaseTextRole
22+
{
23+
protected string $name = 'span';
24+
25+
public function processNode(
26+
ParserContext $parserContext,
27+
string $role,
28+
string $content,
29+
string $rawContent,
30+
): GenericTextRoleInlineNode {
31+
return new GenericTextRoleInlineNode('span', $rawContent, $this->getClass());
32+
}
33+
}

0 commit comments

Comments
 (0)