ENHANCEMENT: Automatic (and manual) hash link generation for headlines done in server-side

This commit is contained in:
Saophalkun Ponlu 2010-09-03 05:29:15 +00:00
parent 04e7b89b66
commit 257c833e86
4 changed files with 103 additions and 3 deletions

View File

@ -14,7 +14,9 @@ class DocumentationParser {
* @var String Rewriting of api links in the format "[api:MyClass]" or "[api:MyClass::$my_property]".
*/
static $api_link_base = 'http://api.silverstripe.org/search/lookup/?q=%s&version=%s&module=%s';
static $heading_counts = array();
/**
* Parse a given path to the documentation for a file. Performs a case insensitive
* lookup on the file system. Automatically appends the file extension to one of the markdown
@ -39,6 +41,7 @@ class DocumentationParser {
$md = self::rewrite_image_links($md, $page);
$md = self::rewrite_relative_links($md, $page, $baselink);
$md = self::rewrite_api_links($md, $page);
$md = self::rewrite_heading_anchors($md, $page);
// $md = self::rewrite_code_blocks($md, $page);
require_once('../sapphiredocs/thirdparty/markdown.php');
@ -187,6 +190,51 @@ class DocumentationParser {
return $md;
}
/**
*
*/
static function rewrite_heading_anchors($md, $page) {
$re = '/
\#+(.*)
/x';
$md = preg_replace_callback($re, array('DocumentationParser', '_rewrite_heading_anchors_callback'), $md);
return $md;
}
static function _rewrite_heading_anchors_callback($matches) {
$heading = $matches[0];
$headingText = $matches[1];
if(preg_match('/\{\#.*\}/', $headingText)) return $heading;
if(!isset(self::$heading_counts[$headingText])) {
self::$heading_counts[$headingText] = 1;
}
else {
self::$heading_counts[$headingText]++;
$headingText .= "-" . self::$heading_counts[$headingText];
}
return sprintf("%s {#%s}", preg_replace('/\n/', '', $heading), self::generate_html_id($headingText));
}
/**
* Generate an html element id from a string
*
* @return String
*/
static function generate_html_id($title) {
$t = $title;
$t = str_replace('&','-and-',$t);
$t = str_replace('&','-and-',$t);
$t = ereg_replace('[^A-Za-z0-9]+','-',$t);
$t = ereg_replace('-+','-',$t);
$t = trim($t, '-');
return $t;
}
/**
* Resolves all relative links within markdown.
*
@ -367,4 +415,5 @@ class DocumentationParser {
return $output;
}
}

View File

@ -13,7 +13,6 @@
$('#left-column h1, #left-column h2, #left-column h3, #left-column h4').each(function(i) {
var current = $(this);
current.attr('id', 'title' + i);
toc += '<li class="' + current.attr("tagName").toLowerCase() + '"><a id="link' + i + '" href="'+ window.location.href +'#title' + i + '" title="' + current.html() + '">' + current.html() + '</a></li>';
});

View File

@ -63,6 +63,44 @@ class DocumentationParserTest extends SapphireTest {
$result
);
}
function testHeadlineAnchors() {
$page = new DocumentationPage(
'test.md',
new DocumentationEntity('mymodule', '2.4', BASE_PATH . '/sapphiredocs/tests/docs/'),
'en',
'2.4'
);
$result = DocumentationParser::rewrite_heading_anchors($page->getMarkdown(), $page);
/*
# Heading one {#Heading-one}
# Heading with custom anchor {#custom-anchor} {#Heading-with-custom-anchor-custom-anchor}
## Heading two {#Heading-two}
### Heading three {#Heading-three}
## Heading duplicate {#Heading-duplicate}
## Heading duplicate {#Heading-duplicate-2}
## Heading duplicate {#Heading-duplicate-3}
*/
$this->assertContains('# Heading one {#Heading-one}', $result);
$this->assertContains('# Heading with custom anchor {#custom-anchor}', $result);
$this->assertNotContains('# Heading with custom anchor {#custom-anchor} {#Heading', $result);
$this->assertContains('# Heading two {#Heading-two}', $result);
$this->assertContains('# Heading three {#Heading-three}', $result);
$this->assertContains('## Heading duplicate {#Heading-duplicate}', $result);
$this->assertContains('## Heading duplicate {#Heading-duplicate-2}', $result);
$this->assertContains('## Heading duplicate {#Heading-duplicate-3}', $result);
}
function testRelativeLinks() {
// Page on toplevel

View File

@ -19,4 +19,18 @@ test
Normal text after code block
code block
without formatting prefix
without formatting prefix
# Heading one
# Heading with custom anchor {#custom-anchor}
## Heading two
### Heading three
## Heading duplicate
## Heading duplicate
## Heading duplicate