mirror of
https://github.com/silverstripe/silverstripe-docsviewer
synced 2024-10-22 11:05:56 +02:00
NEW: Github style code fence blocks. (Fixes #22)
This commit is contained in:
parent
440a2cbaa5
commit
3b27cd6b2b
@ -1,24 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parser wrapping the Markdown Extra parser (see http://michelf.com/projects/php-markdown/extra/).
|
||||
* Parser wrapping the Markdown Extra parser.
|
||||
*
|
||||
* @see http://michelf.com/projects/php-markdown/extra/
|
||||
*
|
||||
* @package docsviewer
|
||||
*/
|
||||
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';
|
||||
const CODE_BLOCK_BACKTICK = 1;
|
||||
const CODE_BLOCK_COLON = 2;
|
||||
|
||||
static $heading_counts = array();
|
||||
/**
|
||||
* @var string Rewriting of api links in the format "[api:MyClass]" or "[api:MyClass::$my_property]".
|
||||
*/
|
||||
public static $api_link_base = 'http://api.silverstripe.org/search/lookup/?q=%s&version=%s&module=%s';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public 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 extensions as well so /install/ in a
|
||||
* web browser will match /install.md or /INSTALL.md
|
||||
* web browser will match /install.md or /INSTALL.md.
|
||||
*
|
||||
* Filepath: /var/www/myproject/src/cms/en/folder/subfolder/page.md
|
||||
* URL: http://myhost/mywebroot/dev/docs/2.4/cms/en/folder/subfolder/page
|
||||
@ -27,8 +35,9 @@ class DocumentationParser {
|
||||
* Pathparts: folder/subfolder/page
|
||||
*
|
||||
* @param DocumentationPage $page
|
||||
* @param String $baselink Link relative to webroot, up until the "root" of the module.
|
||||
* Necessary to rewrite relative links
|
||||
* @param String $baselink Link relative to webroot, up until the "root"
|
||||
* of the module. Necessary to rewrite relative
|
||||
* links
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@ -56,38 +65,110 @@ class DocumentationParser {
|
||||
public static function rewrite_code_blocks($md) {
|
||||
$started = false;
|
||||
$inner = false;
|
||||
$mode = false;
|
||||
$end = false;
|
||||
|
||||
$lines = explode("\n", $md);
|
||||
$output = array();
|
||||
|
||||
foreach($lines as $i => $line) {
|
||||
if(!$started && preg_match('/^\t*:::\s*(.*)/', $line, $matches)) {
|
||||
// first line with custom formatting
|
||||
$started = true;
|
||||
$lines[$i] = sprintf('<pre class="brush: %s">', $matches[1]);
|
||||
} elseif(preg_match('/^\t(.*)/', $line, $matches)) {
|
||||
// inner line of ::: block, or first line of standard markdown code block
|
||||
$mode = self::CODE_BLOCK_COLON;
|
||||
$output[$i] = sprintf('<pre class="brush: %s">', (isset($matches[1])) ? $matches[1] : "");
|
||||
}
|
||||
elseif(!$started && preg_match('/^\t*```\s*(.*)/', $line, $matches)) {
|
||||
$started = true;
|
||||
$mode = self::CODE_BLOCK_BACKTICK;
|
||||
$output[$i] = sprintf('<pre class="brush: %s">', (isset($matches[1])) ? $matches[1] : "");
|
||||
}
|
||||
elseif($started && $mode == self::CODE_BLOCK_BACKTICK) {
|
||||
// inside a backtick fenced box
|
||||
if(preg_match('/^\t*```\s*/', $line, $matches)) {
|
||||
// end of the backtick fenced box. Unset the line that contains the backticks
|
||||
$end = true;
|
||||
}
|
||||
else {
|
||||
// still inside the line.
|
||||
$output[$i] = ($started) ? '' : '<pre>' . "\n";
|
||||
$output[$i] .= htmlentities($line, ENT_COMPAT, 'UTF-8');
|
||||
$inner = true;
|
||||
}
|
||||
}
|
||||
elseif(preg_match('/^\t(.*)/', $line, $matches)) {
|
||||
// inner line of block, or first line of standard markdown code block
|
||||
// regex removes first tab (any following tabs are part of the code).
|
||||
$lines[$i] = ($started) ? '' : '<pre>' . "\n";
|
||||
$lines[$i] .= htmlentities($matches[1], ENT_COMPAT, 'UTF-8');
|
||||
$output[$i] = ($started) ? '' : '<pre>' . "\n";
|
||||
$output[$i] .= htmlentities($matches[1], ENT_COMPAT, 'UTF-8');
|
||||
$inner = true;
|
||||
$started = true;
|
||||
} elseif($started && $inner) {
|
||||
// remove any previous blank lines
|
||||
$j = $i-1;
|
||||
while(isset($lines[$j]) && preg_match('/^[\t\s]*$/', $lines[$j])) {
|
||||
unset($lines[$j]);
|
||||
$j--;
|
||||
}
|
||||
}
|
||||
elseif($started && $inner && $mode == self::CODE_BLOCK_COLON && trim($line) === "") {
|
||||
// still inside a colon based block, if the line is only whitespace
|
||||
// then continue with with it. We can continue with it for now as
|
||||
// it'll be tidied up later in the $end section.
|
||||
$inner = true;
|
||||
$output[$i] = $line;
|
||||
}
|
||||
elseif($started && $inner) {
|
||||
// line contains something other than whitespace, or tabbed. E.g
|
||||
// > code
|
||||
// > \n
|
||||
// > some message
|
||||
//
|
||||
// So actually want to reset $i to the line before this new line
|
||||
// and include this line. The edge case where this will fail is
|
||||
// new the following segment contains a code block as well as it
|
||||
// will not open.
|
||||
$end = true;
|
||||
$output[$i] = $line;
|
||||
$i = $i -1;
|
||||
}
|
||||
else {
|
||||
$output[$i] = $line;
|
||||
}
|
||||
|
||||
// last line, close pre
|
||||
$lines[$i] = '</pre>' . "\n\n" . $line;
|
||||
if($end) {
|
||||
$output = self::finalize_code_output($i, $output);
|
||||
|
||||
// reset state
|
||||
$started = $inner = false;
|
||||
$started = $inner = $mode = $end = false;
|
||||
}
|
||||
}
|
||||
|
||||
return join("\n", $lines);
|
||||
if($started) {
|
||||
$output = self::finalize_code_output($i, $output);
|
||||
}
|
||||
|
||||
return join("\n", $output);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int
|
||||
* @param array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function finalize_code_output($i, $output) {
|
||||
$j = $i;
|
||||
|
||||
while(isset($output[$j]) && trim($output[$j]) === "") {
|
||||
unset($output[$j]);
|
||||
|
||||
$j--;
|
||||
}
|
||||
|
||||
if(isset($output[$j])) {
|
||||
$output[$j] .= "</pre>\n";
|
||||
}
|
||||
|
||||
else {
|
||||
$output[$j] = "</pre>\n\n";
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
static function rewrite_image_links($md, $page) {
|
||||
|
@ -3,41 +3,36 @@
|
||||
The custom Markdown parser can render custom prefixes for code blocks, and
|
||||
render it via a [javascript syntax highlighter](http://alexgorbatchev.com/SyntaxHighlighter).
|
||||
|
||||
In:
|
||||
|
||||
:::php
|
||||
my sourcecode
|
||||
|
||||
Out:
|
||||
|
||||
<pre class="brush: php">
|
||||
my sourcecode
|
||||
</pre>
|
||||
To see examples of the syntax, check out the source of this file in docs/en/
|
||||
|
||||
To include the syntax highlighter source, add the following to your `DocumentationViewer->init()`:
|
||||
|
||||
Requirements::javascript(THIRDPARTY_DIR .'/jquery/jquery.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shCore.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushJScript.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushPHP.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushXML.js');
|
||||
// ... any additional syntaxes you want to support
|
||||
Requirements::combine_files(
|
||||
'syntaxhighlighter.js',
|
||||
array(
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shCore.js',
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushJScript.js',
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushPHP.js',
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushXML.js'
|
||||
)
|
||||
);
|
||||
|
||||
Requirements::javascript('sapphiredocs/javascript/DocumentationViewer.js');
|
||||
```php
|
||||
|
||||
// css
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shCore.css');
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shCoreDefault.css');
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shThemeRDark.css');
|
||||
Requirements::javascript(THIRDPARTY_DIR .'/jquery/jquery.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shCore.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushJScript.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushPHP.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushXML.js');
|
||||
// ... any additional syntaxes you want to support
|
||||
Requirements::combine_files(
|
||||
'syntaxhighlighter.js',
|
||||
array(
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shCore.js',
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushJScript.js',
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushPHP.js',
|
||||
'sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushXML.js'
|
||||
)
|
||||
);
|
||||
|
||||
Requirements::javascript('sapphiredocs/javascript/DocumentationViewer.js');
|
||||
|
||||
// css
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shCore.css');
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shCoreDefault.css');
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shThemeRDark.css');
|
||||
```
|
||||
|
||||
You can overload the `DocumentationViewer` class and add a custom route through `Director::addRule()`
|
||||
if you prefer not to modify the module file.
|
||||
|
19
lang/en.yml
Normal file
19
lang/en.yml
Normal file
@ -0,0 +1,19 @@
|
||||
en:
|
||||
DocumentationSearch:
|
||||
SEARCH: Search
|
||||
SEARCHRESULTS: 'Search Results'
|
||||
DocumentationViewer:
|
||||
CHANGE: Change
|
||||
KEYWORDS: Keywords
|
||||
LANGUAGE: Language
|
||||
MODULES: Modules
|
||||
OPENSEARCHDESC: 'Search the documentation'
|
||||
OPENSEARCHNAME: 'Documentation Search'
|
||||
OPENSEARCHTAGS: documentation
|
||||
SEARCH: Search
|
||||
VERSIONS: Versions
|
||||
DocumentationViewer_home.ss:
|
||||
DOCUMENTEDMODULES: 'Documented Modules'
|
||||
NOMODULEDOCUMENTATION: 'No modules with documentation installed could be found.'
|
||||
DocumentationViewer_results.ss:
|
||||
ADVANCEDSEARCH: 'Advanced Search'
|
@ -26,21 +26,40 @@ code block
|
||||
with multiple
|
||||
lines
|
||||
and tab indent
|
||||
and escaped < brackets
|
||||
</pre>
|
||||
and escaped < brackets</pre>
|
||||
|
||||
Normal text after code block
|
||||
HTML;
|
||||
|
||||
|
||||
$this->assertContains($expected, $result, 'Custom code blocks with ::: prefix');
|
||||
|
||||
$expected = <<<HTML
|
||||
<pre>
|
||||
code block
|
||||
without formatting prefix
|
||||
</pre>
|
||||
without formatting prefix</pre>
|
||||
HTML;
|
||||
$this->assertContains($expected, $result, 'Traditional markdown code blocks');
|
||||
|
||||
$expected = <<<HTML
|
||||
<pre class="brush: ">
|
||||
Fenced code block
|
||||
</pre>
|
||||
HTML;
|
||||
$this->assertContains($expected, $result, 'Backtick code blocks');
|
||||
|
||||
$expected = <<<HTML
|
||||
<pre class="brush: php">
|
||||
Fenced box with
|
||||
|
||||
new lines in
|
||||
|
||||
between
|
||||
|
||||
content
|
||||
</pre>
|
||||
HTML;
|
||||
$this->assertContains($expected, $result, 'Backtick with newlines');
|
||||
}
|
||||
|
||||
function testImageRewrites() {
|
||||
|
@ -25,6 +25,22 @@ Normal text after code block
|
||||
code block
|
||||
without formatting prefix
|
||||
|
||||
```
|
||||
Fenced code block
|
||||
```
|
||||
|
||||
Did the fence work?
|
||||
|
||||
```php
|
||||
Fenced box with
|
||||
|
||||
new lines in
|
||||
|
||||
between
|
||||
|
||||
content
|
||||
```
|
||||
|
||||
# Heading one
|
||||
|
||||
# Heading with custom anchor {#custom-anchor}
|
||||
|
Loading…
Reference in New Issue
Block a user