diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php index c213c764a..b74a0b377 100644 --- a/modules/doc/library/Doc/DocParser.php +++ b/modules/doc/library/Doc/DocParser.php @@ -4,12 +4,8 @@ namespace Icinga\Module\Doc; -require_once 'IcingaVendor/Parsedown/Parsedown.php'; - -use Parsedown; -use Icinga\Data\Tree\Node; +use SplDoublyLinkedList; use Icinga\Exception\NotReadableError; -use Icinga\Module\Doc\Exception\ChapterNotFoundException; use Icinga\Module\Doc\Exception\DocEmptyException; use Icinga\Module\Doc\Exception\DocException; @@ -44,152 +40,26 @@ class DocParser public function __construct($path) { if (! is_dir($path)) { - throw new DocException('Doc directory `' . $path . '\' does not exist'); + throw new DocException( + mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' . mt('doc', 'does not exist') + ); } if (! is_readable($path)) { - throw new NotReadableError('Doc directory `' . $path . '\' is not readable'); + throw new DocException( + mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' . mt('doc', 'is not readable') + ); } $docIterator = new DocIterator($path); if ($docIterator->count() === 0) { - throw new DocEmptyException('Doc directory `' . $path . '\' is empty'); + throw new DocEmptyException( + mt('doc', 'Documentation directory') . ' \'' . $path . '\' ' + . mt('doc', 'does not contain any non-empty Markdown file (\'.md\' suffix') + ); } $this->path = $path; $this->docIterator = $docIterator; } - /** - * Retrieve the table of contents - * - * @return Node - */ - public function getToc() - { - $tocStack = array((object) array( - 'level' => 0, - 'node' => new Node() - )); - foreach ($this->docIterator as $fileObject) { - $line = null; - $currentChapterName = null; - while (! $fileObject->eof()) { - // Save last line for setext-style headers - $lastLine = $line; - $line = $fileObject->fgets(); - $header = $this->extractHeader($line, $lastLine); - if ($header !== null) { - list($header, $level) = $header; - $id = $this->extractHeaderId($header); - $nofollow = false; - $this->reduceToc($tocStack, $level); - if ($id === null) { - $path = array(); - foreach (array_slice($tocStack, 1) as $entity) { - $path[] = $entity->node->getValue()->title; - } - $path[] = $header; - $id = implode('-', $path); - $nofollow = true; - } - $id = urlencode(str_replace('.', '.', strip_tags($id))); - if ($currentChapterName === null) { - // The first header is the chapter's name - $currentChapterName = $id; - $id = null; - } - $node = end($tocStack)->node->appendChild( - (object) array( - 'id' => $id, - 'title' => $header, - 'nofollow' => $nofollow, - 'chapterName' => $currentChapterName - ) - ); - $tocStack[] = (object) array( - 'level' => $level, - 'node' => $node - ); - } - } - } - return $tocStack[0]->node; - } - - /** - * Retrieve a chapter - * - * @param string $chapterName - * - * @return string - * @throws ChapterNotFoundException If the chapter was not found - */ - public function getChapter($chapterName) - { - $tocStack = array((object) array( - 'level' => 0, - 'node' => new Node() - )); - foreach ($this->docIterator as $fileObject) { - $line = null; - $currentChapterName = null; - $chapter = array(); - while (! $fileObject->eof()) { - // Save last line for setext-style headers - $lastLine = $line; - $line = $fileObject->fgets(); - $header = $this->extractHeader($line, $lastLine); - if ($header !== null) { - list($header, $level) = $header; - $id = $this->extractHeaderId($header); - $this->reduceToc($tocStack, $level); - if ($id === null) { - $path = array(); - foreach (array_slice($tocStack, 1) as $entity) { - $path[] = $entity->node->getValue()->title; - } - $path[] = $header; - $id = implode('-', $path); - } - $id = urlencode(str_replace('.', '.', strip_tags($id))); - if ($currentChapterName === null) { - $currentChapterName = $id; - $id = null; - } - $node = end($tocStack)->node->appendChild( - (object) array( - 'title' => $header - ) - ); - $tocStack[] = (object) array( - 'level' => $level, - 'node' => $node - ); - $line = '' . PHP_EOL . $line; - } - $chapter[] = $line; - } - if ($currentChapterName === $chapterName) { - return preg_replace_callback( - '#
(.*?)\
#s',
- array($this, 'highlight'),
- Parsedown::instance()->text(implode('', $chapter))
- );
- }
- }
- throw new ChapterNotFoundException('Chapter \'' . $chapterName . '\' not found');
- }
-
- /**
- * Syntax highlighting for PHP code
- *
- * @param $match
- *
- * @return string
- */
- protected function highlight($match)
- {
- return highlight_string(htmlspecialchars_decode($match[1]), true);
- }
-
/**
* Extract atx- or setext-style headers from the given lines
*
@@ -208,7 +78,7 @@ class DocParser
&& $line[0] === '#'
&& preg_match('/^#+/', $line, $match) === 1
) {
- // Atx-style
+ // Atx
$level = strlen($match[0]);
$header = trim(substr($line, $level));
if (! $header) {
@@ -233,36 +103,69 @@ class DocParser
if ($header === null) {
return null;
}
- return array($header, $level);
- }
-
- /**
- * Extract header id in an a or a span tag
- *
- * @param string &$header
- *
- * @return id|null
- */
- protected function extractHeaderId(&$header)
- {
if ($header[0] === '<'
- && preg_match('#(?:<(?P