diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..48f1d93 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. diff --git a/Parsedown.php b/Parsedown.php index 0534a0e..9166b80 100755 --- a/Parsedown.php +++ b/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 - if ($deindented_line === '') + if ($outdented_line === '') { $block['interrupted'] = true; @@ -280,53 +287,41 @@ class Parsedown 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['lines'] = array($matches[1]); - $block['last'] = true; - - $block['lines'] = array( - preg_replace('/^[ ]{0,4}/', '', $matches[3]), - ); - } + unset($block['first']); + unset($block['interrupted']); continue 2; } - if (isset($block['interrupted'])) + if ( ! isset($block['interrupted'])) { - if ($line[0] === ' ') - { - $block['lines'] []= ''; - - $line = preg_replace('/^[ ]{0,4}/', '', $line); - - $block['lines'] []= $line; - - unset($block['interrupted']); - - continue 2; - } - } - else - { - $line = preg_replace('/^[ ]{0,4}/', '', $line); + $line = preg_replace('/^[ ]{0,'.$block['baseline'].'}/', '', $line); $block['lines'] []= $line; continue 2; } + elseif ($line[0] === ' ') + { + $block['lines'] []= ''; + + $line = preg_replace('/^[ ]{0,'.$block['baseline'].'}/', '', $line); + + $block['lines'] []= $line; + + unset($block['interrupted']); + + continue 2; + } break; } @@ -339,7 +334,7 @@ class Parsedown # code - if (isset($line[3]) and $line[3] === ' ' and $line[2] === ' ' and $line[1] === ' ') + if ($indentation >= 4) { $code_line = substr($line, 4); @@ -428,15 +423,15 @@ class Parsedown # indentation insensitive types - switch ($deindented_line[0]) + switch ($outdented_line[0]) { case '<': - $position = strpos($deindented_line, '>'); + $position = strpos($outdented_line, '>'); if ($position > 1) { - $substring = substr($deindented_line, 1, $position - 1); + $substring = substr($outdented_line, 1, $position - 1); $substring = chop($substring); @@ -474,7 +469,7 @@ class Parsedown { $block = array( 'type' => 'self-closing tag', - 'text' => $deindented_line, + 'text' => $outdented_line, ); unset($is_self_closing); @@ -484,13 +479,13 @@ class Parsedown $block = array( 'type' => 'markup', - 'text' => $deindented_line, + 'text' => $outdented_line, 'start' => '<'.$name.'>', 'end' => '', 'depth' => 0, ); - if (strpos($deindented_line, $block['end'])) + if (strpos($outdented_line, $block['end'])) { $block['closed'] = true; } @@ -504,7 +499,7 @@ class Parsedown # quote - if (preg_match('/^>[ ]?(.*)/', $deindented_line, $matches)) + if (preg_match('/^>[ ]?(.*)/', $outdented_line, $matches)) { $blocks []= $block; @@ -524,7 +519,7 @@ class Parsedown # reference - if (preg_match('/^\[(.+?)\]:[ ]*(.+?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*$/', $deindented_line, $matches)) + if (preg_match('/^\[(.+?)\]:[ ]*(.+?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*$/', $outdented_line, $matches)) { $label = strtolower($matches[1]); @@ -547,7 +542,7 @@ class Parsedown # fenced code block - if (preg_match('/^([`]{3,}|[~]{3,})[ ]*(\S+)?[ ]*$/', $deindented_line, $matches)) + if (preg_match('/^([`]{3,}|[~]{3,})[ ]*(\S+)?[ ]*$/', $outdented_line, $matches)) { $blocks []= $block; @@ -574,7 +569,7 @@ class Parsedown # hr - if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $deindented_line)) + if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $outdented_line)) { $blocks []= $block; @@ -587,42 +582,49 @@ class Parsedown # li - if (preg_match('/^([ ]*)[*+-][ ](.*)/', $line, $matches)) + if (preg_match('/^([*+-][ ]+)(.*)/', $outdented_line, $matches)) { $blocks []= $block; + $baseline = $indentation + strlen($matches[1]); + $block = array( 'type' => 'li', - 'ordered' => false, - 'indentation' => $matches[1], + 'indentation' => $indentation, + 'baseline' => $baseline, + 'marker' => '[*+-]', 'first' => true, 'last' => true, - 'lines' => array( - preg_replace('/^[ ]{0,4}/', '', $matches[2]), - ), + 'lines' => array(), ); + $block['lines'] []= preg_replace('/^[ ]{0,4}/', '', $matches[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; + $baseline = $indentation + strlen($matches[1]); + $block = array( 'type' => 'li', - 'ordered' => true, - 'indentation' => $matches[1], + 'indentation' => $indentation, + 'baseline' => $baseline, + 'marker' => '\d+[.]', 'first' => true, 'last' => true, - 'lines' => array( - preg_replace('/^[ ]{0,4}/', '', $matches[2]), - ), + 'ordered' => true, + 'lines' => array(), ); + $block['lines'] []= preg_replace('/^[ ]{0,4}/', '', $matches[2]); + continue; } @@ -753,7 +755,7 @@ class Parsedown if (isset($block['first'])) { - $type = $block['ordered'] ? 'ol' : 'ul'; + $type = isset($block['ordered']) ? 'ol' : 'ul'; $markup .= '<'.$type.'>'."\n"; } @@ -769,7 +771,7 @@ class Parsedown if (isset($block['last'])) { - $type = $block['ordered'] ? 'ol' : 'ul'; + $type = isset($block['ordered']) ? 'ol' : 'ul'; $markup .= ''."\n"; } @@ -990,34 +992,18 @@ class Parsedown 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); $markup .= ''.$matches[1].''; } elseif (preg_match(self::$em_regex[$closest_marker], $text, $matches)) { + $markers[] = $closest_marker; $matches[1] = $this->parse_span_elements($matches[1], $markers); $markup .= ''.$matches[1].''; } - 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 .= ''.$matches[1].''.$matches[2].''.$matches[3].''; - } - 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 .= ''.$matches[1].''.$matches[2].''.$matches[3].''; - } if (isset($matches) and $matches) { @@ -1113,7 +1099,7 @@ class Parsedown 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 = str_replace('&', '&', $element_url); @@ -1179,23 +1165,13 @@ class Parsedown # Read-only private static $strong_regex = array( - '*' => '/^[*]{2}([^*]+?)[*]{2}(?![*])/s', - '_' => '/^__([^_]+?)__(?!_)/s', + '*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us', ); private static $em_regex = array( - '*' => '/^[*]([^*]+?)[*](?![*])/s', - '_' => '/^_([^_]+?)[_](?![_])\b/s', - ); - - private static $strong_em_regex = array( - '*' => '/^[*]{2}(.*?)[*](.+?)[*](.*?)[*]{2}/s', - '_' => '/^__(.*?)_(.+?)_(.*?)__/s', - ); - - private static $em_strong_regex = array( - '*' => '/^[*](.*?)[*]{2}(.+?)[*]{2}(.*?)[*]/s', - '_' => '/^_(.*?)__(.+?)__(.*?)_/s', + '*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us', ); private static $special_characters = array( @@ -1213,4 +1189,4 @@ class Parsedown 'span', 'time', ); -} \ No newline at end of file +} diff --git a/tests/data/code_span.html b/tests/data/code_span.html index 40674be..67b28b1 100644 --- a/tests/data/code_span.html +++ b/tests/data/code_span.html @@ -1 +1,3 @@ -

