mirror of
https://github.com/erusev/parsedown.git
synced 2024-09-20 03:51:29 +02:00
Merge branch 'master' into better-extendability
* master: simplify em/strong routine outdented is shorter and probably more accurate improve contributing guidelines improve consistency of list item add contributing guidelines dense list items that follow sparse ones should not be rendered as sparse ones improve parsing of list item and code block by measuring line indentation Remove one unnecessary /u flag. Remove /u flag from '*' chars. Add /u to urls. some edge case tests for the code tag Add unicode support for strong/em regex.
This commit is contained in:
commit
6ff1ccf561
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Pull Requests
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Do create pull requests that:
|
||||||
|
|
||||||
|
* resolve an issue
|
||||||
|
* improve an existing feature or text
|
||||||
|
|
||||||
|
Do not create pull requests that:
|
||||||
|
|
||||||
|
* introduce a feature or text
|
||||||
|
* change the currently used coding style
|
||||||
|
|
||||||
|
If a pull request contains unrelated updates, they should be submitted as separate pull requests.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
By contributing to the project, you grant the creator of the project a perpetual, worldwide, no-charge, irrevocable license to use, reproduce and distribute your contributions and derivative works. You also warrant that you are the sole owner of your contributions and that they are your original works of authorship.
|
158
Parsedown.php
158
Parsedown.php
@ -250,11 +250,18 @@ class Parsedown
|
|||||||
|
|
||||||
# ~
|
# ~
|
||||||
|
|
||||||
$deindented_line = ltrim($line);
|
$indentation = 0;
|
||||||
|
|
||||||
|
while(isset($line[$indentation]) and $line[$indentation] === ' ')
|
||||||
|
{
|
||||||
|
$indentation++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$outdented_line = $indentation > 0 ? ltrim($line) : $line;
|
||||||
|
|
||||||
# blank
|
# blank
|
||||||
|
|
||||||
if ($deindented_line === '')
|
if ($outdented_line === '')
|
||||||
{
|
{
|
||||||
$block['interrupted'] = true;
|
$block['interrupted'] = true;
|
||||||
|
|
||||||
@ -280,53 +287,41 @@ class Parsedown
|
|||||||
|
|
||||||
case 'li':
|
case 'li':
|
||||||
|
|
||||||
if (preg_match('/^([ ]{0,3})(\d+[.]|[*+-])[ ](.*)/', $line, $matches))
|
if ($block['indentation'] === $indentation and preg_match('/^'.$block['marker'].'[ ]+(.*)/', $outdented_line, $matches))
|
||||||
{
|
|
||||||
if ($block['indentation'] !== $matches[1])
|
|
||||||
{
|
|
||||||
$block['lines'] []= $line;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
unset($block['last']);
|
unset($block['last']);
|
||||||
|
|
||||||
$blocks []= $block;
|
$blocks []= $block;
|
||||||
|
|
||||||
unset($block['first']);
|
|
||||||
|
|
||||||
$block['last'] = true;
|
$block['last'] = true;
|
||||||
|
$block['lines'] = array($matches[1]);
|
||||||
|
|
||||||
$block['lines'] = array(
|
unset($block['first']);
|
||||||
preg_replace('/^[ ]{0,4}/', '', $matches[3]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($block['interrupted']))
|
|
||||||
{
|
|
||||||
if ($line[0] === ' ')
|
|
||||||
{
|
|
||||||
$block['lines'] []= '';
|
|
||||||
|
|
||||||
$line = preg_replace('/^[ ]{0,4}/', '', $line);
|
|
||||||
|
|
||||||
$block['lines'] []= $line;
|
|
||||||
|
|
||||||
unset($block['interrupted']);
|
unset($block['interrupted']);
|
||||||
|
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if ( ! isset($block['interrupted']))
|
||||||
{
|
{
|
||||||
$line = preg_replace('/^[ ]{0,4}/', '', $line);
|
$line = preg_replace('/^[ ]{0,'.$block['baseline'].'}/', '', $line);
|
||||||
|
|
||||||
$block['lines'] []= $line;
|
$block['lines'] []= $line;
|
||||||
|
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
|
elseif ($line[0] === ' ')
|
||||||
|
{
|
||||||
|
$block['lines'] []= '';
|
||||||
|
|
||||||
|
$line = preg_replace('/^[ ]{0,'.$block['baseline'].'}/', '', $line);
|
||||||
|
|
||||||
|
$block['lines'] []= $line;
|
||||||
|
|
||||||
|
unset($block['interrupted']);
|
||||||
|
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -339,7 +334,7 @@ class Parsedown
|
|||||||
|
|
||||||
# code
|
# code
|
||||||
|
|
||||||
if (isset($line[3]) and $line[3] === ' ' and $line[2] === ' ' and $line[1] === ' ')
|
if ($indentation >= 4)
|
||||||
{
|
{
|
||||||
$code_line = substr($line, 4);
|
$code_line = substr($line, 4);
|
||||||
|
|
||||||
@ -428,15 +423,15 @@ class Parsedown
|
|||||||
|
|
||||||
# indentation insensitive types
|
# indentation insensitive types
|
||||||
|
|
||||||
switch ($deindented_line[0])
|
switch ($outdented_line[0])
|
||||||
{
|
{
|
||||||
case '<':
|
case '<':
|
||||||
|
|
||||||
$position = strpos($deindented_line, '>');
|
$position = strpos($outdented_line, '>');
|
||||||
|
|
||||||
if ($position > 1)
|
if ($position > 1)
|
||||||
{
|
{
|
||||||
$substring = substr($deindented_line, 1, $position - 1);
|
$substring = substr($outdented_line, 1, $position - 1);
|
||||||
|
|
||||||
$substring = chop($substring);
|
$substring = chop($substring);
|
||||||
|
|
||||||
@ -474,7 +469,7 @@ class Parsedown
|
|||||||
{
|
{
|
||||||
$block = array(
|
$block = array(
|
||||||
'type' => 'self-closing tag',
|
'type' => 'self-closing tag',
|
||||||
'text' => $deindented_line,
|
'text' => $outdented_line,
|
||||||
);
|
);
|
||||||
|
|
||||||
unset($is_self_closing);
|
unset($is_self_closing);
|
||||||
@ -484,13 +479,13 @@ class Parsedown
|
|||||||
|
|
||||||
$block = array(
|
$block = array(
|
||||||
'type' => 'markup',
|
'type' => 'markup',
|
||||||
'text' => $deindented_line,
|
'text' => $outdented_line,
|
||||||
'start' => '<'.$name.'>',
|
'start' => '<'.$name.'>',
|
||||||
'end' => '</'.$name.'>',
|
'end' => '</'.$name.'>',
|
||||||
'depth' => 0,
|
'depth' => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (strpos($deindented_line, $block['end']))
|
if (strpos($outdented_line, $block['end']))
|
||||||
{
|
{
|
||||||
$block['closed'] = true;
|
$block['closed'] = true;
|
||||||
}
|
}
|
||||||
@ -504,7 +499,7 @@ class Parsedown
|
|||||||
|
|
||||||
# quote
|
# quote
|
||||||
|
|
||||||
if (preg_match('/^>[ ]?(.*)/', $deindented_line, $matches))
|
if (preg_match('/^>[ ]?(.*)/', $outdented_line, $matches))
|
||||||
{
|
{
|
||||||
$blocks []= $block;
|
$blocks []= $block;
|
||||||
|
|
||||||
@ -524,7 +519,7 @@ class Parsedown
|
|||||||
|
|
||||||
# reference
|
# reference
|
||||||
|
|
||||||
if (preg_match('/^\[(.+?)\]:[ ]*(.+?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*$/', $deindented_line, $matches))
|
if (preg_match('/^\[(.+?)\]:[ ]*(.+?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*$/', $outdented_line, $matches))
|
||||||
{
|
{
|
||||||
$label = strtolower($matches[1]);
|
$label = strtolower($matches[1]);
|
||||||
|
|
||||||
@ -547,7 +542,7 @@ class Parsedown
|
|||||||
|
|
||||||
# fenced code block
|
# fenced code block
|
||||||
|
|
||||||
if (preg_match('/^([`]{3,}|[~]{3,})[ ]*(\S+)?[ ]*$/', $deindented_line, $matches))
|
if (preg_match('/^([`]{3,}|[~]{3,})[ ]*(\S+)?[ ]*$/', $outdented_line, $matches))
|
||||||
{
|
{
|
||||||
$blocks []= $block;
|
$blocks []= $block;
|
||||||
|
|
||||||
@ -574,7 +569,7 @@ class Parsedown
|
|||||||
|
|
||||||
# hr
|
# hr
|
||||||
|
|
||||||
if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $deindented_line))
|
if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $outdented_line))
|
||||||
{
|
{
|
||||||
$blocks []= $block;
|
$blocks []= $block;
|
||||||
|
|
||||||
@ -587,42 +582,49 @@ class Parsedown
|
|||||||
|
|
||||||
# li
|
# li
|
||||||
|
|
||||||
if (preg_match('/^([ ]*)[*+-][ ](.*)/', $line, $matches))
|
if (preg_match('/^([*+-][ ]+)(.*)/', $outdented_line, $matches))
|
||||||
{
|
{
|
||||||
$blocks []= $block;
|
$blocks []= $block;
|
||||||
|
|
||||||
|
$baseline = $indentation + strlen($matches[1]);
|
||||||
|
|
||||||
$block = array(
|
$block = array(
|
||||||
'type' => 'li',
|
'type' => 'li',
|
||||||
'ordered' => false,
|
'indentation' => $indentation,
|
||||||
'indentation' => $matches[1],
|
'baseline' => $baseline,
|
||||||
|
'marker' => '[*+-]',
|
||||||
'first' => true,
|
'first' => true,
|
||||||
'last' => true,
|
'last' => true,
|
||||||
'lines' => array(
|
'lines' => array(),
|
||||||
preg_replace('/^[ ]{0,4}/', '', $matches[2]),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$block['lines'] []= preg_replace('/^[ ]{0,4}/', '', $matches[2]);
|
||||||
|
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# list item
|
# li
|
||||||
|
|
||||||
if ($deindented_line[0] <= '9' and $deindented_line[0] >= '0' and preg_match('/^([ ]*)\d+[.][ ](.*)/', $line, $matches))
|
if ($outdented_line[0] <= '9' and preg_match('/^(\d+[.][ ]+)(.*)/', $outdented_line, $matches))
|
||||||
{
|
{
|
||||||
$blocks []= $block;
|
$blocks []= $block;
|
||||||
|
|
||||||
|
$baseline = $indentation + strlen($matches[1]);
|
||||||
|
|
||||||
$block = array(
|
$block = array(
|
||||||
'type' => 'li',
|
'type' => 'li',
|
||||||
'ordered' => true,
|
'indentation' => $indentation,
|
||||||
'indentation' => $matches[1],
|
'baseline' => $baseline,
|
||||||
|
'marker' => '\d+[.]',
|
||||||
'first' => true,
|
'first' => true,
|
||||||
'last' => true,
|
'last' => true,
|
||||||
'lines' => array(
|
'ordered' => true,
|
||||||
preg_replace('/^[ ]{0,4}/', '', $matches[2]),
|
'lines' => array(),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$block['lines'] []= preg_replace('/^[ ]{0,4}/', '', $matches[2]);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,7 +755,7 @@ class Parsedown
|
|||||||
|
|
||||||
if (isset($block['first']))
|
if (isset($block['first']))
|
||||||
{
|
{
|
||||||
$type = $block['ordered'] ? 'ol' : 'ul';
|
$type = isset($block['ordered']) ? 'ol' : 'ul';
|
||||||
|
|
||||||
$markup .= '<'.$type.'>'."\n";
|
$markup .= '<'.$type.'>'."\n";
|
||||||
}
|
}
|
||||||
@ -769,7 +771,7 @@ class Parsedown
|
|||||||
|
|
||||||
if (isset($block['last']))
|
if (isset($block['last']))
|
||||||
{
|
{
|
||||||
$type = $block['ordered'] ? 'ol' : 'ul';
|
$type = isset($block['ordered']) ? 'ol' : 'ul';
|
||||||
|
|
||||||
$markup .= '</'.$type.'>'."\n";
|
$markup .= '</'.$type.'>'."\n";
|
||||||
}
|
}
|
||||||
@ -990,34 +992,18 @@ class Parsedown
|
|||||||
|
|
||||||
if ($text[1] === $closest_marker and preg_match(self::$strong_regex[$closest_marker], $text, $matches))
|
if ($text[1] === $closest_marker and preg_match(self::$strong_regex[$closest_marker], $text, $matches))
|
||||||
{
|
{
|
||||||
|
$markers[] = $closest_marker;
|
||||||
$matches[1] = $this->parse_span_elements($matches[1], $markers);
|
$matches[1] = $this->parse_span_elements($matches[1], $markers);
|
||||||
|
|
||||||
$markup .= '<strong>'.$matches[1].'</strong>';
|
$markup .= '<strong>'.$matches[1].'</strong>';
|
||||||
}
|
}
|
||||||
elseif (preg_match(self::$em_regex[$closest_marker], $text, $matches))
|
elseif (preg_match(self::$em_regex[$closest_marker], $text, $matches))
|
||||||
{
|
{
|
||||||
|
$markers[] = $closest_marker;
|
||||||
$matches[1] = $this->parse_span_elements($matches[1], $markers);
|
$matches[1] = $this->parse_span_elements($matches[1], $markers);
|
||||||
|
|
||||||
$markup .= '<em>'.$matches[1].'</em>';
|
$markup .= '<em>'.$matches[1].'</em>';
|
||||||
}
|
}
|
||||||
elseif ($text[1] === $closest_marker and preg_match(self::$strong_em_regex[$closest_marker], $text, $matches))
|
|
||||||
{
|
|
||||||
$matches[2] = $this->parse_span_elements($matches[2], $markers);
|
|
||||||
|
|
||||||
$matches[1] and $matches[1] = $this->parse_span_elements($matches[1], $markers);
|
|
||||||
$matches[3] and $matches[3] = $this->parse_span_elements($matches[3], $markers);
|
|
||||||
|
|
||||||
$markup .= '<strong>'.$matches[1].'<em>'.$matches[2].'</em>'.$matches[3].'</strong>';
|
|
||||||
}
|
|
||||||
elseif (preg_match(self::$em_strong_regex[$closest_marker], $text, $matches))
|
|
||||||
{
|
|
||||||
$matches[2] = $this->parse_span_elements($matches[2], $markers);
|
|
||||||
|
|
||||||
$matches[1] and $matches[1] = $this->parse_span_elements($matches[1], $markers);
|
|
||||||
$matches[3] and $matches[3] = $this->parse_span_elements($matches[3], $markers);
|
|
||||||
|
|
||||||
$markup .= '<em>'.$matches[1].'<strong>'.$matches[2].'</strong>'.$matches[3].'</em>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($matches) and $matches)
|
if (isset($matches) and $matches)
|
||||||
{
|
{
|
||||||
@ -1113,7 +1099,7 @@ class Parsedown
|
|||||||
|
|
||||||
case 'http':
|
case 'http':
|
||||||
|
|
||||||
if (preg_match('/^https?:[\/]{2}[^\s]+\b/i', $text, $matches))
|
if (preg_match('/^https?:[\/]{2}[^\s]+\b/ui', $text, $matches))
|
||||||
{
|
{
|
||||||
$element_url = $matches[0];
|
$element_url = $matches[0];
|
||||||
$element_url = str_replace('&', '&', $element_url);
|
$element_url = str_replace('&', '&', $element_url);
|
||||||
@ -1179,23 +1165,13 @@ class Parsedown
|
|||||||
# Read-only
|
# Read-only
|
||||||
|
|
||||||
private static $strong_regex = array(
|
private static $strong_regex = array(
|
||||||
'*' => '/^[*]{2}([^*]+?)[*]{2}(?![*])/s',
|
'*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
||||||
'_' => '/^__([^_]+?)__(?!_)/s',
|
'_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us',
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $em_regex = array(
|
private static $em_regex = array(
|
||||||
'*' => '/^[*]([^*]+?)[*](?![*])/s',
|
'*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
||||||
'_' => '/^_([^_]+?)[_](?![_])\b/s',
|
'_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
||||||
);
|
|
||||||
|
|
||||||
private static $strong_em_regex = array(
|
|
||||||
'*' => '/^[*]{2}(.*?)[*](.+?)[*](.*?)[*]{2}/s',
|
|
||||||
'_' => '/^__(.*?)_(.+?)_(.*?)__/s',
|
|
||||||
);
|
|
||||||
|
|
||||||
private static $em_strong_regex = array(
|
|
||||||
'*' => '/^[*](.*?)[*]{2}(.+?)[*]{2}(.*?)[*]/s',
|
|
||||||
'_' => '/^_(.*?)__(.+?)__(.*?)_/s',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $special_characters = array(
|
private static $special_characters = array(
|
||||||
|
@ -1 +1,3 @@
|
|||||||
<p>a <code>code span</code></p>
|
<p>a <code>code span</code></p>
|
||||||
|
<p><code>this is also a codespan</code> trailing text</p>
|
||||||
|
<p><code>and look at this one!</code></p>
|
@ -1 +1,5 @@
|
|||||||
a `code span`
|
a `code span`
|
||||||
|
|
||||||
|
`this is also a codespan` trailing text
|
||||||
|
|
||||||
|
`and look at this one!`
|
18
tests/data/deeply_nested_list.html
Normal file
18
tests/data/deeply_nested_list.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<ul>
|
||||||
|
<li>li
|
||||||
|
<ul>
|
||||||
|
<li>li
|
||||||
|
<ul>
|
||||||
|
<li>li
|
||||||
|
<ul>
|
||||||
|
<li>li</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>li</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>li</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>li</li>
|
||||||
|
</ul>
|
7
tests/data/deeply_nested_list.md
Normal file
7
tests/data/deeply_nested_list.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
- li
|
||||||
|
- li
|
||||||
|
- li
|
||||||
|
- li
|
||||||
|
- li
|
||||||
|
- li
|
||||||
|
- li
|
7
tests/data/sparse_dense_list.html
Normal file
7
tests/data/sparse_dense_list.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>li</p>
|
||||||
|
</li>
|
||||||
|
<li>li</li>
|
||||||
|
<li>li</li>
|
||||||
|
</ul>
|
4
tests/data/sparse_dense_list.md
Normal file
4
tests/data/sparse_dense_list.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
- li
|
||||||
|
|
||||||
|
- li
|
||||||
|
- li
|
Loading…
Reference in New Issue
Block a user