Fix TOC anchor name collisions in edge cases
authorJackmcbarn <jackmcbarn@gmail.com>
Sun, 28 Dec 2014 20:07:49 +0000 (15:07 -0500)
committerAnomie <bjorsch@wikimedia.org>
Tue, 17 Feb 2015 20:59:33 +0000 (20:59 +0000)
Currently, the parser adds a "_2" to the second of two identical headlines to
avoid collisions, but there's still a collision if another headline actually
ends in "_2". This change causes the new headline to also be checked for a
collision, and advances to "_3" or beyond if there is one.

Bug: T26787
Change-Id: Id0a55aa4c1917bac2f8f0d4863fcb85bd3dff1ca

includes/parser/Parser.php
tests/parser/parserTests.txt

index 7884ca5..a9b0c82 100644 (file)
@@ -4589,16 +4589,22 @@ class Parser {
                                $legacyArrayKey = strtolower( $legacyHeadline );
                        }
 
-                       # count how many in assoc. array so we can track dupes in anchors
+                       # Create the anchor for linking from the TOC to the section
+                       $anchor = $safeHeadline;
+                       $legacyAnchor = $legacyHeadline;
                        if ( isset( $refers[$arrayKey] ) ) {
-                               $refers[$arrayKey]++;
+                               for ( $i = 2; isset( $refers["${arrayKey}_$i"] ); ++$i );
+                               $anchor .= "_$i";
+                               $refers["${arrayKey}_$i"] = true;
                        } else {
-                               $refers[$arrayKey] = 1;
+                               $refers[$arrayKey] = true;
                        }
-                       if ( isset( $refers[$legacyArrayKey] ) ) {
-                               $refers[$legacyArrayKey]++;
+                       if ( $legacyHeadline !== false && isset( $refers[$legacyArrayKey] ) ) {
+                               for ( $i = 2; isset( $refers["${legacyArrayKey}_$i"] ); ++$i );
+                               $legacyAnchor .= "_$i";
+                               $refers["${legacyArrayKey}_$i"] = true;
                        } else {
-                               $refers[$legacyArrayKey] = 1;
+                               $refers[$legacyArrayKey] = true;
                        }
 
                        # Don't number the heading if it is the only one (looks silly)
@@ -4611,15 +4617,6 @@ class Parser {
                                ) . ' ' . $headline;
                        }
 
-                       # Create the anchor for linking from the TOC to the section
-                       $anchor = $safeHeadline;
-                       $legacyAnchor = $legacyHeadline;
-                       if ( $refers[$arrayKey] > 1 ) {
-                               $anchor .= '_' . $refers[$arrayKey];
-                       }
-                       if ( $legacyHeadline !== false && $refers[$legacyArrayKey] > 1 ) {
-                               $legacyAnchor .= '_' . $refers[$legacyArrayKey];
-                       }
                        if ( $enoughToc && ( !isset( $wgMaxTocLevel ) || $toclevel < $wgMaxTocLevel ) ) {
                                $toc .= Linker::tocLine( $anchor, $tocline,
                                        $numbering, $toclevel, ( $isTemplate ? false : $sectionIndex ) );
index d9831e3..cc66280 100644 (file)
@@ -13236,6 +13236,31 @@ Some text
 
 !! end
 
+!! test
+TOC anchors don't collide
+!! wikitext
+__FORCETOC__
+== Headline 2 ==
+== Headline ==
+== Headline 2 ==
+== Headline ==
+!! html
+<div id="toc" class="toc"><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Headline_2"><span class="tocnumber">1</span> <span class="toctext">Headline 2</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Headline"><span class="tocnumber">2</span> <span class="toctext">Headline</span></a></li>
+<li class="toclevel-1 tocsection-3"><a href="#Headline_2_2"><span class="tocnumber">3</span> <span class="toctext">Headline 2</span></a></li>
+<li class="toclevel-1 tocsection-4"><a href="#Headline_3"><span class="tocnumber">4</span> <span class="toctext">Headline</span></a></li>
+</ul>
+</div>
+
+<h2><span class="mw-headline" id="Headline_2">Headline 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Headline 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Headline">Headline</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Headline">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Headline_2_2">Headline 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Headline 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Headline_3">Headline</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: Headline">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+
+!! end
+
 # perl -e 'print "="x$_," Level $_ heading","="x$_,"\n" for 1..10'
 !! test
 Handling of sections up to level 6 and beyond