a code span

\ No newline at end of file +

a code span

+

this is also a codespan trailing text

+

and look at this one!

\ No newline at end of file diff --git a/tests/data/code_span.md b/tests/data/code_span.md index dfd136b..3b850fd 100644 --- a/tests/data/code_span.md +++ b/tests/data/code_span.md @@ -1 +1,5 @@ -a `code span` \ No newline at end of file +a `code span` + +`this is also a codespan` trailing text + +`and look at this one!` \ No newline at end of file diff --git a/tests/data/deeply_nested_list.html b/tests/data/deeply_nested_list.html new file mode 100644 index 0000000..0163d65 --- /dev/null +++ b/tests/data/deeply_nested_list.html @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/tests/data/deeply_nested_list.md b/tests/data/deeply_nested_list.md new file mode 100644 index 0000000..a77e214 --- /dev/null +++ b/tests/data/deeply_nested_list.md @@ -0,0 +1,7 @@ +- li + - li + - li + - li + - li + - li +- li \ No newline at end of file diff --git a/tests/data/sparse_dense_list.html b/tests/data/sparse_dense_list.html new file mode 100644 index 0000000..095bc73 --- /dev/null +++ b/tests/data/sparse_dense_list.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/tests/data/sparse_dense_list.md b/tests/data/sparse_dense_list.md new file mode 100644 index 0000000..5768422 --- /dev/null +++ b/tests/data/sparse_dense_list.md @@ -0,0 +1,4 @@ +- li + +- li +- li \ No newline at end of file