Skip to content

Commit

Permalink
Merge pull request #6 from adhocore/develop
Browse files Browse the repository at this point in the history
Dynamic indentation
  • Loading branch information
adhocore committed Oct 26, 2017
2 parents b1717e7 + 54a000a commit 1225b6f
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 68 deletions.
59 changes: 32 additions & 27 deletions src/HtmlUp.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ class HtmlUp
protected $quoteLevel = 0;
protected $indent = 0;
protected $nextIndent = 0;
protected $indentLen = 4;

protected $indentStr = '';
protected $indentStr = ' ';
protected $line = '';
protected $trimmedLine = '';
protected $prevLine = '';
Expand All @@ -56,22 +57,22 @@ class HtmlUp
* Constructor.
*
* @param string $markdown
* @param int $indentWidth
*/
public function __construct($markdown = null, $indentWidth = 4)
{
$this->indentStr = $indentWidth == 2 ? ' ' : ' ';

if (null !== $markdown) {
$this->scan($markdown);
}
$this->scan($markdown, $indentWidth);
}

protected function scan($markdown)
protected function scan($markdown, $indentWidth = 4)
{
if ('' === trim($markdown)) {
return;
}

$this->indentLen = $indentWidth == 2 ? 2 : 4;
$this->indentStr = $indentWidth == 2 ? ' ' : ' ';

// Normalize whitespaces
$markdown = str_replace("\t", $this->indentStr, $markdown);
$markdown = str_replace(["\r\n", "\r"], "\n", $markdown);
Expand All @@ -84,15 +85,15 @@ public function __toString()
return $this->parse();
}

