*/
class Markdown extends Parser
{
// Include block element parsing using traits.
use block\CodeTrait;
use block\FencedCodeTrait;
use block\HeadlineTrait;
use block\HtmlTrait;
use block\ListTrait;
use block\QuoteTrait;
use block\RuleTrait;
// Include inline element parsing using traits.
use inline\CodeTrait;
use inline\EmphStrongTrait;
use inline\LinkTrait;
/**
* @var array - These are "escapeable" characters.
*
* When using one of these prefixed with a backslash, the character is
* not interpreted as markdown and will be outputted without backslash.
*/
protected $escapeCharacters = [
'\\', // backslash
'/', // forward slash
'`', // backtick
'*', // asterisk
'_', // underscore
'{', '}', // curly braces
'[', ']', // square brackets
'(', ')', // parentheses
'#', // hash mark
'+', // plus sign
'-', // minus sign (hyphen)
'.', // dot
',', // comma
'!', // exclamation mark
'<', '>', // angle brackets
'"', // double quote
'\'', // single quote
'$', // dollar sign
'%', // percent sign
'&', // ampersand
':', // colon
';', // semicolon
'=', // equals sign
'?', // question mark
'@', // at symbol
'~', // tilde
'^', // caret
'|', // pipe
];
/**
* @inheritDoc
*/
protected $blockPriorities = [
'Hr',
'Ul',
'FencedCode',
'Code',
'Html',
'Ol',
'Quote',
'Reference',
'Headline',
];
/**
* @inheritDoc
*/
protected function prepare(): void
{
// Reset references.
$this->references = [];
// Reset anchor links.
$this->headlineAnchorLinks = [];
}
/**
* @inheritDoc
*
* Allow other block types to break paragraphs.
*/
protected function consumeParagraph($lines, $current): array
{
$content = [];
// Consume until blank line or end condition...
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
if (
$line === ''
|| ($trimmed = ltrim($line)) === ''
|| (
(ctype_punct($trimmed[0]) || ctype_digit($trimmed[0]))
&& (
$this->identifyQuote($line, $lines, $i)
|| $this->identifyFencedCode($line, $lines, $i)
|| $this->identifyUl($line, $lines, $i)
|| $this->identifyOl($line, $lines, $i)
|| $this->identifyHr($line, $lines, $i)
|| $this->identifyHtml($line, $lines, $i)
)
)
|| $this->identifyHeadline($line, $lines, $i)
) {
break;
} else {
$content[] = ltrim($line);
}
}
$block = [
'paragraph',
'content' => $this->parseInline(trim(implode("\n", $content))),
];
return [$block, --$i];
}
/**
* @inheritDoc
*
* Parses a newline indicated by a backslash on the end of a markdown line.
*
* @marker \
*/
protected function parseEscape($text): array
{
$br = $this->html5 ? "
\n" : "
\n";
if (isset($text[1]) && $text[1] === "\n") {
// Backslash followed by newline.
return [['text', $br], 2];
}
// Otherwise parse the sequence normally.
return parent::parseEscape($text);
}
/**
* @inheritDoc
*
* Parses a newline indicated by two or more spaces on the end of a markdown line.
*/
protected function renderText($text): string
{
$br = $this->html5 ? "
\n" : "
\n";
$text = $text[1];
// Two or more spaces.
$text = preg_replace("/ {2,}\n/", $br, $text);
// Trim single spaces.
$text = str_replace(" \n", "\n", $text);
return $text;
}
}