mirror of
https://github.com/erusev/parsedown.git
synced 2023-08-10 21:13:06 +03:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
95e9878fb0 | |||
611aed179d | |||
abb88d59fa | |||
14ab6d46fe | |||
ebfdace4c6 | |||
ba7f377290 | |||
548a6f7945 | |||
7a4d3c0f18 | |||
7f68a3a2e1 | |||
7193e634b2 | |||
45c01d4673 | |||
59907ff757 | |||
6e93b68692 | |||
5a525be070 |
@ -1,15 +1,20 @@
|
||||
Pull Requests
|
||||
-------------
|
||||
|
||||
Do create pull requests that:
|
||||
|
||||
* resolve an issue
|
||||
* optimise an existing feature or text
|
||||
* improve an existing feature or text
|
||||
|
||||
Do NOT create pull requests that:
|
||||
Do not create pull requests that:
|
||||
|
||||
* introduce a feature or text
|
||||
* change the currently used coding style
|
||||
* change the interface
|
||||
* change the coding style
|
||||
|
||||
Pull requests should do only ONE thing. If a pull request contains unrelated updates, they should be submitted as separate pull requests.
|
||||
If a pull request contains unrelated changes, they should be submitted as separate pull requests.
|
||||
|
||||
By contributing to the project, you grant to the creator of the project a perpetual, worldwide, no-charge, irrevocable license to use, reproduce and distribute your contributions and derivative works.
|
||||
License
|
||||
-------
|
||||
|
||||
You also warrant that you are the sole owner of your contributions and that they are your original works of authorship.
|
||||
By contributing to the project, you grant the creator of the project a perpetual, worldwide, 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.
|
||||
|
138
Parsedown.php
138
Parsedown.php
@ -164,11 +164,11 @@ class Parsedown
|
||||
$indentation++;
|
||||
}
|
||||
|
||||
$deindented_line = $indentation > 0 ? ltrim($line) : $line;
|
||||
$outdented_line = $indentation > 0 ? ltrim($line) : $line;
|
||||
|
||||
# blank
|
||||
|
||||
if ($deindented_line === '')
|
||||
if ($outdented_line === '')
|
||||
{
|
||||
$block['interrupted'] = true;
|
||||
|
||||
@ -194,7 +194,7 @@ class Parsedown
|
||||
|
||||
case 'li':
|
||||
|
||||
if ($block['indentation'] === $indentation and preg_match('/^'.$block['marker'].'[ ]+(.*)/', $deindented_line, $matches))
|
||||
if ($block['indentation'] === $indentation and preg_match('/^'.$block['marker'].'[ ]+(.*)/', $outdented_line, $matches))
|
||||
{
|
||||
unset($block['last']);
|
||||
|
||||
@ -330,15 +330,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);
|
||||
|
||||
@ -376,7 +376,7 @@ class Parsedown
|
||||
{
|
||||
$block = array(
|
||||
'type' => 'self-closing tag',
|
||||
'text' => $deindented_line,
|
||||
'text' => $outdented_line,
|
||||
);
|
||||
|
||||
unset($is_self_closing);
|
||||
@ -386,13 +386,13 @@ class Parsedown
|
||||
|
||||
$block = array(
|
||||
'type' => 'markup',
|
||||
'text' => $deindented_line,
|
||||
'text' => $outdented_line,
|
||||
'start' => '<'.$name.'>',
|
||||
'end' => '</'.$name.'>',
|
||||
'depth' => 0,
|
||||
);
|
||||
|
||||
if (strpos($deindented_line, $block['end']))
|
||||
if (strpos($outdented_line, $block['end']))
|
||||
{
|
||||
$block['closed'] = true;
|
||||
}
|
||||
@ -406,7 +406,7 @@ class Parsedown
|
||||
|
||||
# quote
|
||||
|
||||
if (preg_match('/^>[ ]?(.*)/', $deindented_line, $matches))
|
||||
if (preg_match('/^>[ ]?(.*)/', $outdented_line, $matches))
|
||||
{
|
||||
$blocks []= $block;
|
||||
|
||||
@ -426,19 +426,73 @@ class Parsedown
|
||||
|
||||
# reference
|
||||
|
||||
if (preg_match('/^\[(.+?)\]:[ ]*(.+?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*$/', $deindented_line, $matches))
|
||||
$position = strpos($outdented_line, ']:');
|
||||
|
||||
if ($position)
|
||||
{
|
||||
$label = strtolower($matches[1]);
|
||||
$reference = array();
|
||||
|
||||
$this->reference_map[$label] = array(
|
||||
'»' => trim($matches[2], '<>'),
|
||||
);
|
||||
$label = substr($outdented_line, 1, $position - 1);
|
||||
$label = strtolower($label);
|
||||
|
||||
if (isset($matches[3]))
|
||||
$substring = substr($outdented_line, $position + 2);
|
||||
$substring = trim($substring);
|
||||
|
||||
if ($substring === '')
|
||||
{
|
||||
$this->reference_map[$label]['#'] = $matches[3];
|
||||
break;
|
||||
}
|
||||
|
||||
if ($substring[0] === '<')
|
||||
{
|
||||
$position = strpos($substring, '>');
|
||||
|
||||
if ($position === false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
$reference['»'] = substr($substring, 1, $position - 1);
|
||||
|
||||
$substring = substr($substring, $position + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
$position = strpos($substring, ' ');
|
||||
|
||||
if ($position === false)
|
||||
{
|
||||
$reference['»'] = $substring;
|
||||
|
||||
$substring = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$reference['»'] = substr($substring, 0, $position);
|
||||
|
||||
$substring = substr($substring, $position + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($substring !== false)
|
||||
{
|
||||
if ($substring[0] !== '"' and $substring[0] !== "'" and $substring[0] !== '(')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
$last_char = substr($substring, -1);
|
||||
|
||||
if ($last_char !== '"' and $last_char !== "'" and $last_char !== ')')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
$reference['#'] = substr($substring, 1, -1);
|
||||
}
|
||||
|
||||
$this->reference_map[$label] = $reference;
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
@ -449,7 +503,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;
|
||||
|
||||
@ -476,7 +530,7 @@ class Parsedown
|
||||
|
||||
# hr
|
||||
|
||||
if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $deindented_line))
|
||||
if (preg_match('/^([-*_])([ ]{0,2}\1){2,}[ ]*$/', $outdented_line))
|
||||
{
|
||||
$blocks []= $block;
|
||||
|
||||
@ -489,7 +543,7 @@ class Parsedown
|
||||
|
||||
# li
|
||||
|
||||
if (preg_match('/^([*+-][ ]+)(.*)/', $deindented_line, $matches))
|
||||
if (preg_match('/^([*+-][ ]+)(.*)/', $outdented_line, $matches))
|
||||
{
|
||||
$blocks []= $block;
|
||||
|
||||
@ -513,7 +567,7 @@ class Parsedown
|
||||
|
||||
# li
|
||||
|
||||
if ($deindented_line[0] <= '9' and preg_match('/^(\d+[.][ ]+)(.*)/', $deindented_line, $matches))
|
||||
if ($outdented_line[0] <= '9' and preg_match('/^(\d+[.][ ]+)(.*)/', $outdented_line, $matches))
|
||||
{
|
||||
$blocks []= $block;
|
||||
|
||||
@ -887,34 +941,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 .= '<strong>'.$matches[1].'</strong>';
|
||||
}
|
||||
elseif (preg_match(self::$em_regex[$closest_marker], $text, $matches))
|
||||
{
|
||||
$markers[] = $closest_marker;
|
||||
$matches[1] = $this->parse_span_elements($matches[1], $markers);
|
||||
|
||||
$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)
|
||||
{
|
||||
@ -990,7 +1028,7 @@ class Parsedown
|
||||
|
||||
case '`':
|
||||
|
||||
if (preg_match('/^(`+)(.+?)\1(?!`)/', $text, $matches))
|
||||
if (preg_match('/^(`+)[ ]*(.+?)[ ]*\1(?!`)/', $text, $matches))
|
||||
{
|
||||
$element_text = $matches[2];
|
||||
$element_text = htmlspecialchars($element_text, ENT_NOQUOTES, 'UTF-8');
|
||||
@ -1010,7 +1048,7 @@ class Parsedown
|
||||
|
||||
case 'http':
|
||||
|
||||
if (preg_match('/^https?:[\/]{2}[^\s]+\b/ui', $text, $matches))
|
||||
if (preg_match('/^https?:[\/]{2}[^\s]+\b\/*/ui', $text, $matches))
|
||||
{
|
||||
$element_url = $matches[0];
|
||||
$element_url = str_replace('&', '&', $element_url);
|
||||
@ -1070,23 +1108,13 @@ class Parsedown
|
||||
# Read-only
|
||||
|
||||
private static $strong_regex = array(
|
||||
'*' => '/^[*]{2}([^*]+?)[*]{2}(?![*])/s',
|
||||
'_' => '/^__([^_]+?)__(?!_)/us',
|
||||
'*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
||||
'_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us',
|
||||
);
|
||||
|
||||
private static $em_regex = array(
|
||||
'*' => '/^[*]([^*]+?)[*](?![*])/s',
|
||||
'_' => '/^_([^_]+?)[_](?![_])\b/us',
|
||||
);
|
||||
|
||||
private static $strong_em_regex = array(
|
||||
'*' => '/^[*]{2}(.*?)[*](.+?)[*](.*?)[*]{2}/s',
|
||||
'_' => '/^__(.*?)_(.+?)_(.*?)__/us',
|
||||
);
|
||||
|
||||
private static $em_strong_regex = array(
|
||||
'*' => '/^[*](.*?)[*]{2}(.+?)[*]{2}(.*?)[*]/s',
|
||||
'_' => '/^_(.*?)__(.+?)__(.*?)_/us',
|
||||
'*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
||||
'_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
||||
);
|
||||
|
||||
private static $special_characters = array(
|
||||
|
@ -9,8 +9,14 @@ class Test extends PHPUnit_Framework_TestCase
|
||||
/**
|
||||
* @dataProvider provider
|
||||
*/
|
||||
function test_($markdown, $expected_markup)
|
||||
function test_($filename)
|
||||
{
|
||||
$path = $this->get_data_path();
|
||||
$markdown = file_get_contents($path . $filename . '.md');
|
||||
$expected_markup = file_get_contents($path . $filename . '.html');
|
||||
$expected_markup = str_replace("\r\n", "\n", $expected_markup);
|
||||
$expected_markup = str_replace("\r", "\n", $expected_markup);
|
||||
|
||||
$actual_markup = Parsedown::instance()->parse($markdown);
|
||||
|
||||
$this->assertEquals($expected_markup, $actual_markup);
|
||||
@ -20,9 +26,8 @@ class Test extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
$provider = array();
|
||||
|
||||
$path = dirname(__FILE__).'/';
|
||||
|
||||
$DirectoryIterator = new DirectoryIterator($path . '/' . self::provider_dir);
|
||||
$path = $this->get_data_path();
|
||||
$DirectoryIterator = new DirectoryIterator($path);
|
||||
|
||||
foreach ($DirectoryIterator as $Item)
|
||||
{
|
||||
@ -36,20 +41,17 @@ class Test extends PHPUnit_Framework_TestCase
|
||||
continue;
|
||||
|
||||
$basename = $Item->getBasename('.md');
|
||||
|
||||
$markdown = file_get_contents($path . '/' . self::provider_dir . $basename . '.md');
|
||||
|
||||
if (!$markdown)
|
||||
continue;
|
||||
|
||||
$expected_markup = file_get_contents($path . '/' . self::provider_dir . $basename . '.html');
|
||||
$expected_markup = str_replace("\r\n", "\n", $expected_markup);
|
||||
$expected_markup = str_replace("\r", "\n", $expected_markup);
|
||||
|
||||
$provider [] = array($markdown, $expected_markup);
|
||||
if (file_exists($path.$basename.'.html')) {
|
||||
$provider [] = array($basename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
function get_data_path()
|
||||
{
|
||||
return dirname(__FILE__).'/'.self::provider_dir;
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
<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>
|
||||
<p><code>and look at this one!</code></p>
|
||||
<p>single backtick in a code span: <code>`</code></p>
|
||||
<p>backtick-delimited string in a code span: <code>`foo`</code></p>
|
@ -2,4 +2,8 @@ a `code span`
|
||||
|
||||
`this is also a codespan` trailing text
|
||||
|
||||
`and look at this one!`
|
||||
`and look at this one!`
|
||||
|
||||
single backtick in a code span: `` ` ``
|
||||
|
||||
backtick-delimited string in a code span: `` `foo` ``
|
1
tests/data/inline_link_title.html
Normal file
1
tests/data/inline_link_title.html
Normal file
@ -0,0 +1 @@
|
||||
<p><a href="http://example.com" title="Title">single quotes</a> and <a href="http://example.com" title="Title">double quotes</a></p>
|
1
tests/data/inline_link_title.md
Normal file
1
tests/data/inline_link_title.md
Normal file
@ -0,0 +1 @@
|
||||
[single quotes](http://example.com 'Title') and [double quotes](http://example.com "Title")
|
@ -1 +1,2 @@
|
||||
<p><a href="http://example.com" title="Title">single quotes</a> and <a href="http://example.com" title="Title">double quotes</a></p>
|
||||
<p><a href="http://example.com" title="example title">double quotes</a> and <a href="http://example.com" title="example title">single quotes</a> and <a href="http://example.com" title="example title">parentheses</a></p>
|
||||
<p>[invalid title]: <a href="http://example.com">http://example.com</a> example title</p>
|
@ -1 +1,6 @@
|
||||
[single quotes](http://example.com 'Title') and [double quotes](http://example.com "Title")
|
||||
[double quotes] and [single quotes] and [parentheses]
|
||||
|
||||
[double quotes]: http://example.com "example title"
|
||||
[single quotes]: http://example.com 'example title'
|
||||
[parentheses]: http://example.com (example title)
|
||||
[invalid title]: http://example.com example title
|
@ -1,2 +1,3 @@
|
||||
<p>an autolink <a href="http://example.com">http://example.com</a></p>
|
||||
<p>inside of brackets [<a href="http://example.com">http://example.com</a>], inside of braces {<a href="http://example.com">http://example.com</a>}, inside of parentheses (<a href="http://example.com">http://example.com</a>)</p>
|
||||
<p>inside of brackets [<a href="http://example.com">http://example.com</a>], inside of braces {<a href="http://example.com">http://example.com</a>}, inside of parentheses (<a href="http://example.com">http://example.com</a>)</p>
|
||||
<p>trailing slash <a href="http://example.com/">http://example.com/</a> and <a href="http://example.com/path/">http://example.com/path/</a></p>
|
@ -1,3 +1,5 @@
|
||||
an autolink http://example.com
|
||||
|
||||
inside of brackets [http://example.com], inside of braces {http://example.com}, inside of parentheses (http://example.com)
|
||||
inside of brackets [http://example.com], inside of braces {http://example.com}, inside of parentheses (http://example.com)
|
||||
|
||||
trailing slash http://example.com/ and http://example.com/path/
|
Reference in New Issue
Block a user