From 9a7371cacbeea8d4e09f64f88027c41e67c02a57 Mon Sep 17 00:00:00 2001 From: Jitendra Adhikari Date: Thu, 26 Oct 2017 17:16:40 +0700 Subject: [PATCH 1/5] wip: [skip ci] [ci skip] --- src/HtmlUp.php | 2 +- tests/bootstrap.php | 14 +---------- tests/src/HtmlUpTest.php | 50 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/HtmlUp.php b/src/HtmlUp.php index 58ecfb8..ab4aef8 100644 --- a/src/HtmlUp.php +++ b/src/HtmlUp.php @@ -377,7 +377,7 @@ protected function listt() ++$this->listLevel; } - $this->markup .= '
  • ' . ltrim($this->trimmedLine, '-*0123456789. '); + $this->markup .= '
  • ' . ltrim($this->trimmedLine, '+-*0123456789. '); $this->listInternal(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 5244c37..6c8c4f5 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,15 +1,3 @@ assertion($testName, $expectedMarkup, $actualMarkup); } + public function testParseWithArg() + { + $htmlup = new HtmlUp; + + $this->assertion( + 'Parse the markdown provided in runtime', + '

    abc

    ', + $htmlup->parse('`abc`') + ); + } + /** * Prototype of test data: * @@ -28,15 +39,50 @@ public function testAll() public function dataSrc() { return [ + [ + 'Empty', + '', + '' + ], + [ + 'Raw html', + '
    this is html already
    ', + '
    this is html already
    ' + ], + [ + 'Quotes', + "> Here goes a quote\n\n> And another one", + '

    Here goes a quote

    ' . + '

    And another one

    ' + ], + [ + 'Nested Quotes', + "> Main quote\n>> And nested one", + '

    Main quote' . + '


    And nested one

    ' . + '
    ' + ], + [ + 'Setext', + "Header2\n---\nHeader1\n===", + '

    Header2

    Header1

    ' + ], [ 'Atx Header', $this->assemble('# HelloH1', '## HelloH2'), '

    HelloH1

    HelloH2

    ', ], + [ + 'Codes', + "```php\n\necho 'HtmlUp rocks';", + '
    echo \'HtmlUp rocks\';
    ' + ], [ 'Unordered List', - $this->assemble('- Hello', '* HelloAgain'), - '
    • Hello
    • HelloAgain
    ', + $this->assemble('- Hello', '* HelloAgain', ' + DeepHello', ' - DeepHelloAgain'), + '
    • Hello
    • HelloAgain' . + '
      • DeepHello
      • DeepHelloAgain
      ' . + '
    ', ], [ 'Ordered List', From db496ca1ead8ddf15509a10ab17e1fa22c87ba6a Mon Sep 17 00:00:00 2001 From: Jitendra Adhikari Date: Thu, 26 Oct 2017 19:13:40 +0700 Subject: [PATCH 2/5] fix: extract method and csfix --- src/HtmlUp.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/HtmlUp.php b/src/HtmlUp.php index ab4aef8..f594de3 100644 --- a/src/HtmlUp.php +++ b/src/HtmlUp.php @@ -113,12 +113,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; } @@ -127,6 +123,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]; @@ -371,7 +372,7 @@ protected function listt() if (!$this->inList) { $this->stackList[] = ""; - $this->markup .= "\n<$wrapper>\n"; + $this->markup .= "\n<$wrapper>\n"; $this->inList = true; ++$this->listLevel; @@ -394,7 +395,7 @@ protected function listInternal() if ($this->nextIndent > $this->indent) { $this->stackList[] = "
  • \n"; $this->stackList[] = ""; - $this->markup .= "\n<$wrapper>\n"; + $this->markup .= "\n<$wrapper>\n"; ++$this->listLevel; } else { @@ -459,7 +460,7 @@ protected function tableInternal($headerCount) ++$this->pointer; $this->inTable = true; - $this->markup .= "\n\n\n"; + $this->markup .= "
    \n\n\n"; $this->trimmedLine = trim($this->trimmedLine, '|'); foreach (explode('|', $this->trimmedLine) as $hdr) { From 1319384685df36a214611438ec0da20ca0402d48 Mon Sep 17 00:00:00 2001 From: Jitendra Adhikari Date: Thu, 26 Oct 2017 19:14:05 +0700 Subject: [PATCH 3/5] test: 100% --- tests/src/HtmlUpTest.php | 86 +++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/tests/src/HtmlUpTest.php b/tests/src/HtmlUpTest.php index 4b85527..c7ed097 100644 --- a/tests/src/HtmlUpTest.php +++ b/tests/src/HtmlUpTest.php @@ -42,51 +42,67 @@ public function dataSrc() [ 'Empty', '', - '' + '', ], [ 'Raw html', '
    this is html already
    ', - '
    this is html already
    ' + '
    this is html already
    ', ], [ 'Quotes', "> Here goes a quote\n\n> And another one", '

    Here goes a quote

    ' . - '

    And another one

    ' + '

    And another one

    ', ], [ 'Nested Quotes', "> Main quote\n>> And nested one", '

    Main quote' . '


    And nested one

    ' . - '
    ' + '', ], [ 'Setext', "Header2\n---\nHeader1\n===", - '

    Header2

    Header1

    ' + '

    Header2

    Header1

    ', ], [ 'Atx Header', - $this->assemble('# HelloH1', '## HelloH2'), + "# HelloH1\n## HelloH2", '

    HelloH1

    HelloH2

    ', ], [ 'Codes', "```php\n\necho 'HtmlUp rocks';", - '
    echo \'HtmlUp rocks\';
    ' + '
    echo \'HtmlUp rocks\';
    ', + ], + [ + 'Code with indent', + " <?php phpinfo();', ], [ 'Unordered List', - $this->assemble('- Hello', '* HelloAgain', ' + DeepHello', ' - DeepHelloAgain'), - '
    • Hello
    • HelloAgain' . - '
      • DeepHello
      • DeepHelloAgain
      ' . - '
    ', + "- Hello\n* HelloAgain\n + DeepHello\n - Deeper\n - Deepest" . + "\n - Undeep\n* OutAgain", + '
      ' . + '
    • Hello
    • ' . + '
    • HelloAgain' . + '
        ' . + '
      • DeepHello
      • ' . + '
      • Deeper' . + '
        • Deepest
        ' . + '
      • ' . + '
      • Undeep
      • ' . + '
      ' . + '
    • ' . + '
    • OutAgain
    • ' . + '
    ', ], [ 'Ordered List', - $this->assemble('1. Hello', '2. HelloAgain'), + "1. Hello\n2. HelloAgain", '
    1. Hello
    2. HelloAgain
    ', ], [ @@ -96,30 +112,36 @@ public function dataSrc() ], [ 'Horizontal Rule', - $this->assemble('', '***', '', '___'), + "***\n\n___", '

    ', ], [ 'Table', - $this->assemble('a | b', '---|---', '1 | 2', '4 | 5'), - '
    - - - - - - - - - - - - - - - - -
    ab
    12
    45
    ', + "a | b\n---|---\n1 | 2 | 3\n4 | 5", + '' . + '' . + '' . + '' . + '' . + '
    ab
    12
    45
    ', + ], + [ + 'Font faces', + '**Bold** _em_ `code` __strong__ ~~strike~~', + '

    Bold em code' . + ' strong strike

    ', + ], + [ + 'Image', + '[![alt](http://imageurl)](https://contenturl)', + '

    alt

    ', + ], + [ + 'URLs', + "[label](https://link) ", + '

    label' . + ' http://anotherlink' . + ' mail@localhost

    ', ], ]; } From 69edd260922cf44dbbccc086eac7f9e7aef81174 Mon Sep 17 00:00:00 2001 From: Jitendra Adhikari Date: Thu, 26 Oct 2017 20:49:36 +0700 Subject: [PATCH 4/5] feat: dynamic indentation --- src/HtmlUp.php | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/HtmlUp.php b/src/HtmlUp.php index f594de3..4a2005f 100644 --- a/src/HtmlUp.php +++ b/src/HtmlUp.php @@ -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 = ''; @@ -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); @@ -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 ''; } @@ -222,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) { @@ -313,9 +314,10 @@ 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] . '"' : ''; @@ -323,7 +325,7 @@ protected function code() $this->markup .= "\n
    ";
     
                 if (!$codeBlock) {
    -                $this->markup .= $this->escape(substr($this->line, 4));
    +                $this->markup .= $this->escape(substr($this->line, $this->indentLen));
                 }
     
                 $this->codeInternal($codeBlock);
    @@ -342,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;
                 }
             }
         }
    @@ -403,7 +407,7 @@ protected function listInternal()
                 }
     
                 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);
    
    From 54a000a6572eabde06685000ce56ab83b143920d Mon Sep 17 00:00:00 2001
    From: Jitendra Adhikari 
    Date: Thu, 26 Oct 2017 20:50:03 +0700
    Subject: [PATCH 5/5] test: with 2 space indent
    
    ---
     tests/src/HtmlUpTest.php | 25 ++++++++++++++++++++-----
     1 file changed, 20 insertions(+), 5 deletions(-)
    
    diff --git a/tests/src/HtmlUpTest.php b/tests/src/HtmlUpTest.php
    index c7ed097..160b264 100644
    --- a/tests/src/HtmlUpTest.php
    +++ b/tests/src/HtmlUpTest.php
    @@ -7,17 +7,32 @@ 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;
    +        $htmlup = new HtmlUp('paragraph');
    +
    +        $this->assertion(
    +            'Parse constructor injected markdown',
    +            '

    paragraph

    ', + (string) $htmlup + ); $this->assertion( 'Parse the markdown provided in runtime',