Skip to content

[TASK] Warn about duplicate anchors in LinkTargetNode #945

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ protected function processSub(
CollectionNode $collectionNode,
Directive $directive,
): Node|null {
$id = $this->anchorReducer->reduceAnchor($directive->getData());
$id = $directive->getData();
if ($directive->hasOption('name')) {
$id = (string) $directive->getOption('name')->getValue();
}

$id = $this->anchorReducer->reduceAnchor($id);
$type = null;
$required = false;
$default = null;
Expand All @@ -79,7 +84,7 @@ protected function processSub(
}

foreach ($directive->getOptions() as $option) {
if (in_array($option->getName(), ['type', 'required', 'default', 'noindex'], true)) {
if (in_array($option->getName(), ['type', 'required', 'default', 'noindex', 'name'], true)) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
use phpDocumentor\Guides\Nodes\Node;
use phpDocumentor\Guides\Nodes\SectionNode;
use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer;
use Psr\Log\LoggerInterface;
use SplStack;
use Webmozart\Assert\Assert;

use function sprintf;

/** @implements NodeTransformer<DocumentNode|AnchorNode|SectionNode> */
final class CollectLinkTargetsTransformer implements NodeTransformer
{
Expand All @@ -34,6 +37,7 @@ final class CollectLinkTargetsTransformer implements NodeTransformer

public function __construct(
private readonly AnchorNormalizer $anchorReducer,
private LoggerInterface|null $logger = null,
) {
/*
* TODO: remove stack here, as we should not have sub documents in this way, sub documents are
Expand All @@ -47,7 +51,11 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node
{
if ($node instanceof DocumentNode) {
$this->documentStack->push($node);
} elseif ($node instanceof AnchorNode) {

return $node;
}

if ($node instanceof AnchorNode) {
$currentDocument = $compilerContext->getDocumentNode();
$parentSection = $compilerContext->getShadowTree()->getParent()?->getNode();
$title = null;
Expand All @@ -64,12 +72,33 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node
$title,
),
);
} elseif ($node instanceof LinkTargetNode) {

return $node;
}

if ($node instanceof SectionNode) {
$currentDocument = $this->documentStack->top();
Assert::notNull($currentDocument);
$anchor = $node->getId();
$anchorName = $node->getId();
$compilerContext->getProjectNode()->addLinkTarget(
$anchor,
$anchorName,
new InternalTarget(
$currentDocument->getFilePath(),
$anchorName,
$node->getLinkText(),
$node->getLinkType(),
),
);

return $node;
}

if ($node instanceof LinkTargetNode) {
$currentDocument = $this->documentStack->top();
Assert::notNull($currentDocument);
$anchor = $this->anchorReducer->reduceAnchor($node->getId());
$this->addLinkTargetToProject(
$compilerContext,
new InternalTarget(
$currentDocument->getFilePath(),
$anchor,
Expand All @@ -79,11 +108,12 @@ public function enterNode(Node $node, CompilerContext $compilerContext): Node
);
if ($node instanceof MultipleLinkTargetsNode) {
foreach ($node->getAdditionalIds() as $id) {
$compilerContext->getProjectNode()->addLinkTarget(
$id,
$anchor = $this->anchorReducer->reduceAnchor($id);
$this->addLinkTargetToProject(
$compilerContext,
new InternalTarget(
$currentDocument->getFilePath(),
$id,
$anchor,
$node->getLinkText(),
$node->getLinkType(),
),
Expand Down Expand Up @@ -114,4 +144,28 @@ public function getPriority(): int
// After MetasPass
return 5000;
}

private function addLinkTargetToProject(CompilerContext $compilerContext, InternalTarget $internalTarget): void
{
if ($compilerContext->getProjectNode()->hasInternalTarget($internalTarget->getAnchor(), $internalTarget->getLinkType())) {
$otherLink = $compilerContext->getProjectNode()->getInternalTarget($internalTarget->getAnchor(), $internalTarget->getLinkType());
$this->logger?->warning(
sprintf(
'Duplicate anchor "%s" for link type "%s" in document "%s". The anchor is already used at "%s"',
$internalTarget->getAnchor(),
$internalTarget->getLinkType(),
$compilerContext->getDocumentNode()->getFilePath(),
$otherLink?->getDocumentPath(),
),
$compilerContext->getLoggerInformation(),
);

return;
}

$compilerContext->getProjectNode()->addLinkTarget(
$internalTarget->getAnchor(),
$internalTarget,
);
}
}
5 changes: 5 additions & 0 deletions packages/guides/src/Nodes/ProjectNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ public function addLinkTarget(string $anchorName, InternalTarget $target): void
$this->internalLinkTargets[$target->getLinkType()][$anchorName] = $target;
}

public function hasInternalTarget(string $anchorName, string $linkType = SectionNode::STD_LABEL): bool
{
return isset($this->internalLinkTargets[$linkType][$anchorName]);
}

public function getInternalTarget(string $anchorName, string $linkType = SectionNode::STD_LABEL): InternalTarget|null
{
return $this->internalLinkTargets[$linkType][$anchorName] ?? null;
Expand Down
4 changes: 2 additions & 2 deletions tests/Functional/tests/section-nesting/section-nesting.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ <h1>
<h1>
Level 1 Test 4
</h1>
<div class="section" id="level-2-test-3">
<div class="section" id="level-2-test-3b">
<h2>
Level 2 Test 3
Level 2 Test 3b
</h2>
<div class="section" id="level-3-test-1">
<h3>
Expand Down
4 changes: 2 additions & 2 deletions tests/Functional/tests/section-nesting/section-nesting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ Level 1 Test 3
Level 1 Test 4
==============

Level 2 Test 3
--------------
Level 2 Test 3b
---------------

Level 3 Test 1
**************
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Confval directive</title>

</head>
<body>
<!-- content start -->
<div class="section" id="confval-directive">
<h1>Confval directive</h1>

<dl class="confval">
<dt id="demo">
<dt id="another-demo">
<code class="sig-name descname"><span class="pre">demo</span></code></dt>
<dd>
<div class="line-block">
Expand All @@ -21,3 +28,5 @@ <h1>Confval directive</h1>
</div>

<!-- content end -->
</body>
</html>
39 changes: 39 additions & 0 deletions tests/Integration/tests/confval/confval-name/expected/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Confval directive</title>

</head>
<body>
<!-- content start -->
<div class="section" id="confval-directive">
<h1>Confval directive</h1>

<dl class="confval">
<dt id="demo">
<code class="sig-name descname"><span class="pre">demo</span></code></dt>
<dd>
<div class="line-block">
<div class="line"><strong>Type:</strong> <code>&quot;Hello World&quot;</code></div>
<div class="line"><strong>Required:</strong> true</div>
<div class="line"><strong>Custom Info:</strong> <strong>custom</strong></div>

</div>
<div class="confval-description">
<p>This is the confval <code>demo</code> content!</p><p>Another paragraph.</p>
</div>
</dd>
</dl>
<p>See option <a href="/index.html#demo">demo</a>.</p>
<div class="toc">
<ul class="menu-level">
<li class="toc-item"><a href="/another.html#confval-directive">Confval directive</a></li>

</ul>
</div>

</div>

<!-- content end -->
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"std:doc": {
"another": [
"-",
"-",
"another.html",
"Confval directive"
],
"index": [
"-",
"-",
"index.html",
"Confval directive"
]
},
"std:label": {
"confval-directive": [
"-",
"-",
"index.html#confval-directive",
"Confval directive"
]
},
"std:confval": {
"another-demo": [
"-",
"-",
"another.html#another-demo",
"demo"
],
"demo": [
"-",
"-",
"index.html#demo",
"demo"
]
}
}
15 changes: 15 additions & 0 deletions tests/Integration/tests/confval/confval-name/input/another.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Confval directive
=================

.. confval:: demo
:name: another-demo
:type: :php:`string`
:default: ``"Hello World"``
:required: true
:Custom Info: **custom**

This is the confval ``demo`` content!

Another paragraph.

See option :confval:`demo`.
19 changes: 19 additions & 0 deletions tests/Integration/tests/confval/confval-name/input/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Confval directive
=================

.. confval:: demo
:type: :php:`string`
:default: ``"Hello World"``
:required: true
:Custom Info: **custom**

This is the confval ``demo`` content!

Another paragraph.

See option :confval:`demo`.

.. toctree::
:glob:

*
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Confval directive</title>

</head>
<body>
<!-- content start -->
<div class="section" id="confval-directive">
<h1>Confval directive</h1>

<dl class="confval">
<dt id="demo">
<code class="sig-name descname"><span class="pre">demo</span></code></dt>
<dd>
<div class="line-block">
<div class="line"><strong>Type:</strong> <code>&quot;Hello World&quot;</code></div>
<div class="line"><strong>Required:</strong> true</div>
<div class="line"><strong>Custom Info:</strong> <strong>custom</strong></div>

</div>
<div class="confval-description">
<p>This is the confval <code>demo</code> content!</p><p>Another paragraph.</p>
</div>
</dd>
</dl>
<p>See option <a href="/another.html#demo">demo</a>.</p>
</div>

<!-- content end -->
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Confval directive</title>

</head>
<body>
<!-- content start -->
<div class="section" id="confval-directive">
<h1>Confval directive</h1>

<dl class="confval">
<dt id="demo">
<code class="sig-name descname"><span class="pre">demo</span></code></dt>
<dd>
<div class="line-block">
<div class="line"><strong>Type:</strong> <code>&quot;Hello World&quot;</code></div>
<div class="line"><strong>Required:</strong> true</div>
<div class="line"><strong>Custom Info:</strong> <strong>custom</strong></div>

</div>
<div class="confval-description">
<p>This is the confval <code>demo</code> content!</p><p>Another paragraph.</p>
</div>
</dd>
</dl>
<p>See option <a href="/another.html#demo">demo</a>.</p>
<div class="toc">
<ul class="menu-level">
<li class="toc-item"><a href="/another.html#confval-directive">Confval directive</a></li>

</ul>
</div>

</div>

<!-- content end -->
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app.WARNING: Duplicate anchor "demo" for link type "std:confval" in document "index". The anchor is already used at "another"
Loading