diff --git a/psalm.xml b/psalm.xml
index 3b0a03d..3cb03e6 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -27,6 +27,7 @@
+
diff --git a/src/Components/Inlines/HardBreak.php b/src/Components/Inlines/HardBreak.php
new file mode 100644
index 0000000..b031469
--- /dev/null
+++ b/src/Components/Inlines/HardBreak.php
@@ -0,0 +1,91 @@
+width = $width;
+ $this->position = $position;
+ }
+
+ /**
+ * @param Excerpt $Excerpt
+ * @param State $State
+ * @return static|null
+ */
+ public static function build(Excerpt $Excerpt, State $State)
+ {
+ $context = $Excerpt->context();
+ $offset = $Excerpt->offset();
+
+ if ($offset < 1) {
+ return null;
+ }
+
+ if (\substr($context, $offset -1, 1) === '\\') {
+ $trimTrailingWhitespace = \rtrim(\substr($context, 0, $offset -1));
+ $contentLen = \strlen($trimTrailingWhitespace);
+
+ return new self($offset - $contentLen, $contentLen);
+ }
+
+ if ($offset < 2) {
+ return null;
+ }
+
+ if (\substr($context, $offset -2, 2) === ' ') {
+ $trimTrailingWhitespace = \rtrim(\substr($context, 0, $offset));
+ $contentLen = \strlen($trimTrailingWhitespace);
+
+ return new self($offset - $contentLen, $contentLen);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return an integer to declare that the inline should be treated as if it
+ * started from that position in the excerpt given to static::build.
+ * Return null to use the excerpt offset value.
+ * @return int|null
+ * */
+ public function modifyStartPositionTo()
+ {
+ return $this->position;
+ }
+
+ /**
+ * @return Element
+ */
+ public function stateRenderable(Parsedown $_)
+ {
+ return Element::selfClosing('br', []);
+ }
+
+ /**
+ * @return Text
+ */
+ public function bestPlaintext()
+ {
+ return new Text("\n");
+ }
+}
diff --git a/src/Components/Inlines/PlainText.php b/src/Components/Inlines/PlainText.php
index 79e7aa1..9d83216 100644
--- a/src/Components/Inlines/PlainText.php
+++ b/src/Components/Inlines/PlainText.php
@@ -2,11 +2,8 @@
namespace Erusev\Parsedown\Components\Inlines;
-use Erusev\Parsedown\AST\Handler;
use Erusev\Parsedown\AST\StateRenderable;
use Erusev\Parsedown\Components\Inline;
-use Erusev\Parsedown\Html\Renderables\Container;
-use Erusev\Parsedown\Html\Renderables\Element;
use Erusev\Parsedown\Html\Renderables\Text;
use Erusev\Parsedown\Parsedown;
use Erusev\Parsedown\Parsing\Excerpt;
@@ -45,34 +42,11 @@ final class PlainText implements Inline
}
/**
- * @return Handler
+ * @return Text
*/
public function stateRenderable(Parsedown $_)
{
- return new Handler(
- /** @return Container */
- function (State $_) {
- $Renderables = [];
- $text = $this->text;
-
- $text = \preg_replace('/(?text);
}
/**
diff --git a/src/Components/Inlines/SoftBreak.php b/src/Components/Inlines/SoftBreak.php
new file mode 100644
index 0000000..45b6a20
--- /dev/null
+++ b/src/Components/Inlines/SoftBreak.php
@@ -0,0 +1,76 @@
+width = $width;
+ $this->position = $position;
+ }
+
+ /**
+ * @param Excerpt $Excerpt
+ * @param State $State
+ * @return static|null
+ */
+ public static function build(Excerpt $Excerpt, State $State)
+ {
+ $context = $Excerpt->context();
+ $offset = $Excerpt->offset();
+
+ $trimTrailingWhitespaceBefore = \rtrim(\substr($context, 0, $offset));
+ $trimLeadingWhitespaceAfter = \ltrim(\substr($context, $offset + 1));
+ $contentLenBefore = \strlen($trimTrailingWhitespaceBefore);
+ $contentLenAfter = \strlen($trimLeadingWhitespaceAfter);
+
+ $originalLen = \strlen($context);
+ $afterWidth = $originalLen - $offset - $contentLenAfter;
+
+ return new self($offset + $afterWidth - $contentLenBefore, $contentLenBefore);
+ }
+
+ /**
+ * Return an integer to declare that the inline should be treated as if it
+ * started from that position in the excerpt given to static::build.
+ * Return null to use the excerpt offset value.
+ * @return int|null
+ * */
+ public function modifyStartPositionTo()
+ {
+ return $this->position;
+ }
+
+ /**
+ * @return Text
+ */
+ public function stateRenderable(Parsedown $_)
+ {
+ return new Text("\n");
+ }
+
+ /**
+ * @return Text
+ */
+ public function bestPlaintext()
+ {
+ return new Text("\n");
+ }
+}
diff --git a/src/Parsedown.php b/src/Parsedown.php
index 275fa7d..84822a8 100644
--- a/src/Parsedown.php
+++ b/src/Parsedown.php
@@ -22,10 +22,12 @@ use Erusev\Parsedown\Components\Inlines\Code;
use Erusev\Parsedown\Components\Inlines\Email;
use Erusev\Parsedown\Components\Inlines\Emphasis;
use Erusev\Parsedown\Components\Inlines\EscapeSequence;
+use Erusev\Parsedown\Components\Inlines\HardBreak;
use Erusev\Parsedown\Components\Inlines\Image;
use Erusev\Parsedown\Components\Inlines\Link;
use Erusev\Parsedown\Components\Inlines\Markup as InlineMarkup;
use Erusev\Parsedown\Components\Inlines\PlainText;
+use Erusev\Parsedown\Components\Inlines\SoftBreak;
use Erusev\Parsedown\Components\Inlines\SpecialCharacter;
use Erusev\Parsedown\Components\Inlines\Strikethrough;
use Erusev\Parsedown\Components\Inlines\Url;
@@ -41,48 +43,13 @@ use Erusev\Parsedown\Parsing\Lines;
final class Parsedown
{
- # ~
-
const version = '2.0.0-dev';
- # ~
-
/** @var State */
private $State;
- public function __construct(State $State = null)
- {
- $this->State = $State ?: new State;
- }
-
- /**
- * @param string $text
- * @return string
- */
- public function text($text)
- {
- $InitialState = $this->State;
-
- $StateRenderables = $this->lines(Lines::fromTextLines($text, 0));
-
- $Renderables = $this->State->applyTo($StateRenderables);
-
- $this->State = $InitialState;
-
- $html = self::render($Renderables);
-
- # trim line breaks
- $html = \trim($html, "\n");
-
- return $html;
- }
-
- #
- # Lines
- #
-
/** @var array[]> */
- protected $BlockTypes = [
+ private $BlockTypes = [
'#' => [Header::class],
'*' => [Rule::class, TList::class],
'+' => [TList::class],
@@ -108,16 +75,57 @@ final class Parsedown
'~' => [FencedCode::class],
];
- # ~
-
/** @var class-string[] */
- protected $unmarkedBlockTypes = [
+ private $unmarkedBlockTypes = [
IndentedCode::class,
];
- #
- # Blocks
- #
+ /** @var array[]> */
+ private $InlineTypes = [
+ '!' => [Image::class],
+ '*' => [Emphasis::class],
+ '_' => [Emphasis::class],
+ '&' => [SpecialCharacter::class],
+ '[' => [Link::class],
+ ':' => [Url::class],
+ '<' => [UrlTag::class, Email::class, InlineMarkup::class],
+ '`' => [Code::class],
+ '~' => [Strikethrough::class],
+ '\\' => [EscapeSequence::class],
+ "\n" => [HardBreak::class, SoftBreak::class],
+ ];
+
+ /** @var string */
+ private $inlineMarkers;
+
+ public function __construct(State $State = null)
+ {
+ $this->State = $State ?: new State;
+
+ $this->inlineMarkers = \implode('', \array_keys($this->InlineTypes));
+ }
+
+ /**
+ * @param string $text
+ * @return string
+ */
+ public function text($text)
+ {
+ $InitialState = $this->State;
+
+ $StateRenderables = $this->lines(Lines::fromTextLines($text, 0));
+
+ $Renderables = $this->State->applyTo($StateRenderables);
+
+ $this->State = $InitialState;
+
+ $html = self::render($Renderables);
+
+ # trim line breaks
+ $html = \trim($html, "\n");
+
+ return $html;
+ }
/**
* @return StateRenderable[]
@@ -225,29 +233,6 @@ final class Parsedown
return $Blocks;
}
- #
- # Inline Elements
- #
-
- /** @var array[]> */
- protected $InlineTypes = [
- '!' => [Image::class],
- '&' => [SpecialCharacter::class],
- '*' => [Emphasis::class],
- ':' => [Url::class],
- '<' => [UrlTag::class, Email::class, InlineMarkup::class],
- '[' => [Link::class],
- '_' => [Emphasis::class],
- '`' => [Code::class],
- '~' => [Strikethrough::class],
- '\\' => [EscapeSequence::class],
- ];
-
- # ~
-
- /** @var string */
- protected $inlineMarkerList = '!*_&[:<`~\\';
-
#
# ~
#
@@ -280,9 +265,9 @@ final class Parsedown
# $excerpt is based on the first occurrence of a marker
for (
- $Excerpt = (new Excerpt($text, 0))->pushingOffsetTo($this->inlineMarkerList);
+ $Excerpt = (new Excerpt($text, 0))->pushingOffsetTo($this->inlineMarkers);
$Excerpt->text() !== '';
- $Excerpt = $Excerpt->pushingOffsetTo($this->inlineMarkerList)
+ $Excerpt = $Excerpt->pushingOffsetTo($this->inlineMarkers)
) {
$marker = \substr($Excerpt->text(), 0, 1);