diff --git a/libs/Format/HTML/ContentTypes/Markdown/TOC/Processor.php b/libs/Format/HTML/ContentTypes/Markdown/TOC/Processor.php
index 8b96b77f..f46e3857 100644
--- a/libs/Format/HTML/ContentTypes/Markdown/TOC/Processor.php
+++ b/libs/Format/HTML/ContentTypes/Markdown/TOC/Processor.php
@@ -42,6 +42,7 @@ public function processDocument(Document $document)
$headings = [];
+ $document->heading_ids = [];
$walker = $document->walker();
while ($event = $walker->next()) {
$node = $event->getNode();
@@ -55,7 +56,7 @@ public function processDocument(Document $document)
continue;
}
- $this->ensureHeadingHasId($node);
+ $this->ensureHeadingHasId($document, $node);
$headings[] = new Entry($node);
}
@@ -72,21 +73,49 @@ public function processDocument(Document $document)
}
}
+ protected function escaped($url) {
+ $url = trim($url);
+ $url = preg_replace('~[^\\pL0-9_]+~u', '-', $url);
+ $url = trim($url, "-");
+ $url = iconv("utf-8", "us-ascii//TRANSLIT", $url);
+ $url = preg_replace('~[^-a-zA-Z0-9_]+~', '', $url);
+
+ return $url;
+ }
+
+ protected function getUniqueId(Document $document, $proposed) {
+ if ($proposed == "page_") {
+ $proposed = "page_section_" . (count($document->heading_ids) + 1);
+ }
+
+ // Quick path, it's a unique ID
+ if (!in_array($proposed, $document->heading_ids)) {
+ $document->heading_ids[] = $proposed;
+ return $proposed;
+ }
+
+ $extension = 1; // Initialize the variable at one, so on the first iteration we have 2
+ do {
+ $extension++;
+ } while (in_array("$proposed-$extension", $document->heading_ids));
+
+ return "$proposed-$extension";
+ }
+
/**
* @param Heading $node
*/
- protected function ensureHeadingHasId(Heading $node)
+ protected function ensureHeadingHasId(Document $document, Heading $node)
{
- // If the node has an ID, no need to generate it
+ // If the node has an ID, no need to generate it, just check it's unique
$attributes = $node->getData('attributes', []);
if (array_key_exists('id', $attributes) && !empty($attributes['id'])) {
- // TODO :: check for uniqueness
+ $node->data['attributes']['id'] = $this->getUniqueId($document, $attributes['id']);
return;
}
// Well, seems we have to generate an ID
-
$walker = $node->walker();
$inside = [];
while ($event = $walker->next()) {
@@ -106,10 +135,7 @@ protected function ensureHeadingHasId(Heading $node)
}
}
- $text = 'page_' . urlencode(trim($text));
-
- // TODO :: check for uniqueness
- $node->data['attributes']['id'] = $text;
+ $node->data['attributes']['id'] = $this->getUniqueId($document,'page_'. $this->escaped($text));
}
/**
diff --git a/tests/Format/HTML/TableOfContentsTest.php b/tests/Format/HTML/TableOfContentsTest.php
index 5203595c..474b9521 100644
--- a/tests/Format/HTML/TableOfContentsTest.php
+++ b/tests/Format/HTML/TableOfContentsTest.php
@@ -31,14 +31,56 @@ function testTOCToken() {
function testUnicodeTOC() {
$converter = new CommonMarkConverter(['daux' => new MainConfig]);
- $source = "[TOC]\n# 基础操作";
+ $source = "[TOC]\n# 基础操作\n# 操作基础";
$expected = <<
-基础操作
+基础操作
+
+
+操作基础
+
+
+基础操作
+操作基础
+
+EXPECTED;
+
+ $this->assertEquals($expected, $converter->convertToHtml($source));
+ }
+
+ function testDuplicatedTOC() {
+ $converter = new CommonMarkConverter(['daux' => new MainConfig]);
+
+ $source = "[TOC]\n# Test\n# Test";
+ $expected = <<
+
+Test
+
+
+Test
+
+
+Test
+Test
+
+EXPECTED;
+
+ $this->assertEquals($expected, $converter->convertToHtml($source));
+ }
+
+ function testEscapedTOC() {
+ $converter = new CommonMarkConverter(['daux' => new MainConfig]);
+
+ $source = "[TOC]\n# TEST : Test";
+ $expected = <<
+
+TEST : Test
-基础操作
+TEST : Test
EXPECTED;