diff --git a/UPGRADE.md b/UPGRADE.md index 33c1452e..4ff4b5ab 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,11 @@ +# Upgrade to 0.6 + +## TitleNode requires an id + +The TitleNode constructor is changed to require an ID as 4th argument. +Similarly, `Doctrine\RST\NodeFactory::createTitleNode()` has been +updated with an ID as 4th argument. + # Upgrade to 0.5 ## Property visibility changed from protected to private diff --git a/lib/NodeFactory/DefaultNodeFactory.php b/lib/NodeFactory/DefaultNodeFactory.php index 4b5fdf2c..d5bdc80a 100644 --- a/lib/NodeFactory/DefaultNodeFactory.php +++ b/lib/NodeFactory/DefaultNodeFactory.php @@ -78,9 +78,9 @@ public function createTocNode(Environment $environment, array $files, array $opt return $tocNode; } - public function createTitleNode(Node $value, int $level, string $token): TitleNode + public function createTitleNode(Node $value, int $level, string $token, string $id): TitleNode { - $titleNode = $this->create(NodeTypes::TITLE, [$value, $level, $token]); + $titleNode = $this->create(NodeTypes::TITLE, [$value, $level, $token, $id]); assert($titleNode instanceof TitleNode); return $titleNode; diff --git a/lib/NodeFactory/NodeFactory.php b/lib/NodeFactory/NodeFactory.php index b6ffa40a..85284131 100644 --- a/lib/NodeFactory/NodeFactory.php +++ b/lib/NodeFactory/NodeFactory.php @@ -45,7 +45,7 @@ public function createDocumentNode(Environment $environment): DocumentNode; */ public function createTocNode(Environment $environment, array $files, array $options): TocNode; - public function createTitleNode(Node $value, int $level, string $token): TitleNode; + public function createTitleNode(Node $value, int $level, string $token, string $id): TitleNode; public function createSeparatorNode(int $level): SeparatorNode; diff --git a/lib/Nodes/DocumentNode.php b/lib/Nodes/DocumentNode.php index e818de5d..b52075bd 100644 --- a/lib/Nodes/DocumentNode.php +++ b/lib/Nodes/DocumentNode.php @@ -13,6 +13,7 @@ use function array_unshift; use function assert; use function count; +use function in_array; use function is_string; use function sprintf; @@ -33,6 +34,9 @@ class DocumentNode extends Node /** @var Node[] */ private $nodes = []; + /** @var string[] */ + private $implicitLinkTargets = []; + public function __construct(Environment $environment) { parent::__construct(); @@ -91,6 +95,24 @@ public function getNodes(?callable $function = null): array return $nodes; } + /** + * Creates an implicit hyperlink target for this document. + * + * @param string $preferredId The preferred ID of the hyperlink target, the actual ID is returned by the method (this avoids duplicates) + */ + public function createImplicitLinkTarget(string $preferredId): string + { + $i = 1; + $actualId = $preferredId; + while (in_array($actualId, $this->implicitLinkTargets, true)) { + $actualId = $preferredId . '-' . ($i++); + } + + $this->implicitLinkTargets[] = $actualId; + + return $actualId; + } + public function getTitle(): ?string { foreach ($this->nodes as $node) { diff --git a/lib/Nodes/TitleNode.php b/lib/Nodes/TitleNode.php index a55f3e37..936eabf1 100644 --- a/lib/Nodes/TitleNode.php +++ b/lib/Nodes/TitleNode.php @@ -4,8 +4,6 @@ namespace Doctrine\RST\Nodes; -use Doctrine\RST\Environment; - class TitleNode extends Node { /** @var SpanNode */ @@ -23,13 +21,13 @@ class TitleNode extends Node /** @var string */ private $target = ''; - public function __construct(Node $value, int $level, string $token) + public function __construct(Node $value, int $level, string $token, string $id) { parent::__construct($value); $this->level = $level; $this->token = $token; - $this->id = Environment::slugify($this->value->getText()); + $this->id = $id; } public function getValue(): SpanNode diff --git a/lib/Parser/DocumentParser.php b/lib/Parser/DocumentParser.php index a6a7b11d..a9ffa4d9 100644 --- a/lib/Parser/DocumentParser.php +++ b/lib/Parser/DocumentParser.php @@ -490,11 +490,13 @@ private function flush(): void $level = $this->environment->getConfiguration()->getInitialHeaderLevel() + $level - 1; $token = $this->environment->createTitle($level); + $id = $this->document->createImplicitLinkTarget(Environment::slugify($data)); $node = $this->nodeFactory->createTitleNode( $this->parser->createSpanNode($data), $level, - $token + $token, + $id ); if ($this->lastTitleNode !== null) { diff --git a/tests/DefaultNodeFactoryTest.php b/tests/DefaultNodeFactoryTest.php index 6954365a..7e80ce49 100644 --- a/tests/DefaultNodeFactoryTest.php +++ b/tests/DefaultNodeFactoryTest.php @@ -92,12 +92,12 @@ public function testCreateTitle(): void $nodeInstantiator->expects(self::once()) ->method('create') - ->with([$node, 1, 'test']) + ->with([$node, 1, 'test', 'test']) ->willReturn($expectedReturn); $defaultNodeFactory = $this->createDefaultNodeFactory($nodeInstantiator); - self::assertSame($expectedReturn, $defaultNodeFactory->createTitleNode($node, 1, 'test')); + self::assertSame($expectedReturn, $defaultNodeFactory->createTitleNode($node, 1, 'test', 'test')); } public function testCreateSeparator(): void diff --git a/tests/Functional/tests/section-nesting/section-nesting.html b/tests/Functional/tests/section-nesting/section-nesting.html index ce0dcfcc..b25528c9 100644 --- a/tests/Functional/tests/section-nesting/section-nesting.html +++ b/tests/Functional/tests/section-nesting/section-nesting.html @@ -37,7 +37,7 @@