commonmark: markdown done right

44
CommonMark Markdown done right Colin O’Dell @colinodell

Upload: colin-odell

Post on 16-Feb-2017

218 views

Category:

Software


1 download

TRANSCRIPT

CommonMarkMarkdown done right

Colin O’Dell

@colinodell

COLIN O’DELL

Creator & Maintainer of league/commonmark

Lead Web Developer at Unleashed Technologies

Author of PHP 7 Migration Guide e-book

@colinodell

www.colinodell.com

LEAGUE/COMMONMARK

A well-written, super-configurable Markdown

parser for PHP based on the CommonMark

spec.

COMMONMARK IS…

A strongly defined, highly compatible specification of Markdown.

Written by people from Github, StackOverflow, Reddit, and others.

Spec includes:

Strict rules (precedence, parsing order, handling edge cases)

Specific definitions (ex: “whitespace”, “punctuation”)

616 examples

WHY IS IT NEEDED?

*I love Markdown*

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

WHY IS IT NEEDED?

*I *love* Markdown*

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

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/

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

ADDING LEAGUE/COMMONMARK

$ composer require league/commonmark:^0.13

<?php$converter = new CommonMarkConverter();echo $converter->convertToHtml('Hello **php[tek]!**');

INTEGRATIONS

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

CONVERSION PROCESS

<https://tek.phparch.com>

<a href="https://tek.phparch.com">https://tek.phparch.com

</a>

CONVERSION PROCESS

<https://tek.phparch.com>

Markdown Parse

<document><paragraph><link destination="https://tek.phparch.com">

<text>https://tek.phparch.com</text></link>

</paragraph></document>

CONVERSION PROCESS

Markdown AST RenderParse

CONVERSION PROCESS

<a href="https://tek.phparch.com">https://tek.phparch.com

</a>

Markdown AST HTMLRenderParse

CONVERSION PROCESS

Markdown AST HTMLRenderParse

Add your own custom parser, processor, or renderer

EXAMPLE 1: CUSTOM PARSER

<https://tek.phparch.com>

<a href="https://tek.phparch.com">https://tek.phparch.com

</a>

<@colinodell>

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

</a>

class TwitterUsernameAutolinkParser extends AbstractInlineParser {public function getCharacters() {

return ['<'];}

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

}}

CURSOR

Learning CommonMark with <@colinodell>!

public function getCharacters() {

return ['<'];}

CURSOR

Learning CommonMark with <@colinodell>!

getCharacter()

getFirstNonSpaceCharacter()

getRemainder()

getLine()

getPosition()

advance()

advanceBy($count)

advanceBySpaceOrTab()

advanceWhileMatches($char)

advanceToFirstNonSpace()

isIndented()

isAtEnd()

match($regex)

peek($count)

CURSOR

Learning CommonMark with <@colinodell>!

getCharacter()

getFirstNonSpaceCharacter()

getRemainder()

getLine()

getPosition()

advance()

advanceBy($count)

advanceBySpaceOrTab()

advanceWhileMatches($char)

advanceToFirstNonSpace()

isIndented()

isAtEnd()

match($regex)

peek($count)

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_]+>/

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;}

}

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

new TwitterHandleParser());

$converter = new CommonMarkConverter($environment);

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

);

EXAMPLE 2: CUSTOM AST PROCESSOR

<document><paragraph><link destination="https://tek.phparch.com">

<text>https://tek.phparch.com</text></link>

</paragraph></document>

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

<text>https://tek.phparch.com</text></link>

</paragraph></document>

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);

}

}

}

}

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

new ShortenLinkProcessor());

$converter = new CommonMarkConverter($environment);

$html = $converter->convertToHtml("Schedule: <https://tek.phparch.com/schedule>"

);

EXAMPLE 2: CUSTOM AST PROCESSOR

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" />

EXAMPLE 3: CUSTOM RENDERER

class ImageHorizontalRuleRenderer implements BlockRendererInterface {

public function render(...) {

return new HtmlElement('img', ['src' => 'hr.png']);

}

}

$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

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

WELL-TESTED

94% code coverage

Functional tests

All 616 spec examples

Library of regression tests

Unit tests

Cursor

Environment

Utility classes

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

PERFORMANCE

0 20 40 60 80 100

Parsedown

cebe/markdown

PHP Markdown Extra

league/commonmark

Time (ms)

league/commonmark is ~35-40ms slower

PHP 5.6 PHP 7.0

Tips:

• Choose library based on your needs

• Cache rendered HTML (100% boost)

• Use PHP 7 (50-80% boost)

• Optimize custom functionality

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

STABILITY

Current version: 0.13.3

Conforms to CommonMark spec 0.25

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

FEATURES

100% compliance with the CommonMark spec

Easy to implement

Easy to customize

Well-tested

Decent performance

(Relatively) stable

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

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

Slides / Feedback:https://joind.in/talk/96dc9

@colinodell