Skip to content

Commit 8d1db85

Browse files
authored
Merge pull request #371 from phpDocumentor/feature/references
Add new compiler pass to move anchors
2 parents 0a51254 + ee89ac4 commit 8d1db85

File tree

39 files changed

+448
-64
lines changed

39 files changed

+448
-64
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ public function apply(ParserContext $parserContext, InlineLexer $lexer): Literal
3636
public function getPriority(): int
3737
{
3838
// Should be executed first as any other rules within may not be interpreted
39-
return 10000;
39+
return 10_000;
4040
}
4141
}

packages/guides/src/Compiler/CompilerContext.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
use Exception;
1717
use phpDocumentor\Guides\Compiler\ShadowTree\TreeNode;
1818
use phpDocumentor\Guides\Nodes\DocumentNode;
19+
use phpDocumentor\Guides\Nodes\Node;
1920
use phpDocumentor\Guides\Nodes\ProjectNode;
2021

2122
class CompilerContext
2223
{
23-
/** @var TreeNode<DocumentNode> */
24+
/** @var TreeNode<Node> */
2425
private TreeNode $shadowTree;
2526

2627
public function __construct(
@@ -42,15 +43,24 @@ public function getDocumentNode(): DocumentNode
4243
return $this->shadowTree->getRoot()->getNode();
4344
}
4445

45-
public function withShadowTree(DocumentNode $documentNode): static
46+
public function withDocumentShadowTree(DocumentNode $documentNode): static
4647
{
4748
$that = clone $this;
4849
$that->shadowTree = TreeNode::createFromDocument($documentNode);
4950

5051
return $that;
5152
}
5253

53-
/** @return TreeNode<DocumentNode> */
54+
/** @param TreeNode<Node> $shadowTree */
55+
public function withShadowTree(TreeNode $shadowTree): static
56+
{
57+
$that = clone $this;
58+
$that->shadowTree = $shadowTree;
59+
60+
return $that;
61+
}
62+
63+
/** @return TreeNode<Node> */
5464
public function getShadowTree(): TreeNode
5565
{
5666
if (!isset($this->shadowTree)) {

packages/guides/src/Compiler/DocumentNodeTraverser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private function traverseForTransformer(
5050
}
5151

5252
foreach ($shadowNode->getChildren() as $shadowChild) {
53-
$this->traverseForTransformer($transformer, $shadowChild, $compilerContext);
53+
$this->traverseForTransformer($transformer, $shadowChild, $compilerContext->withShadowTree($shadowChild));
5454
}
5555

5656
if (!$supports) {

packages/guides/src/Compiler/NodeTransformers/CitationTargetTransformer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public function supports(Node $node): bool
4040

4141
public function getPriority(): int
4242
{
43-
return 20000;
43+
return 20_000;
4444
}
4545
}

packages/guides/src/Compiler/NodeTransformers/ClassNodeTransformer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@ public function supports(Node $node): bool
6161

6262
public function getPriority(): int
6363
{
64-
return 40000;
64+
return 40_000;
6565
}
6666
}

packages/guides/src/Compiler/NodeTransformers/CollectLinkTargetsTransformer.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use SplStack;
1515
use Webmozart\Assert\Assert;
1616

17+
use function assert;
18+
1719
/** @implements NodeTransformer<DocumentNode|AnchorNode> */
1820
final class CollectLinkTargetsTransformer implements NodeTransformer
1921
{
@@ -35,12 +37,17 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node
3537
if ($node instanceof DocumentNode) {
3638
$this->documentStack->push($node);
3739
} elseif ($node instanceof AnchorNode) {
38-
$currentDocument = $this->documentStack->top();
39-
Assert::notNull($currentDocument);
40+
$currentDocument = $compilerContext->getDocumentNode();
41+
$parentSection = $compilerContext->getShadowTree()->getParent()?->getNode();
42+
assert($parentSection instanceof SectionNode);
4043

4144
$compilerContext->getProjectNode()->addLinkTarget(
4245
$node->toString(),
43-
new InternalTarget($currentDocument->getFilePath(), $node->toString()),
46+
new InternalTarget(
47+
$currentDocument->getFilePath(),
48+
$node->toString(),
49+
$parentSection->getTitle()->toString(),
50+
),
4451
);
4552
} elseif ($node instanceof SectionNode) {
4653
$currentDocument = $this->documentStack->top();

packages/guides/src/Compiler/NodeTransformers/FootNodeNamedTransformer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ public function supports(Node $node): bool
4141
public function getPriority(): int
4242
{
4343
// must be run *after* FootNodeNumberedTransformer
44-
return 20000;
44+
return 20_000;
4545
}
4646
}

packages/guides/src/Compiler/NodeTransformers/FootNodeNumberedTransformer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public function supports(Node $node): bool
4040
public function getPriority(): int
4141
{
4242
// must be run *before* FootNodeNamedTransformer
43-
return 30000;
43+
return 30_000;
4444
}
4545
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\Compiler\NodeTransformers;
6+
7+
use ArrayIterator;
8+
use LogicException;
9+
use phpDocumentor\Guides\Compiler\CompilerContext;
10+
use phpDocumentor\Guides\Compiler\NodeTransformer;
11+
use phpDocumentor\Guides\Compiler\ShadowTree\TreeNode;
12+
use phpDocumentor\Guides\Nodes\AnchorNode;
13+
use phpDocumentor\Guides\Nodes\Node;
14+
use phpDocumentor\Guides\Nodes\SectionNode;
15+
use WeakMap;
16+
17+
/** @implements NodeTransformer<AnchorNode> */
18+
final class MoveAnchorTransformer implements NodeTransformer
19+
{
20+
/** @var WeakMap<AnchorNode, true> */
21+
private WeakMap $seen;
22+
23+
public function __construct()
24+
{
25+
$this->seen = new WeakMap();
26+
}
27+
28+
public function enterNode(Node $node, CompilerContext $compilerContext): Node
29+
{
30+
return $node;
31+
}
32+
33+
public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null
34+
{
35+
//When exists in seen, it means that the node has already been processed. Ignore it.
36+
if (isset($this->seen[$node])) {
37+
return $node;
38+
}
39+
40+
$this->seen[$node] = true;
41+
$parent = $compilerContext->getShadowTree()->getParent();
42+
if ($parent === null) {
43+
throw new LogicException('Node not found in shadow tree');
44+
}
45+
46+
$position = $parent->findPosition($node);
47+
if ($position === null) {
48+
throw new LogicException('Node not found in shadow tree');
49+
}
50+
51+
return $this->attemptMoveToNeighbour($parent, $position, $node);
52+
}
53+
54+
public function supports(Node $node): bool
55+
{
56+
return $node instanceof AnchorNode;
57+
}
58+
59+
public function getPriority(): int
60+
{
61+
return 30_000;
62+
}
63+
64+
/** @param TreeNode<Node> $parent */
65+
private function attemptMoveToNeighbour(TreeNode $parent, int $position, AnchorNode $node): AnchorNode|null
66+
{
67+
$current = $this->findNextSection($parent, $position);
68+
if ($current === null) {
69+
if ($parent->getParent() === null) {
70+
return $node;
71+
}
72+
73+
$position = $parent->getParent()->findPosition($parent->getNode());
74+
if ($position === null) {
75+
throw new LogicException('Node not found in shadow tree');
76+
}
77+
78+
return $this->attemptMoveToNeighbour($parent->getParent(), $position, $node);
79+
}
80+
81+
if ($current->getNode() instanceof SectionNode) {
82+
$current->pushChild($node);
83+
84+
return null;
85+
}
86+
87+
return $node;
88+
}
89+
90+
/**
91+
* @param TreeNode<Node> $parent
92+
*
93+
* @return TreeNode<Node>|null
94+
*/
95+
private function findNextSection(TreeNode $parent, int $position): TreeNode|null
96+
{
97+
$children = new ArrayIterator($parent->getChildren());
98+
if ($children->count() <= $position + 1) {
99+
return null;
100+
}
101+
102+
$children->seek($position + 1);
103+
while ($children->valid() && $children->current()->getNode() instanceof AnchorNode) {
104+
$children->next();
105+
}
106+
107+
return $children->current();
108+
}
109+
}

packages/guides/src/Compiler/NodeTransformers/TransformerPass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function run(array $documents, CompilerContext $compilerContext): array
3434
continue;
3535
}
3636

37-
$compilerContext = $compilerContext->withShadowTree($document);
37+
$compilerContext = $compilerContext->withDocumentShadowTree($document);
3838
$documents[$key] = $this->documentNodeTraverser->traverse($document, $compilerContext);
3939
}
4040

0 commit comments

Comments
 (0)