1
0
mirror of https://github.com/erusev/parsedown.git synced 2023-08-10 21:13:06 +03:00
parsedown/src/Parsedown.php

250 lines
6.8 KiB
PHP
Raw Normal View History

2013-11-09 01:40:00 +04:00
<?php
2018-04-17 16:44:38 +03:00
namespace Erusev\Parsedown;
2013-07-11 00:22:16 +04:00
2019-01-20 05:39:31 +03:00
use Erusev\Parsedown\AST\StateRenderable;
use Erusev\Parsedown\Components\Block;
use Erusev\Parsedown\Components\Blocks\Paragraph;
use Erusev\Parsedown\Components\ContinuableBlock;
use Erusev\Parsedown\Components\Inline;
use Erusev\Parsedown\Components\Inlines\PlainText;
use Erusev\Parsedown\Components\StateUpdatingBlock;
2019-01-25 22:49:18 +03:00
use Erusev\Parsedown\Configurables\BlockTypes;
use Erusev\Parsedown\Configurables\InlineTypes;
use Erusev\Parsedown\Html\Renderable;
2019-01-20 05:39:31 +03:00
use Erusev\Parsedown\Html\Renderables\Text;
2018-12-05 17:07:55 +03:00
use Erusev\Parsedown\Parsing\Context;
2019-01-20 05:39:31 +03:00
use Erusev\Parsedown\Parsing\Excerpt;
2018-12-05 13:30:49 +03:00
use Erusev\Parsedown\Parsing\Line;
2018-12-05 17:07:55 +03:00
use Erusev\Parsedown\Parsing\Lines;
2018-12-05 13:30:49 +03:00
final class Parsedown
2013-07-11 00:22:16 +04:00
{
2018-04-17 16:44:38 +03:00
const version = '2.0.0-dev';
2015-01-19 18:11:13 +03:00
2019-01-20 05:44:34 +03:00
/** @var State */
private $State;
2019-01-30 22:53:49 +03:00
public function __construct(StateBearer $StateBearer = null)
2019-01-25 21:52:49 +03:00
{
2019-01-30 23:04:47 +03:00
$StateBearer = $StateBearer ?: new State;
$this->State = $StateBearer->state();
2019-01-25 21:52:49 +03:00
}
/**
* @param string $text
* @return string
*/
public function text($text)
{
list($StateRenderables, $State) = self::lines(
Lines::fromTextLines($text, 0),
$this->State
);
2019-01-25 21:52:49 +03:00
$Renderables = $State->applyTo($StateRenderables);
2019-01-25 21:52:49 +03:00
$html = self::render($Renderables);
# trim line breaks
$html = \trim($html, "\n");
return $html;
}
2014-05-05 15:39:40 +04:00
2019-01-20 05:44:34 +03:00
/**
* @return array{0: StateRenderable[], 1: State}
2019-01-20 05:44:34 +03:00
*/
public static function lines(Lines $Lines, State $State)
2014-04-17 11:59:35 +04:00
{
list($Blocks, $State) = self::blocks($Lines, $State);
$StateRenderables = \array_map(
/** @return StateRenderable */
function (Block $Block) { return $Block->stateRenderable(); },
$Blocks
);
return [$StateRenderables, $State];
}
/**
* @return array{0: Block[], 1: State}
*/
public static function blocks(Lines $Lines, State $State)
{
/** @var Block[] */
$Blocks = [];
2019-01-20 05:44:34 +03:00
/** @var Block|null */
$Block = null;
/** @var Block|null */
2014-04-17 11:59:35 +04:00
$CurrentBlock = null;
2013-11-09 01:40:00 +04:00
2018-12-05 17:07:55 +03:00
foreach ($Lines->contexts() as $Context) {
$Line = $Context->line();
2019-01-20 05:44:34 +03:00
if (
isset($CurrentBlock)
&& $CurrentBlock instanceof ContinuableBlock
&& ! $CurrentBlock instanceof Paragraph
) {
$Block = $CurrentBlock->advance($Context);
2018-12-04 19:24:25 +03:00
if (isset($Block)) {
2014-04-17 11:59:35 +04:00
$CurrentBlock = $Block;
2014-04-17 11:59:35 +04:00
continue;
}
}
2019-02-03 00:07:12 +03:00
$marker = \substr($Line->text(), 0, 1);
2013-11-09 01:40:00 +04:00
2019-01-25 22:49:18 +03:00
$potentialBlockTypes = \array_merge(
$State->get(BlockTypes::class)->unmarked(),
$State->get(BlockTypes::class)->markedBy($marker)
2019-01-25 22:49:18 +03:00
);
2019-01-25 22:49:18 +03:00
foreach ($potentialBlockTypes as $blockType) {
$Block = $blockType::build($Context, $CurrentBlock, $State);
2013-11-09 01:40:00 +04:00
2018-12-04 19:24:25 +03:00
if (isset($Block)) {
2019-01-20 05:44:34 +03:00
if ($Block instanceof StateUpdatingBlock) {
$State = $Block->latestState();
}
2013-11-10 00:23:56 +04:00
2019-01-20 05:44:34 +03:00
if (isset($CurrentBlock) && ! $Block->acquiredPrevious()) {
$Blocks[] = $CurrentBlock;
}
2014-04-17 11:59:35 +04:00
$CurrentBlock = $Block;
2013-11-09 01:40:00 +04:00
2014-04-17 11:59:35 +04:00
continue 2;
}
}
2019-01-26 17:51:05 +03:00
if (isset($CurrentBlock) && $CurrentBlock instanceof Paragraph) {
$Block = $CurrentBlock->advance($Context);
}
2018-12-04 19:24:25 +03:00
if (isset($Block)) {
$CurrentBlock = $Block;
2018-12-04 19:24:25 +03:00
} else {
if (isset($CurrentBlock)) {
$Blocks[] = $CurrentBlock;
}
2014-04-17 11:59:35 +04:00
2019-01-20 05:44:34 +03:00
$CurrentBlock = Paragraph::build($Context);
2014-04-17 11:59:35 +04:00
}
}
2018-12-04 19:24:25 +03:00
if (isset($CurrentBlock)) {
$Blocks[] = $CurrentBlock;
2015-01-11 15:35:09 +03:00
}
return [$Blocks, $State];
}
2019-01-20 05:44:34 +03:00
/**
* @param string $text
* @return StateRenderable[]
*/
public static function line($text, State $State)
{
return \array_map(
/** @return StateRenderable */
function (Inline $Inline) { return $Inline->stateRenderable(); },
self::inlines($text, $State)
);
}
/**
* @param string $text
* @return Inline[]
*/
public static function inlines($text, State $State)
2018-03-21 05:18:34 +03:00
{
# standardize line breaks
2018-12-04 19:24:25 +03:00
$text = \str_replace(["\r\n", "\r"], "\n", $text);
/** @var Inline[] */
$Inlines = [];
2015-06-25 01:05:05 +03:00
# $excerpt is based on the first occurrence of a marker
$InlineTypes = $State->get(InlineTypes::class);
2019-01-25 22:49:18 +03:00
$markerMask = $InlineTypes->markers();
2019-01-20 05:44:34 +03:00
for (
2019-01-25 22:49:18 +03:00
$Excerpt = (new Excerpt($text, 0))->pushingOffsetTo($markerMask);
2019-01-20 05:44:34 +03:00
$Excerpt->text() !== '';
2019-01-25 22:49:18 +03:00
$Excerpt = $Excerpt->pushingOffsetTo($markerMask)
2019-01-20 05:44:34 +03:00
) {
$marker = \substr($Excerpt->text(), 0, 1);
2013-11-09 01:40:00 +04:00
2019-01-25 23:19:18 +03:00
foreach ($InlineTypes->markedBy($marker) as $inlineType) {
# check to see if the current inline type is nestable in the current context
$Inline = $inlineType::build($Excerpt, $State);
2014-01-20 11:26:25 +04:00
2018-12-04 19:24:25 +03:00
if (! isset($Inline)) {
2014-04-28 03:10:18 +04:00
continue;
}
2014-01-20 11:26:25 +04:00
2019-01-20 05:44:34 +03:00
$startPosition = $Inline->modifyStartPositionTo();
2015-06-25 01:05:05 +03:00
2019-01-20 05:44:34 +03:00
if (! isset($startPosition)) {
$startPosition = $Excerpt->offset();
}
2019-01-20 05:44:34 +03:00
# makes sure that the inline belongs to "our" marker
2015-06-25 01:05:05 +03:00
2019-01-20 05:44:34 +03:00
if ($startPosition > $Excerpt->offset()) {
continue;
}
2015-06-25 01:05:05 +03:00
# the text that comes before the inline
# compile the unmarked text
$Inlines[] = Plaintext::build($Excerpt->choppingUpToOffset($startPosition));
2014-01-21 00:19:23 +04:00
2015-06-25 01:05:05 +03:00
# compile the inline
$Inlines[] = $Inline;
2014-01-21 00:19:23 +04:00
2015-06-25 01:05:05 +03:00
# remove the examined text
2019-01-20 05:44:34 +03:00
/** @psalm-suppress LoopInvalidation */
$Excerpt = $Excerpt->choppingFromOffset($startPosition + $Inline->width());
2014-04-28 03:10:18 +04:00
continue 2;
2014-04-17 11:59:35 +04:00
}
2019-01-20 05:44:34 +03:00
/** @psalm-suppress LoopInvalidation */
$Excerpt = $Excerpt->addingToOffset(1);
2015-01-16 04:18:07 +03:00
}
$Inlines[] = Plaintext::build($Excerpt->choppingFromOffset(0));
2013-11-09 01:40:00 +04:00
return $Inlines;
}
2014-01-18 17:10:24 +04:00
2018-03-19 01:37:40 +03:00
/**
* @param Renderable[] $Renderables
2019-01-20 05:44:34 +03:00
* @return string
2018-03-19 01:37:40 +03:00
*/
public static function render(array $Renderables)
{
2019-01-20 05:44:34 +03:00
return \array_reduce(
$Renderables,
2019-01-20 05:44:34 +03:00
/**
* @param string $html
* @return string
*/
function ($html, Renderable $Renderable) {
$newHtml = $Renderable->getHtml();
return $html . ($newHtml === '' ? '' : "\n") . $newHtml;
2019-01-20 05:44:34 +03:00
},
''
);
2014-02-21 04:22:31 +04:00
}
}