Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Generators/HTML: improve anchor links #859

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
64 changes: 61 additions & 3 deletions src/Generators/HTML.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ class HTML extends Generator
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -100,6 +113,13 @@ class HTML extends Generator
}
</style>';

/**
* List of seen slugified anchors to ensure uniqueness.
*
* @var array<string, true>
*/
private $seenAnchors = [];


/**
* Generates the documentation for a standard.
Expand All @@ -119,6 +139,10 @@ public function generate()
$content = ob_get_contents();
ob_end_clean();

// Clear anchor cache after Documentation generation.
// The anchor generation for the TOC anchor links will use the same logic, so should end up with the same unique slugs.
$this->seenAnchors = [];

if (trim($content) !== '') {
echo $this->getFormattedHeader();
echo $this->getFormattedToc();
Expand Down Expand Up @@ -215,7 +239,7 @@ protected function getFormattedToc()
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$title = $this->getTitle($documentation);
$output .= sprintf($listItemTemplate, str_replace(' ', '-', $title), $title);
$output .= sprintf($listItemTemplate, $this->titleToAnchor($title), $title);
}

$output .= ' </ul>'.PHP_EOL;
Expand Down Expand Up @@ -290,14 +314,48 @@ public function processSniff(DOMNode $doc)

if (trim($content) !== '') {
$title = $this->getTitle($doc);
echo ' <a name="'.str_replace(' ', '-', $title).'" />'.PHP_EOL;
echo ' <h2>'.$title.'</h2>'.PHP_EOL;
printf(
' <h2 id="%1$s">%2$s<a class="sniffanchor" href="#%1$s"> &sect; </a></h2>'.PHP_EOL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the href attribute need to have its value URL-encoded here? Or, switch the logic in titleToAnchor() to only return characters which do not need to be encoded.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea 👍🏻 Wasn't what I was focussing on for this PR, but that makes for a nice additional enhancement.

Let's slugify the anchor, though that increases the risk of duplicate anchors, so I'll work something out for that as well.

$this->titleToAnchor($title),
$title
);
echo $content;
}

}//end processSniff()


/**
* Transform a title to a string which can be used as an HTML anchor.
*
* @param string $title The title.
*
* @since 3.12.0
*
* @return string
*/
private function titleToAnchor($title)
{
// Slugify the text.
$title = strtolower($title);
$title = str_replace(' ', '-', $title);
$title = preg_replace('`[^a-z0-9._-]`', '-', $title);

if (isset($this->seenAnchors[$title]) === false) {
// Add to "seen" list.
$this->seenAnchors[$title] = true;
} else {
// Try to find a unique anchor for this title.
for ($i = 2; (isset($this->seenAnchors[$title.'-'.$i]) === true); $i++);
$title .= '-'.$i;
$this->seenAnchors[$title] = true;
}

return $title;

}//end titleToAnchor()


/**
* Print a text block found in a standard.
*
Expand Down
10 changes: 10 additions & 0 deletions tests/Core/Generators/AnchorLinksTest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="GeneratorTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">

<config name="installed_paths" value="./tests/Core/Generators/Fixtures/"/>

<rule ref="StandardWithDocs.Content.DocumentationTitleToAnchorSlug1"/>
<rule ref="StandardWithDocs.Content.DocumentationTitleToAnchorSlug2"/>
<rule ref="StandardWithDocs.Content.DocumentationTitleToAnchorSlug3"/>

</ruleset>
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Code-Comparison,-blank-lines" />
<h2>Code Comparison, blank lines</h2>
<h2 id="code-comparison--blank-lines">Code Comparison, blank lines<a class="sniffanchor" href="#code-comparison--blank-lines"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<table class="code-comparison">
<tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Code-Comparison,-block-length" />
<h2>Code Comparison, block length</h2>
<h2 id="code-comparison--block-length">Code Comparison, block length<a class="sniffanchor" href="#code-comparison--block-length"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<table class="code-comparison">
<tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Code-Comparison,-char-encoding" />
<h2>Code Comparison, char encoding</h2>
<h2 id="code-comparison--char-encoding">Code Comparison, char encoding<a class="sniffanchor" href="#code-comparison--char-encoding"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<table class="code-comparison">
<tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Code-Comparison,-line-length" />
<h2>Code Comparison, line length</h2>
<h2 id="code-comparison--line-length">Code Comparison, line length<a class="sniffanchor" href="#code-comparison--line-length"> &sect; </a></h2>
<p class="text">Ensure there is no PHP &quot;Warning: str_repeat(): Second argument has to be greater than or equal to 0&quot;.<br/>
Ref: squizlabs/PHP_CodeSniffer#2522</p>
<table class="code-comparison">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Code-Title,-line-wrapping" />
<h2>Code Title, line wrapping</h2>
<h2 id="code-title--line-wrapping">Code Title, line wrapping<a class="sniffanchor" href="#code-title--line-wrapping"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<table class="code-comparison">
<tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Code-Title,-whitespace-handling" />
<h2>Code Title, whitespace handling</h2>
<h2 id="code-title--whitespace-handling">Code Title, whitespace handling<a class="sniffanchor" href="#code-title--whitespace-handling"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<table class="code-comparison">
<tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="lowercase-title" />
<h2>lowercase title</h2>
<h2 id="lowercase-title">lowercase title<a class="sniffanchor" href="#lowercase-title"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<div class="tag-line">Documentation generated on #REDACTED# by <a href="https://github.com/PHPCSStandards/PHP_CodeSniffer">PHP_CodeSniffer #VERSION#</a></div>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="This-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title" />
<h2>This is a very very very very very very very very very very very long title</h2>
<h2 id="this-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title">This is a very very very very very very very very very very very long title<a class="sniffanchor" href="#this-is-a-very-very-very-very-very-very-very-very-very-very-very-long-title"> &sect; </a></h2>
<p class="text">This is a standard block.</p>
<div class="tag-line">Documentation generated on #REDACTED# by <a href="https://github.com/PHPCSStandards/PHP_CodeSniffer">PHP_CodeSniffer #VERSION#</a></div>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
margin-top: 50px;
}

h2 a.sniffanchor,
h2 a.sniffanchor {
color: #006C95;
opacity: 0;
padding: 0 3px;
text-decoration: none;
font-weight: bold;
}
h2:hover a.sniffanchor,
h2:focus a.sniffanchor {
opacity: 1;
}

.code-comparison {
width: 100%;
}
Expand Down Expand Up @@ -75,8 +88,7 @@
</head>
<body>
<h1>GeneratorTest Coding Standards</h1>
<a name="Documentation-Title-PCRE-Fallback" />
<h2>Documentation Title PCRE Fallback</h2>
<h2 id="documentation-title-pcre-fallback">Documentation Title PCRE Fallback<a class="sniffanchor" href="#documentation-title-pcre-fallback"> &sect; </a></h2>
<p class="text">Testing the document title can get determined from the sniff name if missing.</p>
<p class="text">This file name contains an acronym on purpose to test the word splitting.</p>
<div class="tag-line">Documentation generated on #REDACTED# by <a href="https://github.com/PHPCSStandards/PHP_CodeSniffer">PHP_CodeSniffer #VERSION#</a></div>
Expand Down
Loading