-
-
Save DarkFighter/0095077337449fdfe1ba to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Ciconia\Extension\Core; | |
use Ciconia\Common\Text; | |
use Ciconia\Event\EmitterAwareInterface; | |
use Ciconia\Event\EmitterAwareTrait; | |
use Ciconia\Extension\ExtensionInterface; | |
use Ciconia\Renderer\RendererAwareInterface; | |
use Ciconia\Renderer\RendererAwareTrait; | |
use Ciconia\Markdown; | |
/** | |
* Form HTML ordered (numbered) and unordered (bulleted) lists. | |
* | |
* Original source code from Markdown.pl | |
* | |
* > Copyright (c) 2004 John Gruber | |
* > <http://daringfireball.net/projects/markdown/> | |
* | |
* @author Kazuyuki Hayashi <hayashi@valnur.net> | |
*/ | |
class ListExtension implements ExtensionInterface, RendererAwareInterface, EmitterAwareInterface | |
{ | |
use RendererAwareTrait; | |
use EmitterAwareTrait; | |
/** | |
* @var Markdown | |
*/ | |
private $markdown; | |
/** | |
* @var string | |
*/ | |
private $ul = '[*+\-]'; | |
/** | |
* @var string | |
*/ | |
private $ol = '\d+[\.]'; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function register(Markdown $markdown) | |
{ | |
$this->markdown = $markdown; | |
$markdown->on('block', array($this, 'processList'), 30); | |
} | |
/** | |
* Form HTML ordered (numbered) and unordered (bulleted) lists. | |
* | |
* @param Text $text | |
* @param array $options | |
* @param int $level | |
*/ | |
//protected $list_level = 0; | |
public function processList(Text $text, array $options = array(), $level = 0) | |
{ | |
$options['tabWidth'] = 5; // deepest level | |
$lessThanTab = $options['tabWidth'] - 1; | |
$markers_relist = array( | |
$this->ul => $this->ol, | |
$this->ol => $this->ul, | |
); | |
$callback = function (Text $list, Text $marker, Text $numberOfSpaces, Text $firstItemMarker, Text $itemItext) use ($options, $level) { | |
$type = preg_match('/' . $this->ul . '/', $firstItemMarker) ? 'ul' : 'ol'; | |
// $list->replace('/\n{2,}/', "\n\n\n"); | |
$marker_re = ($type == 'ul' ? $this->ul : $this->ol); | |
$list->append("\n"); | |
$this->processListItems($list, $marker_re, $options, $level); | |
return "\n" . $this->getRenderer()->renderList($list, array('type' => $type)) . "\n\n"; | |
}; | |
foreach ($markers_relist as $marker_re => $other_marker_re) { | |
$whole_list_re = ' | |
#( # $1 = whole list | |
( # $2 | |
([ ]{0,' . $lessThanTab . '}) # $3 = number of spaces | |
(' . $marker_re . ') # $4 = first list item marker | |
[ ]+ | |
) | |
(?s:.+?) | |
( # $5 | |
\z | |
| | |
\n{2,} | |
(?=\S) | |
(?! # Negative lookahead for another list item marker | |
[ ]* | |
' . $marker_re . '[ ]+ | |
) | |
| | |
(?= # Lookahead for another kind of list | |
\n | |
\3 # Must have the same indentation | |
' . $other_marker_re . '[ ]+ | |
) | |
) | |
#) | |
'; // mx | |
if ($level) { | |
// $text->replace("{^$whole_list_re}mx", $callback); | |
$text->replace("{^$whole_list_re}mx", $callback); | |
} else { | |
//$text->replace('{(?:(?<=\n\n)|\A\n?)' . $whole_list_re . '}mx', $callback); | |
$text->replace('{(?:(?<=\n)\n|\A\n?)' . $whole_list_re . '}mx', $callback); | |
} | |
} | |
/* | |
$wholeList = ' | |
#( # $1 = whole list | |
( # $2 | |
[ ]{0,' . $lessThanTab . '} | |
(' . $this->getPattern() . ') # $3 = first list item marker | |
[ \t]+ | |
) | |
(?s:.+?) | |
(?: # $4 | |
\z | |
| | |
\n{2,} | |
(?=\S) | |
(?! # Negative lookahead for another list item marker | |
[ \t]* | |
' . $this->getPattern() . '[ \t]+ | |
) | |
) | |
#) | |
'; | |
*/ | |
/** @noinspection PhpUnusedParameterInspection */ | |
} | |
public function processListItems(Text $list, $marker_any_re, array $options = array(), $level = 0) { | |
//protected function processListItems($list_str, $marker_any_re, array $options = array(), $level = 0) { | |
// $list->replace('/\n{2,}\z/', "\n"); | |
$list->replace("/\n{2,}\\z/", "\n"); | |
$list->replace('{ | |
(\n)? # leading line = $1 | |
(^[ ]*) # leading whitespace = $2 | |
('.$marker_any_re.' # list marker and space = $3 | |
(?:[ ]+|(?=\n)) # space only required if item is not empty | |
) | |
((?s:.*?)) # list item text = $4 | |
(?:(\n+(?=\n))|\n) # tailing blank line = $5 | |
(?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) | |
}mx', function (Text $w, Text $leadingLine, Text $w, Text $markerSpace, Text $item, Text $tailingBlankLine) use ($marker_any_re, $options, $level) { | |
/* | |
if ((string)$leadingLine or $item->match('/\n{2,}/')) { | |
$this->markdown->emit('outdent', array($item)); | |
$this->markdown->emit('block', array($item)); | |
} else { | |
$this->markdown->emit('outdent', array($item)); | |
$this->processList($item, $options, ++$level); | |
$item->rtrim(); | |
$this->markdown->emit('inline', array($item)); | |
} | |
*/ | |
/* | |
$item = $matches[4]; | |
$leading_line =& $matches[1]; | |
$leading_space =& $matches[2]; | |
$marker_space = $matches[3]; | |
$tailing_blank_line =& $matches[5]; | |
*/ | |
if ((string)$leadingLine || (string)$tailingBlankLine || $item->match('/\n{2,}/')) { | |
$item->setString( $leadingLine->getString() . str_repeat(' ', $markerSpace->getLength()) . $item->getString() ); | |
$item->replace('/^(\t|[ ]{1,'.$options['tabWidth'].'})/m', ''); | |
$item->append("\n"); | |
// тут может возникнуть баг: в оригинале делается так: | |
// $this->markdown->emit('outdent', array($item)); | |
// $this->markdown->emit('block', array($item)); | |
// но почему?? | |
$this->processList($item, $options, ++$level); | |
$item->replace('/\n+$/', ''); | |
$this->markdown->emit('inline', array($item)); | |
// $this->markdown->emit('outdent', array($item)); | |
// $this->markdown->emit('block', array($item)); | |
} else { | |
// тут что-то не так! | |
// $item->replace('/^(\t|[ ]{1,'.$options['tabWidth'].'})/m', ''); | |
#var_dump($item); | |
#echo '<hr />'; | |
$this->processList($item, $options, ++$level); | |
$item->replace('/\n+$/', ''); | |
// $item->rtrim(); | |
$this->markdown->emit('inline', array($item)); | |
/* | |
$item = $this->doLists($this->outdent($item)); | |
$item = preg_replace('/\n+$/', '', $item); | |
$item = $this->runSpanGamut($item); | |
*/ | |
} | |
return $this->getRenderer()->renderListItem($item) . "\n"; | |
}); | |
/* | |
$list_str = preg_replace_callback('{ | |
(\n)? # leading line = $1 | |
(^[ ]*) # leading whitespace = $2 | |
('.$marker_any_re.' # list marker and space = $3 | |
(?:[ ]+|(?=\n)) # space only required if item is not empty | |
) | |
((?s:.*?)) # list item text = $4 | |
(?:(\n+(?=\n))|\n) # tailing blank line = $5 | |
(?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) | |
}xm', | |
array(&$this, '_processListItems_callback'), $list_str); | |
$this->list_level--; | |
return $list_str; | |
*/ | |
} | |
/** | |
* Process the contents of a single ordered or unordered list, splitting it into individual list items. | |
* | |
* @param Text $list | |
* @param array $options | |
* @param int $level | |
*/ | |
public function processListItemsOld(Text $list, array $options = array(), $level = 0) | |
{ | |
$list->replace('/\n{2,}\z/', "\n"); | |
/** @noinspection PhpUnusedParameterInspection */ | |
$list->replace('{ | |
(\n)? # leading line = $1 | |
(^[ \t]*) # leading whitespace = $2 | |
(' . $this->getPattern() . ') [ \t]+ # list marker = $3 | |
((?s:.+?) # list item text = $4 | |
(\n{1,2})) | |
(?= \n* (\z | \2 (' . $this->getPattern() . ') [ \t]+)) | |
}mx', function (Text $w, Text $leadingLine, Text $ls, Text $m, Text $item) use ($options, $level) { | |
if ((string)$leadingLine or $item->match('/\n{2,}/')) { | |
$this->markdown->emit('outdent', array($item)); | |
$this->markdown->emit('block', array($item)); | |
} else { | |
$this->markdown->emit('outdent', array($item)); | |
$this->processList($item, $options, ++$level); | |
$item->rtrim(); | |
$this->markdown->emit('inline', array($item)); | |
} | |
return $this->getRenderer()->renderListItem($item) . "\n"; | |
}); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getName() | |
{ | |
return 'list'; | |
} | |
/** | |
* @return string | |
*/ | |
protected function getPattern() | |
{ | |
return '(?:' . $this->ul . '|' . $this->ol . ')'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment