64
CommonMark Markdown done right Colin O’Dell @colinodel l

CommonMark: Markdown done right - Nomad PHP September 2016

Embed Size (px)

Citation preview

Page 1: CommonMark: Markdown done right - Nomad PHP September 2016

CommonMarkMarkdown done right

Colin O’Dell@colinodell

Page 2: CommonMark: Markdown done right - Nomad PHP September 2016

COLIN O’DELL

Creator & Maintainer of league/commonmarkLead Web Developer at Unleashed TechnologiesAuthor of PHP 7 Migration Guide e-book

@colinodell

Page 3: CommonMark: Markdown done right - Nomad PHP September 2016

TOPICS

Origins of MarkdownCommonMark Specleague/commonmark OverviewCustomizing league/commonmark

Page 4: CommonMark: Markdown done right - Nomad PHP September 2016

ORIGINS OF MARKDOWN

Created in March 2004 by John GruberInformal plain-text formatting languageConverts readable text to valid (X)HTMLPrimary goal - readability

Page 5: CommonMark: Markdown done right - Nomad PHP September 2016

HISTORY OF MARKDOWNHello Nomad PHP!----------------

Markdown is **awesome**!

1. Foo2. Bar3. Baz

Wikipedia entry: <https://en.wikipedia.org/wiki/Markdown>

Page 6: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT SUCCESSFUL?

1.Syntax is visually-similar to the resulting markup

2.Non-strict, forgiving parsing3.Easily adaptable for different uses

Page 7: CommonMark: Markdown done right - Nomad PHP September 2016

67+ DIFFERENT FLAVORS

Source: https://github.com/markdown/markdown.github.com/wiki/Implementations

ActuariusBlackfridayBlueClothBlueFeathercebe/markdownCocoaMarkdownDiscountffi-sundownGHMarkdownParserGoskirtHoedownHoepKnockoffkramdownLaikalibpandocLowdownlua-discount

Lunamarkmarkdownmarkdown-cljmarkdown-jsmarkdown-oo-phpmarkdown.bashmarkdown.luamarkdown.plmarkdown4jMarkdownDeepMarkdownJMarkdownPapersMarkdownSharpmarkedMarukumd2html.awkMisakaMistune

MMMarkdownMoonShineMultiMarkdownnode-discountnode-markdownnode-multimarkdownOMDPandocParsedownParsedown Extrapeg-markdownpeg-multimarkdown & forkpegdownPHP MarkdownPHP Markdown ExtraPHP-SundownPython-Discountpython-hoedown

Python-MarkdownPython-Markdown2RDiscountRedcarpetRoboSkirtShowdownSundownSundown HSSundown.nettext-markdowntexts.jsTxtmarkupskirt.go

Page 8: CommonMark: Markdown done right - Nomad PHP September 2016

https://xkcd.com/927/

Page 9: CommonMark: Markdown done right - Nomad PHP September 2016

COMMONMARK IS…A strongly defined, highly compatible specification of Markdown.

Written by John MacFarlane(in collaboration with people from Github, StackOverflow, Reddit, and others)

Spec includes: Strict rules (precedence, parsing order, handling edge cases) Specific definitions (ex: “whitespace”, “punctuation”) 616 examples

Page 10: CommonMark: Markdown done right - Nomad PHP September 2016
Page 11: CommonMark: Markdown done right - Nomad PHP September 2016
Page 12: CommonMark: Markdown done right - Nomad PHP September 2016
Page 13: CommonMark: Markdown done right - Nomad PHP September 2016
Page 14: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

*I love Markdown*<p><em>I love Markdown</em></p>

Page 15: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

*I *love* Markdown*

Page 16: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED? Source: http://johnmacfarlane.net/babelmark2/

Page 17: CommonMark: Markdown done right - Nomad PHP September 2016

30%

WHY IS IT NEEDED?

*I *love* Markdown*<p><em>I <em>love</em> Markdown</em></p>

*I *love* Markdown*<p><em>I </em>love<em> Markdown</em></p>

*I *love* Markdown*<p><em>I *love</em> Markdown*</p>

15%

33%

Source: http://johnmacfarlane.net/babelmark2/

Page 18: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

1. > Hello World! ------

Source: http://johnmacfarlane.net/babelmark2/

Page 19: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

1. > Hello World! ------

Source: http://johnmacfarlane.net/babelmark2/

Page 20: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

1. > Hello World! ------

Source: http://johnmacfarlane.net/babelmark2/

Page 21: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

1. > Hello World! ------

Source: http://johnmacfarlane.net/babelmark2/

Page 22: CommonMark: Markdown done right - Nomad PHP September 2016

WHY IS IT NEEDED?

1. > Hello World! ------

Source: http://johnmacfarlane.net/babelmark2/

Page 23: CommonMark: Markdown done right - Nomad PHP September 2016

LEAGUE/COMMONMARK

A well-written, super-configurable Markdown parser for PHP based on the CommonMark spec.

Page 24: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 25: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 26: CommonMark: Markdown done right - Nomad PHP September 2016

ADDING LEAGUE/COMMONMARK$ composer require league/commonmark:^0.14

<?php$converter = new CommonMarkConverter();echo $converter->convertToHtml('Hello **Nomad PHP!**');

Page 27: CommonMark: Markdown done right - Nomad PHP September 2016

INTEGRATIONS

Page 28: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 29: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 30: CommonMark: Markdown done right - Nomad PHP September 2016

CONVERSION PROCESS

<https://nomadphp.com>

<a href="https://nomadphp.com">https://nomadphp.com

</a>

Page 31: CommonMark: Markdown done right - Nomad PHP September 2016

CONVERSION PROCESS

<https://nomadphp.com>

Markdown Parse

Page 32: CommonMark: Markdown done right - Nomad PHP September 2016

<document> <paragraph> <link destination="https://nomadphp.com"> <text>https://nomadphp.com</text> </link> </paragraph></document>

CONVERSION PROCESS

Markdown AST RenderParse

Page 33: CommonMark: Markdown done right - Nomad PHP September 2016

CONVERSION PROCESS

<a href="https://nomadphp.com">https://nomadphp.com

</a>

Markdown AST HTMLRenderParse

Page 34: CommonMark: Markdown done right - Nomad PHP September 2016

CONVERSION PROCESS

Markdown AST HTMLRenderParse

Add your own custom parser, processor, or renderer

Page 35: CommonMark: Markdown done right - Nomad PHP September 2016

EXAMPLE 1: CUSTOM PARSER<https://nomadphp.com>

<a href="https://nomadphp.com">https://nomadphp.com

</a>

<@colinodell>

<a href="https://twitter.com/colinodell">

@colinodell</a>

Page 36: CommonMark: Markdown done right - Nomad PHP September 2016

class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; }

public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); }}

Page 37: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR

Learning CommonMark with <@colinodell>!

getCharacter()getFirstNonSpaceCharacter()getRemainder()getLine()getPosition()

advance()advanceBy($count)advanceBySpaceOrTab()advanceWhileMatches($char)advanceToFirstNonSpace()

isIndented() isAtEnd() match($regex) peek($count)

Page 38: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR::ADVANCE()

Learning CommonMark with <@colinodell>!

getCharacter()getFirstNonSpaceCharacter()getRemainder()getLine()getPosition()

advance()advanceBy($count)advanceBySpaceOrTab()advanceWhileMatches($char)advanceToFirstNonSpace()

isIndented() isAtEnd() match($regex) peek($count)

Page 39: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR::ADVANCEBY(INT)

Learning CommonMark with <@colinodell>!

getCharacter()getFirstNonSpaceCharacter()getRemainder()getLine()getPosition()

advance()advanceBy($count)advanceBySpaceOrTab()advanceWhileMatches($char)advanceToFirstNonSpace()

isIndented() isAtEnd() match($regex) peek($count)

Page 40: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR::PEEK(INT)

Learning CommonMark with <@colinodell>!

getCharacter()getFirstNonSpaceCharacter()getRemainder()getLine()getPosition()

advance()advanceBy($count)advanceBySpaceOrTab()advanceWhileMatches($char)advanceToFirstNonSpace()

isIndented() isAtEnd() match($regex) peek($count)

Page 41: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR

Learning CommonMark with <@colinodell>!

public function getCharacters() { return ['<'];}

Page 42: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR

Learning CommonMark with <@colinodell>!

getCharacter()getFirstNonSpaceCharacter()getRemainder()getLine()getPosition()

advance()advanceBy($count)advanceBySpaceOrTab()advanceWhileMatches($char)advanceToFirstNonSpace()

isIndented() isAtEnd() match($regex) peek($count)

Page 43: CommonMark: Markdown done right - Nomad PHP September 2016

CURSOR

Learning CommonMark with <@colinodell>!

getCharacter()getFirstNonSpaceCharacter()getRemainder()getLine()getPosition()

advance()advanceBy($count)advanceBySpaceOrTab()advanceWhileMatches($char)advanceToFirstNonSpace()

isIndented() isAtEnd() match($regex) peek($count)

/^<@[A-Za-z0-9_]+>/

Page 44: CommonMark: Markdown done right - Nomad PHP September 2016

CUSTOMIZING LEAGUE/COMMONMARK

class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; }

public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($match = $cursor->match('/^<@[A-Za-z0-9_]+>/')) { // Remove the starting '<@' and ending '>' that were matched $username = substr($match, 2, -1);

$profileUrl = 'https://twitter.com/' . $username; $link = new Link($profileUrl, '@'.$username); $inlineContext->getContainer()->appendChild($link);

return true; }

return false; }}

Page 45: CommonMark: Markdown done right - Nomad PHP September 2016

$environment = Environment::createCommonMarkEnvironment();$environment->addInlineParser( new TwitterHandleParser());

$converter = new CommonMarkConverter($environment);

$html = $converter->convertToHtml( "Follow <@colinodell> on Twitter!");

Page 46: CommonMark: Markdown done right - Nomad PHP September 2016

EXAMPLE 2: CUSTOM AST PROCESSOR<document> <paragraph> <link destination="https://nomadphp.com"> <text>https://nomadphp.com</text> </link> </paragraph></document>

<document> <paragraph> <link destination="https://bit.ly/foo"> <text>https://nomadphp.com</text> </link> </paragraph></document>

Page 47: CommonMark: Markdown done right - Nomad PHP September 2016

class ShortenLinkProcessor implements DocumentProcessorInterface { public function processDocument(Document $document) { $walker = $document->walker(); while ($event = $walker->next()) { if ($event->isEntering() && $event->getNode() instanceof Link) { /** @var Link $linkNode */ $linkNode = $event->getNode(); $originalUrl = $linkNode->getUrl(); $shortUrl = $this->bitly->shorten($originalUrl); $linkNode->setUrl($shortUrl); } } }}

Page 48: CommonMark: Markdown done right - Nomad PHP September 2016

$environment = Environment::createCommonMarkEnvironment();$environment->addDocumentProcessor( new ShortenLinkProcessor());

$converter = new CommonMarkConverter($environment);

$html = $converter->convertToHtml( "Meetings: <https://nomadphp.com/upcoming/>");

EXAMPLE 2: CUSTOM AST PROCESSOR

Page 49: CommonMark: Markdown done right - Nomad PHP September 2016

EXAMPLE 3: CUSTOM RENDERER<document> <paragraph> <text>Hello World!</text> </paragraph> <thematic_break /></document>

<p>Hello World!</p><hr />

<p>Hello World!</p><img src="hr.png" />

Page 50: CommonMark: Markdown done right - Nomad PHP September 2016

EXAMPLE 3: CUSTOM RENDERERclass ImageHorizontalRuleRenderer implements BlockRendererInterface { public function render(...) { return new HtmlElement('img', ['src' => 'hr.png']); }}

Page 51: CommonMark: Markdown done right - Nomad PHP September 2016

$environment = Environment::createCommonMarkEnvironment();$environment->addBlockRenderer( League\CommonMark\Block\Element\ThematicBreak::class, new ImageHorizontalRuleRenderer());

$converter = new CommonMarkConverter($environment);

$html = $converter->convertToHtml("Hello World!\n\n-----");

EXAMPLE 3: CUSTOM RENDERER

Page 52: CommonMark: Markdown done right - Nomad PHP September 2016

BUNDLING INTO AN EXTENSIONclass MyCustomExtension extends Extension { public function getInlineParsers() { return [new TwitterUsernameAutolinkParser()]; }

public function getDocumentProcessors() { return [new ShortenLinkProcessor()]; }

public function getBlockRenderers() { return [new ImageHorizontalRuleRenderer()]; }}

Page 53: CommonMark: Markdown done right - Nomad PHP September 2016

BUNDLING INTO AN EXTENSION

$environment = Environment::createCommonMarkEnvironment();$environment->addExtension(new MyCustomExtension());

$converter = new CommonMarkConverter($environment);

$html = $converter->convertToHtml("...");

Page 54: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 55: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 56: CommonMark: Markdown done right - Nomad PHP September 2016

WELL-TESTED

94% code coverage Functional tests

All 616 spec examples Library of regression tests

Unit tests Cursor Environment Utility classes

Page 57: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 58: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 59: CommonMark: Markdown done right - Nomad PHP September 2016

PERFORMANCE

Parsedown

cebe/markdown

PHP Markdown Extra

league/commonmark

0 10 20 30 40 50 60 70 80 90

league/commonmark is ~35-40ms slower

PHP 5.6 PHP 7.0

Time (ms)

Tips: • Use PHP 7 (50-80% boost)• Choose library based on your

needs• Cache rendered HTML (100%

boost)• Optimize custom functionality

Page 60: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 61: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 62: CommonMark: Markdown done right - Nomad PHP September 2016

STABILITY

Current version: 0.15.0 Conforms to CommonMark spec 0.26 1.0.0 will be released once CommonMark spec is 1.0

No major stability issues Backwards Compatibility Promise:

No BC breaks to CommonMarkConverter class in 0.x Other BC breaks will be documented

Page 63: CommonMark: Markdown done right - Nomad PHP September 2016

FEATURES100% compliance with the CommonMark specEasy to implementEasy to customizeWell-testedDecent performance (Relatively) stable

Page 64: CommonMark: Markdown done right - Nomad PHP September 2016

Installation & Documentation: http://github.com/thephpleague/commonmark

Learn More About CommonMark: http://commonmark.org

Slides / Feedback: https://joind.in/talk/22293

@colinodell