From 3cbbf288dcce94f908c1363dc81997faab90abe0 Mon Sep 17 00:00:00 2001 From: Davey Shafik Date: Tue, 30 Dec 2014 20:48:57 -0500 Subject: [PATCH] Initial commit of league\markua --- .gitattributes | 11 + .gitignore | 6 + .scrutinizer.yml | 38 ++++ .travis.yml | 18 ++ CONTRIBUTING.md | 32 +++ LICENSE | 32 +++ README.md | 143 +++++++++++++ composer.json | 39 ++++ phpunit.xml.dist | 18 ++ src/Block/Element/Aside.php | 102 ++++++++++ src/Block/Element/IconBlock.php | 132 ++++++++++++ src/Block/Parser/AsideParser.php | 51 +++++ src/Block/Parser/IconBlockParser.php | 53 +++++ src/Block/Renderer/AsideRenderer.php | 50 +++++ src/Block/Renderer/IconBlockRenderer.php | 52 +++++ src/Environment/Markua.php | 38 ++++ src/MarkuaConverter.php | 37 ++++ tests/MarkuaSpecTest.php | 66 ++++++ tests/bootstrap.php | 12 ++ tests/spec/markua/aside.txt | 34 ++++ tests/spec/markua/icon-blocks.txt | 244 +++++++++++++++++++++++ 21 files changed, 1208 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .scrutinizer.yml create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 phpunit.xml.dist create mode 100644 src/Block/Element/Aside.php create mode 100644 src/Block/Element/IconBlock.php create mode 100644 src/Block/Parser/AsideParser.php create mode 100644 src/Block/Parser/IconBlockParser.php create mode 100644 src/Block/Renderer/AsideRenderer.php create mode 100644 src/Block/Renderer/IconBlockRenderer.php create mode 100644 src/Environment/Markua.php create mode 100644 src/MarkuaConverter.php create mode 100644 tests/MarkuaSpecTest.php create mode 100644 tests/bootstrap.php create mode 100644 tests/spec/markua/aside.txt create mode 100644 tests/spec/markua/icon-blocks.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..43590dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Path-based git attributes +# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html + +# Ignore all test and documentation with "export-ignore". +/.gitattributes export-ignore +/.gitignore export-ignore +/.scrutinizer.yml export-ignore +/.travis.yml export-ignore +/phpunit.xml.dist export-ignore +/scrutinizer.yml export-ignore +/tests export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..158f9a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.idea/ +composer.lock +composer.phar +/vendor/ +phpunit.xml +tests/output diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..ea0b5a6 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,38 @@ +filter: + excluded_paths: [tests/*] +checks: + php: + code_rating: true + remove_extra_empty_lines: true + remove_php_closing_tag: true + remove_trailing_whitespace: true + fix_use_statements: + remove_unused: true + preserve_multiple: false + preserve_blanklines: true + order_alphabetically: true + fix_php_opening_tag: true + fix_linefeed: true + fix_line_ending: true + fix_identation_4spaces: true + fix_doc_comments: true +tools: + external_code_coverage: + timeout: 1200 + runs: 3 + php_analyzer: true + php_code_coverage: false + php_code_sniffer: + config: + standard: PSR2 + filter: + paths: ['src'] + php_cpd: + enabled: true + excluded_dirs: [vendor, tests] + php_loc: + enabled: true + excluded_dirs: [vendor, tests] + php_pdepend: true + php_sim: true + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7ee8fc5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +sudo: false + +install: travis_retry composer install --no-interaction --prefer-source + +script: phpunit --coverage-text --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..99ceb0a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +We accept contributions via Pull Requests on [GitHub](https://github.com/thephpleague/markua). + + +## Pull Requests + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **Create feature branches** - Don't ask us to pull from your master branch. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. + + +## Running Tests + +``` bash +$ phpunit +``` + + +**Happy coding**! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..01cf61c --- /dev/null +++ b/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2014, Davey Shafik + +Based on stmd.js: Copyright (c) 2014, John MacFarlane + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Colin O'Dell nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..418deac --- /dev/null +++ b/README.md @@ -0,0 +1,143 @@ +# Markua + +[![Build Status](https://travis-ci.org/dshafik/markua.svg?branch=master)](https://travis-ci.org/dshafik/markua) + + +**league/markua** is a Markdown parser for PHP which intends to support the full [Markua] spec. The Markua spec is [still evolving](https://leanpub.com/markua/read). + +## Goals + +This aims to fully support the Markua specification, and will continue to evolve as the spec does. + +### League\CommonMark + +This package depends on [League\CommonMark](http://commonmark.thephpleague.com) and functions identically +except for using the Markua specification. + +## Installation + +This project can be installed via [Composer]: + +``` bash +$ composer require league/markua +``` + +## Basic Usage + +The `MarkuaConverter` class provides a simple wrapper for converting Markua to HTML: + +```php +use League\Markua\MarkuaConverter; + +$converter = new MarkuaConverter(); +echo $converter->convertToHtml('# Hello World!'); + +//