public function parse($markdown = null)
public function parse($markdown = null, $indentWidth = 4)
{
if (null !== $markdown) {
$this->reset(true);

$this->scan($markdown);
$this->scan($markdown, $indentWidth);
}

if ([] === $this->lines) {
if (empty($this->lines)) {
return '';
}

Expand All @@ -113,12 +114,8 @@ protected function parseBlockElements()

$this->quote();

if ($this->atx() || $this->setext() || $this->code() || $this->rule() || $this->listt()) {
continue;
}

if ($this->inList) {
$this->markup .= $this->trimmedLine;
if (($block = $this->isBlock()) || $this->inList) {
$this->markup .= $block ? '' : $this->trimmedLine;

continue;
}
Expand All @@ -127,6 +124,11 @@ protected function parseBlockElements()
}
}

protected function isBlock()
{
return $this->atx() || $this->setext() || $this->code() || $this->rule() || $this->listt();
}

protected function init()
{
list($this->prevLine, $this->trimmedPrevLine) = [$this->line, $this->trimmedLine];
Expand Down Expand Up @@ -221,7 +223,7 @@ protected function escape($input)

protected function reset($all = false)
{
$except = $all ? [] : array_fill_keys(['lines', 'pointer', 'markup'], true);
$except = $all ? [] : array_flip(['lines', 'pointer', 'markup', 'indentStr', 'indentLen']);

// Reset all current values.
foreach (get_class_vars(__CLASS__) as $prop => $value) {
Expand Down Expand Up @@ -312,17 +314,18 @@ protected function setext()

protected function code()
{
$codeBlock = (bool) preg_match(static::RE_MD_CODE, $this->line, $codeMatch);
$isShifted = ($this->indent - $this->nextIndent) >= $this->indentLen;
$codeBlock = preg_match(static::RE_MD_CODE, $this->line, $codeMatch);

if ($codeBlock || (empty($this->inList) && empty($this->inQuote) && $this->indent >= 4)) {
if ($codeBlock || (!$this->inList && !$this->inQuote && $isShifted)) {
$lang = isset($codeMatch[1])
? ' class="language-' . $codeMatch[1] . '"'
: '';

$this->markup .= "\n<pre><code{$lang}>";

if (!$codeBlock) {
$this->markup .= $this->escape(substr($this->line, 4));
$this->markup .= $this->escape(substr($this->line, $this->indentLen));
}

$this->codeInternal($codeBlock);
Expand All @@ -341,12 +344,14 @@ public function codeInternal($codeBlock)
$this->line = $this->escape($this->lines[$this->pointer + 1]);

if (($codeBlock && substr(ltrim($this->line), 0, 3) !== '```')
|| substr($this->line, 0, 4) === $this->indentStr
|| strpos($this->line, $this->indentStr) === 0
) {
$this->markup .= "\n"; // @todo: donot use \n for first line
$this->markup .= $codeBlock ? $this->line : substr($this->line, 4);
$this->markup .= $codeBlock ? $this->line : substr($this->line, $this->indentLen);

++$this->pointer;
} else {
break;
}
}
}
Expand All @@ -371,13 +376,13 @@ protected function listt()

if (!$this->inList) {
$this->stackList[] = "</$wrapper>";
$this->markup .= "\n<$wrapper>\n";
$this->markup .= "\n<$wrapper>\n";
$this->inList = true;

++$this->listLevel;
}

$this->markup .= '<li>' . ltrim($this->trimmedLine, '-*0123456789. ');
$this->markup .= '<li>' . ltrim($this->trimmedLine, '+-*0123456789. ');

$this->listInternal();

Expand All @@ -394,15 +399,15 @@ protected function listInternal()
if ($this->nextIndent > $this->indent) {
$this->stackList[] = "</li>\n";
$this->stackList[] = "</$wrapper>";
$this->markup .= "\n<$wrapper>\n";
$this->markup .= "\n<$wrapper>\n";

++$this->listLevel;
} else {
$this->markup .= "</li>\n";
}

if ($this->nextIndent < $this->indent) {
$shift = intval(($this->indent - $this->nextIndent) / 4);
$shift = intval(($this->indent - $this->nextIndent) / $this->indentLen);

while ($shift--) {
$this->markup .= array_pop($this->stackList);
Expand Down Expand Up @@ -459,7 +464,7 @@ protected function tableInternal($headerCount)
++$this->pointer;

$this->inTable = true;
$this->markup .= "<table>\n<thead>\n<tr>\n";
$this->markup .= "<table>\n<thead>\n<tr>\n";
$this->trimmedLine = trim($this->trimmedLine, '|');

foreach (explode('|', $this->trimmedLine) as $hdr) {
Expand Down
14 changes: 1 addition & 13 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
<?php

error_reporting(E_ALL);

foreach ([
dirname(__DIR__) . '/vendor/autoload.php',
dirname(dirname(dirname(__DIR__))) . '/autoload.php',
] as $loader) {
if (is_file($loader)) {
return require $loader;
}
}

// As a last resort
require dirname(__DIR__) . '/src/HtmlUp.php';
require_once __DIR__ . '/../vendor/autoload.php';
139 changes: 111 additions & 28 deletions tests/src/HtmlUpTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,38 @@ class HtmlUpTest extends PHPUnit_Framework_TestCase
/**
* @dataProvider dataSrc
*/
public function testAll()
public function testAllWith4spaces($testName, $markdown, $expected)
{
list($testName, $markdown, $expectedMarkup) = func_get_args();
$actualMarkup = (string) new HtmlUp($markdown);
$actualMarkup = (string) new HtmlUp($markdown, 4);

$this->assertion($testName, $expectedMarkup, $actualMarkup);
$this->assertion($testName, $expected, $actualMarkup);
}

/**
* @dataProvider dataSrc
*/
public function testAllWith2spaces($testName, $markdown, $expected)
{
$actualMarkup = (string) new HtmlUp(str_replace(' ', ' ', $markdown), 2);

$this->assertion($testName, $expected, $actualMarkup);
}

public function testParseWithArg()
{
$htmlup = new HtmlUp('paragraph');

$this->assertion(
'Parse constructor injected markdown',
'<p>paragraph</p>',
(string) $htmlup
);

$this->assertion(
'Parse the markdown provided in runtime',
'<p><code>abc</code></p>',
$htmlup->parse('`abc`')
);
}

/**
Expand All @@ -28,19 +54,70 @@ public function testAll()
public function dataSrc()
{
return [
[
'Empty',
'',
'',
],
[
'Raw html',
'<div><span>this is html already</div>',
'<div><span>this is html already</div>',
],
[
'Quotes',
"> Here goes a quote\n\n> And another one",
'<blockquote><p>Here goes a quote</p></blockquote>' .
'<blockquote><p>And another one</p></blockquote>',
],
[
'Nested Quotes',
"> Main quote\n>> And nested one",
'<blockquote><p>Main quote' .
'<blockquote><br />And nested one</p></blockquote>' .
'</blockquote>',
],
[
'Setext',
"Header2\n---\nHeader1\n===",
'<h2>Header2</h2><h1>Header1</h1>',
],
[
'Atx Header',
$this->assemble('# HelloH1', '## HelloH2'),
"# HelloH1\n## HelloH2",
'<h1>HelloH1</h1><h2>HelloH2</h2>',
],
[
'Codes',
"```php\n\necho 'HtmlUp rocks';",
'<pre><code class="language-php">echo \'HtmlUp rocks\';</code></pre>',
],
[
'Code with indent',
" <?php phpinfo();",
'<pre><code>&lt;?php phpinfo();</code></pre>',
],
[
'Unordered List',
$this->assemble('- Hello', '* HelloAgain'),
'<ul><li>Hello</li><li>HelloAgain</li></ul>',
"- Hello\n* HelloAgain\n + DeepHello\n - Deeper\n - Deepest" .
"\n - Undeep\n* OutAgain",
'<ul>' .
'<li>Hello</li>' .
'<li>HelloAgain' .
'<ul>' .
'<li>DeepHello</li>' .
'<li>Deeper' .
'<ul><li>Deepest</li></ul>' .
'</li>' .
'<li>Undeep</li>' .
'</ul>' .
'</li>' .
'<li>OutAgain</li>' .
'</ul>',
],
[
'Ordered List',
$this->assemble('1. Hello', '2. HelloAgain'),
"1. Hello\n2. HelloAgain",
'<ol><li>Hello</li><li>HelloAgain</li></ol>',
],
[
Expand All @@ -50,30 +127,36 @@ public function dataSrc()
],
[
'Horizontal Rule',
$this->assemble('', '***', '', '___'),
"***\n\n___",
'<hr /><hr />',
],
[
'Table',
$this->assemble('a | b', '---|---', '1 | 2', '4 | 5'),
'<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
</tr>
</tbody>
</table>',
"a | b\n---|---\n1 | 2 | 3\n4 | 5",
'<table><thead><tr><th>a</th><th>b</th></tr></thead>' .
'<tbody>' .
'<tr><td>1</td><td>2</td></tr>' .
'<tr><td>4</td><td>5</td></tr>' .
'</tbody>' .
'</table>',
],
[
'Font faces',
'**Bold** _em_ `code` __strong__ ~~strike~~',
'<p><strong>Bold</strong> <em>em</em> <code>code</code>' .
' <strong>strong</strong> <del>strike</del></p>',
],
[
'Image',
'[![alt](http://imageurl)](https://contenturl)',
'<p><a href="https://contenturl"><img src="http://imageurl" alt="alt" /></a></p>',
],
[
'URLs',
"[label](https://link) <http://anotherlink> <mail@localhost>",
'<p><a href="https://link">label</a>' .
' <a href="http://anotherlink">http://anotherlink</a>' .
' <a href="mailto:mail@localhost">mail@localhost</a></p>',
],
];
}
Expand Down

0 comments on commit 1225b6f

Please sign in to comment.