Hello World!

+``` + +## Advanced Usage & Customization + +The actual conversion process requires two steps: + + 1. Parsing the Markdown input into an AST + 2. Rendering the AST document as HTML + +Although the `MarkuaConverter` wrapper simplifies this process for you, advanced users will likely want to do this themselves: + +```php +use League\CommonMark\DocParser; +use League\CommonMark\Environment; +use League\CommonMark\HtmlRenderer; +use League\Markua\Environment\Markua; + +// Obtain a pre-configured Environment with all the Markua parsers/renderers ready-to-go +$environment = Environment::createEnvironment(new Markua()); + +// Optional: Add your own parsers/renderers here, if desired +// For example: $environment->addInlineParser(new TwitterHandleParser()); + +// Create the document parser and HTML renderer engines +$parser = new DocParser($environment); +$htmlRenderer = new HtmlRenderer($environment); + +// Here's our sample input +$markdown = '# Hello World!'; + +// 1. Parse the Markdown to AST +$documentAST = $parser->parse($markdown); + +// Optional: If you want to access/modify the AST before rendering, do it here + +// 2. Render the AST as HTML +echo $htmlRenderer->renderBlock($documentAST); + +// The output should be: +//

Hello World!

+``` + +This approach allows you to access/modify the AST before rendering it. + +You can also add custom parsers/renderers by [registering them with the `Environment` class](http://commonmark.thephpleague.com/customization/environment/). +The [league/commonmark documentation][commonmark-docs] provides several [customization examples][docs-examples] such as: + +- [Parsing Twitter handles into profile links][docs-example-twitter] +- [Converting smilies into emoticon images][docs-example-smilies] + +You can also reference the core CommonMark parsers/renderers as they use the same functionality available to you. + +## Compatibility with Markua ## + +This project aims to fully support the entire [Markua Spec]. Other flavors of Markdown may work but are not supported. Any/all changes made to the spec should eventually find their way back into this codebase. + +This package is **not** part of Markua, but rather a compatible derivative. + +## Documentation + +Documentation can be found at [markua.thephpleague.com][docs]. + +## Testing + +``` bash +$ ./vendor/bin/phpunit +``` + +## Stability and Versioning + +While this package does work well, the underlying code should not be considered "stable" yet. The original spec may undergo changes in the near future, which will result in corresponding changes to this code. Any methods tagged with `@api` are not expected to change, but other methods/classes might. + +Major release 1.0.0 will be reserved for when both Markua and this project are considered stable. 0.x.x will be used until that happens. + +SemVer will be followed [closely](http://semver.org/). + +## Contributing + +If you encounter a bug in the spec, please report it to the [Markua] project. Any resulting fix will eventually be implemented in this project as well. + +Please see [CONTRIBUTING](https://github.com/thephpleague/commonmark/blob/master/CONTRIBUTING.md) for additional details. + +## Credits & Acknowledgements + +- [Davey Shafik][@dshafik] +- [Colin O'Dell][@colinodell] +- [John MacFarlane][@jgm] +- [All Contributors] + +## License ## + +**league/markua** is licensed under the BSD-3 license. See the `LICENSE` file for more details. + +[Markua]: http://markua.org/ +[Markua spec]: https://leanpub.com/markua/read +[John MacFarlane]: http://johnmacfarlane.net +[commonmark-docs]: http://commonmark.thephpleague.com/ +[docs]: http://markua.thephpleague.com/ +[docs-examples]: http://commonmark.thephpleague.com/customization/overview/#examples +[docs-example-twitter]: http://commonmark.thephpleague.com/customization/inline-parsing#example-1---twitter-handles +[docs-example-smilies]: http://commonmark.thephpleague.com/customization/inline-parsing#example-2---emoticons +[All Contributors]: https://github.com/thephpleague/markua/contributors +[@colinodell]: https://github.com/colinodell +[@jgm]: https://github.com/jgm +[jgm/stmd]: https://github.com/jgm/stmd +[Composer]: https://getcomposer.org/ +[Davey Shafik]: http://twitter.com/dshafik +[@dshafik]: https://github.com/dshafik diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a314ade --- /dev/null +++ b/composer.json @@ -0,0 +1,39 @@ +{ + "name": "league/markua", + "type": "library", + "description": "Markdown parser for PHP based on the Markua spec", + "keywords": ["markdown","parser","markua"], + "homepage": "https://github.com/thephpleague/markua", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Davey Shafik", + "email": "me@daveyshafik.com", + "homepage": "http://daveyshafik.com", + "role": "Lead Developer" + } + ], + "repositories": [ + { + "type": "vcs", + "url": "git@github.com:dshafik/commonmark.git" + } + ], + "require": { + "php": ">=5.3.3", + "league/commonmark": "dev-markua" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "autoload": { + "psr-4": { + "League\\Markua\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "League\\Markua\\Tests\\": "tests/" + } + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..550084a --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + tests/ + + + + + + src/ + + + + + + + diff --git a/src/Block/Element/Aside.php b/src/Block/Element/Aside.php new file mode 100644 index 0000000..51ad7ed --- /dev/null +++ b/src/Block/Element/Aside.php @@ -0,0 +1,102 @@ + + * (c) Colin O'Dell + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua\Block\Element; + +use League\CommonMark\Block\Element\AbstractBlock; +use League\CommonMark\Block\Element\Paragraph; +use League\CommonMark\ContextInterface; +use League\CommonMark\Cursor; + +class Aside extends AbstractBlock +{ + /** + * Returns true if this block can contain the given block as a child node + * + * @param AbstractBlock $block + * + * @return bool + */ + public function canContain(AbstractBlock $block) + { + return true; + } + + /** + * Returns true if block type can accept lines of text + * + * @return bool + */ + public function acceptsLines() + { + return false; + } + + /** + * Whether this is a code block + * + * @return bool + */ + public function isCode() + { + return false; + } + + public function matchesNextLine(Cursor $cursor) + { + if ($cursor->getIndent() <= 3 && $cursor->getFirstNonSpaceCharacter() == 'A') { + $cursor->advanceToFirstNonSpace(); + $cursor->advance(); + if ($cursor->getCharacter() === '>') { + $cursor->advance(); + if ($cursor->getCharacter() === ' ') { + $cursor->advance(); + } + } + + return true; + } + + return false; + } + + /** + * @param ContextInterface $context + * @param Cursor $cursor + */ + public function handleRemainingContents(ContextInterface $context, Cursor $cursor) + { + if ($cursor->isBlank()) { + return; + } + + $context->addBlock(new Paragraph()); + $cursor->advanceToFirstNonSpace(); + $context->getTip()->addLine($cursor->getRemainder()); + } + + /** + * @param Cursor $cursor + * @param int $currentLineNumber + * + * @return $this + */ + public function setLastLineBlank(Cursor $cursor, $currentLineNumber) + { + parent::setLastLineBlank($cursor, $currentLineNumber); + + $this->lastLineBlank = false; + } +} diff --git a/src/Block/Element/IconBlock.php b/src/Block/Element/IconBlock.php new file mode 100644 index 0000000..ce71d16 --- /dev/null +++ b/src/Block/Element/IconBlock.php @@ -0,0 +1,132 @@ + + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua\Block\Element; + +use League\CommonMark\Block\Element\AbstractBlock; +use League\CommonMark\Block\Element\Paragraph; +use League\CommonMark\ContextInterface; +use League\CommonMark\Cursor; + +class IconBlock extends AbstractBlock +{ + protected $type; + + public function __construct($type) + { + parent::__construct(); + $this->type = $type; + } + + public function getType() + { + return $this->type; + } + + public function getTypeName() + { + $types = array_flip(static::getIconBlockTypes()); + return $types[$this->type]; + } + + /** + * Returns true if this block can contain the given block as a child node + * + * @param AbstractBlock $block + * + * @return bool + */ + public function canContain(AbstractBlock $block) + { + return true; + } + + /** + * Returns true if block type can accept lines of text + * + * @return bool + */ + public function acceptsLines() + { + return false; + } + + /** + * Whether this is a code block + * + * @return bool + */ + public function isCode() + { + return false; + } + + public function matchesNextLine(Cursor $cursor) + { + if ($cursor->getIndent() <= 3 && in_array($cursor->getFirstNonSpaceCharacter(), static::getIconBlockTypes())) { + $cursor->advanceToFirstNonSpace(); + $cursor->advance(); + if ($cursor->getCharacter() === '>') { + $cursor->advance(); + if ($cursor->getCharacter() === ' ') { + $cursor->advance(); + } + } + + return true; + } + + return false; + } + + /** + * @param ContextInterface $context + * @param Cursor $cursor + */ + public function handleRemainingContents(ContextInterface $context, Cursor $cursor) + { + if ($cursor->isBlank()) { + return; + } + + $context->addBlock(new Paragraph()); + $cursor->advanceToFirstNonSpace(); + $context->getTip()->addLine($cursor->getRemainder()); + } + + /** + * @param Cursor $cursor + * @param int $currentLineNumber + * + * @return $this + */ + public function setLastLineBlank(Cursor $cursor, $currentLineNumber) + { + parent::setLastLineBlank($cursor, $currentLineNumber); + + $this->lastLineBlank = false; + } + + public static function getIconBlockTypes() { + return array( + 'discussion' => 'D', + 'error' => 'E', + 'info' => 'I', + 'question' => 'Q', + 'tip' => 'T', + 'warning' => 'W', + 'exercise' => 'X' + ); + } +} diff --git a/src/Block/Parser/AsideParser.php b/src/Block/Parser/AsideParser.php new file mode 100644 index 0000000..47e91fe --- /dev/null +++ b/src/Block/Parser/AsideParser.php @@ -0,0 +1,51 @@ + + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua\Block\Parser; + +use League\CommonMark\Block\Parser\AbstractBlockParser; +use League\CommonMark\ContextInterface; +use League\CommonMark\Cursor; +use League\Markua\Block\Element\Aside; +use League\Markua\Block\Element\IconBlock; + +class AsideParser extends AbstractBlockParser +{ + /** + * @param ContextInterface $context + * @param Cursor $cursor + * + * @return bool + */ + public function parse(ContextInterface $context, Cursor $cursor) + { + if ($cursor->getFirstNonSpaceCharacter() !== 'A' || $cursor->getCharacter($cursor->getFirstNonSpacePosition() + 1) !== '>') { + return false; + } + + $cursor->advanceToFirstNonSpace(); + $cursor->advance(); + if ($cursor->getCharacter() === '>') { + $cursor->advance(); + if ($cursor->getCharacter() === ' ') { + $cursor->advance(); + } + } + + $context->addBlock(new Aside()); + + return true; + } +} diff --git a/src/Block/Parser/IconBlockParser.php b/src/Block/Parser/IconBlockParser.php new file mode 100644 index 0000000..2cc227a --- /dev/null +++ b/src/Block/Parser/IconBlockParser.php @@ -0,0 +1,53 @@ + + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua\Block\Parser; + +use League\CommonMark\Block\Parser\AbstractBlockParser; +use League\CommonMark\ContextInterface; +use League\CommonMark\Cursor; +use League\Markua\Block\Element\Aside; +use League\Markua\Block\Element\IconBlock; + +class IconBlockParser extends AbstractBlockParser +{ + /** + * @param ContextInterface $context + * @param Cursor $cursor + * + * @return bool + */ + public function parse(ContextInterface $context, Cursor $cursor) + { + if (!in_array($cursor->getFirstNonSpaceCharacter(), IconBlock::getIconBlockTypes()) || $cursor->getCharacter($cursor->getFirstNonSpacePosition() + 1) !== '>') { + return false; + } + + $type = $cursor->getFirstNonSpaceCharacter(); + + $cursor->advanceToFirstNonSpace(); + $cursor->advance(); + if ($cursor->getCharacter() === '>') { + $cursor->advance(); + if ($cursor->getCharacter() === ' ') { + $cursor->advance(); + } + } + + $context->addBlock(new IconBlock($type)); + + return true; + } +} diff --git a/src/Block/Renderer/AsideRenderer.php b/src/Block/Renderer/AsideRenderer.php new file mode 100644 index 0000000..972378e --- /dev/null +++ b/src/Block/Renderer/AsideRenderer.php @@ -0,0 +1,50 @@ + + * (c) Colin O'Dell + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua\Block\Renderer; + +use League\CommonMark\Block\Element\AbstractBlock; +use League\CommonMark\Block\Renderer\BlockRendererInterface; +use League\CommonMark\HtmlElement; +use League\CommonMark\HtmlRenderer; +use League\Markua\Block\Element\Aside; + +class AsideRenderer implements BlockRendererInterface +{ + /** + * @param Aside $block + * @param HtmlRenderer $htmlRenderer + * @param bool $inTightList + * + * @return HtmlElement + */ + public function render(AbstractBlock $block, HtmlRenderer $htmlRenderer, $inTightList = false) + { + if (!($block instanceof Aside)) { + throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block)); + } + + $filling = $htmlRenderer->renderBlocks($block->getChildren()); + if ($filling === '') { + return new HtmlElement('aside', array(), $htmlRenderer->getOption('innerSeparator')); + } + + return new HtmlElement( + 'aside', + array(), + $htmlRenderer->getOption('innerSeparator') . $filling . $htmlRenderer->getOption('innerSeparator') + ); + } +} diff --git a/src/Block/Renderer/IconBlockRenderer.php b/src/Block/Renderer/IconBlockRenderer.php new file mode 100644 index 0000000..69160e5 --- /dev/null +++ b/src/Block/Renderer/IconBlockRenderer.php @@ -0,0 +1,52 @@ + + * (c) Colin O'Dell + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua\Block\Renderer; + +use League\CommonMark\Block\Element\AbstractBlock; +use League\CommonMark\Block\Renderer\BlockRendererInterface; +use League\CommonMark\HtmlElement; +use League\CommonMark\HtmlRenderer; +use League\Markua\Block\Element\Aside; +use League\Markua\Block\Element\IconBlock; + +class IconBlockRenderer implements BlockRendererInterface +{ + /** + * @param Aside $block + * @param HtmlRenderer $htmlRenderer + * @param bool $inTightList + * + * @return HtmlElement + */ + public function render(AbstractBlock $block, HtmlRenderer $htmlRenderer, $inTightList = false) + { + if (!($block instanceof IconBlock)) { + throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block)); + } + + $filling = $htmlRenderer->renderBlocks($block->getChildren()); + + if ($filling === '') { + return new HtmlElement('aside', array('class' => $block->getTypeName()), $htmlRenderer->getOption('innerSeparator')); + } + + return new HtmlElement( + 'aside', + array('class' => $block->getTypeName()), + $htmlRenderer->getOption('innerSeparator') . $filling . $htmlRenderer->getOption('innerSeparator') + ); + } +} diff --git a/src/Environment/Markua.php b/src/Environment/Markua.php new file mode 100644 index 0000000..489c2bc --- /dev/null +++ b/src/Environment/Markua.php @@ -0,0 +1,38 @@ + + * + * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmarkjs) + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Markua; + +use League\CommonMark\CommonMarkConverter; +use League\CommonMark\DocParser; +use League\CommonMark\Environment; +use League\CommonMark\HtmlRenderer; +use League\Markua\Environment\Markua; + +/** + * Converts Markua-compatible Markdown to HTML. + */ +class MarkuaConverter extends CommonMarkConverter +{ + /** + * Create a new markua converter instance. + */ + public function __construct() + { + $environment = Environment::createEnvironment(new Markua()); + $this->docParser = new DocParser($environment); + $this->htmlRenderer = new HtmlRenderer($environment); + } +} diff --git a/tests/MarkuaSpecTest.php b/tests/MarkuaSpecTest.php new file mode 100644 index 0000000..50f3acb --- /dev/null +++ b/tests/MarkuaSpecTest.php @@ -0,0 +1,66 @@ +convertor = new MarkuaConverter(); + } + + /** + * @param string $markdown Markdown to parse + * @param string $html Expected result + * + * @dataProvider dataProvider + */ + public function testExample($title, $markdown, $html) + { + $this->setName($title); + $actualResult = $this->convertor->convertToHtml($markdown); + + $this->assertEquals($html, $actualResult); + } + + /** + * @return array + */ + public function dataProvider() + { + foreach (glob(__DIR__ . '/spec/markua/*.txt') as $spec) { + $test = array(); + foreach (file($spec) as $line) { + if (preg_match('/=== (.*?) ===/', trim($line), $matches)) { + $section = $matches[1]; + if (strtolower($section) == 'end') { + $tests[] = $test; + $test = array(); + continue; + } + $test[$section] = ''; + } elseif (strtolower($section) != 'end') { + $test[$section] .= $line; + } + } + } + + return $tests; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..d0b5660 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once __DIR__.'/../vendor/autoload.php'; diff --git a/tests/spec/markua/aside.txt b/tests/spec/markua/aside.txt new file mode 100644 index 0000000..43dc87a --- /dev/null +++ b/tests/spec/markua/aside.txt @@ -0,0 +1,34 @@ +=== Name === +Aside +=== Markdown === +A> An aside +=== Result === + +=== End === + +=== Name === +Multi-line Aside +=== Markdown === +A> An aside +A> With multiple lines +=== Result === + +=== End === + +=== Name === +Aside with multiple paragraphs +=== Markdown === +A> An aside +A> +A> With multiple paragraphs +=== Result === + +=== End === diff --git a/tests/spec/markua/icon-blocks.txt b/tests/spec/markua/icon-blocks.txt new file mode 100644 index 0000000..f2328a6 --- /dev/null +++ b/tests/spec/markua/icon-blocks.txt @@ -0,0 +1,244 @@ +=== Name === +Icon Blocks: Discussion — Single Line +=== Markdown === +D> A discussion +=== Result === + +=== End === + +=== Name === +Icon Blocks: Discussion — Multi-line +=== Markdown === +D> A discussion +D> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Discussion — Multiple Paragraphs +=== Markdown === +D> A discussion +D> +D> With multiple paragraphs +=== Result === + +=== End === + +=== Name === +Icon Blocks: Error — Single Line +=== Markdown === +E> An error +=== Result === + +=== End === + +=== Name === +Icon Blocks: Error — Multi-line +=== Markdown === +E> An error +E> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Error — Multiple Paragraphs +=== Markdown === +E> An error +E> +E> With multiple paragraphs +=== Result === + +=== End === + +=== Name === +Icon Blocks: Information — Single Line +=== Markdown === +I> Some information +=== Result === + +=== End === + +=== Name === +Icon Blocks: Information — Multi-line +=== Markdown === +I> Some information +I> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Information — Multiple Paragraphs +=== Markdown === +I> Some information +I> +I> With multiple paragraphs +=== Result === + +=== End === + +=== Name === +Icon Blocks: Question — Single Line +=== Markdown === +Q> A question +=== Result === + +=== End === + +=== Name === +Icon Blocks: Question — Multi-line +=== Markdown === +Q> A question +Q> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Question — Multiple Paragraphs +=== Markdown === +Q> A question +Q> +Q> With multiple paragraphs +=== Result === + +=== End === + +=== Name === +Icon Blocks: Tip — Single Line +=== Markdown === +T> A tip +=== Result === + +=== End === + +=== Name === +Icon Blocks: Tip — Multi-line +=== Markdown === +T> A tip +T> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Tip — Multiple Paragraphs +=== Markdown === +T> A tip +T> +T> With multiple paragraphs +=== Result === + +=== End === + +=== Name === +Icon Blocks: Warning — Single Line +=== Markdown === +W> A warning +=== Result === + +=== End === + +=== Name === +Icon Blocks: Warning — Multi-line +=== Markdown === +W> A warning +W> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Warning — Multiple Paragraphs +=== Markdown === +W> A warning +W> +W> With multiple paragraphs +=== Result === + +=== End === + +=== Name === +Icon Blocks: Exercise — Single Line +=== Markdown === +X> An exercise +=== Result === + +=== End === + +=== Name === +Icon Blocks: Exercise — Multi-line +=== Markdown === +X> An exercise +X> With multiple lines +=== Result === + +=== End === + +=== Name === +Icon Blocks: Exercise — Multiple Paragraphs +=== Markdown === +X> An exercise +X> +X> With multiple paragraphs +=== Result === + +=== End ===