/parsedown-extension-manager

Extend Parsedown in a more SOLID way.

Primary LanguagePHPDo What The F*ck You Want To Public LicenseWTFPL

Parsedown Extensions

A tiny project which introduces a new way to create and use markdown extensions with erusev/parsedown!

Requirements

  • PHP 7.1

Installation

It's a composer installation away!

composer require nessworthy/parsedown-extension-manager

Why?

Each extension added to Parsedown must be done by extending it, registering the extension in a few places, and adding 1-3 new methods in the extended class.

After adding in a few extensions the original way, I grew a little frustrated at how "vertical" the markdown class was becoming.

So, I decided to change the way extensions could be registered.

What's new?

Extensions can be represented as concrete classes of one of two interfaces: ParsedownBlockExtension, or ParsedownInlineExtension.

Each extension is then separately instantiated and registered to your Nessworthy\ParsedownExtensionManager\Parsedown instance by using the added registerBlockExtension or registerInlineExtension and passing your extension through.

Parsedown will use your extensions in the same way as it normally would with the benefit of each extension being isolated and separately extendable!

Usage Example

Step 1: Create your Extension!

Extensions can either implement \Nessworthy\ParsedownExtensionManager\ParsedownInlineExtension or \Nessworthy\ParsedownExtensionManager\ParsedownBlockExtension. Both expect methods which mirror closely to how you would add extensions normally.

<?php

use \Nessworthy\ParsedownExtensionManager\Parsedown;

/**
 * This is an implementation of the "Add Inline Element"  example in the parsedown docs.
 * @see https://github.com/erusev/parsedown/wiki/Tutorial:-Create-Extensions#add-inline-element
 */
class ExampleInlineExtension implements \Nessworthy\ParsedownExtensionManager\ParsedownInlineExtension
{
    public function getStartingCharacter(): string
    {
        return '{';
    }
    
    public function run(array $excerpt): ?array
    {
        if (preg_match('/^{c:([#\w]\w+)}(.*?){\/c}/', $excerpt['text'], $matches)) {
            return [
                'extent' => strlen($matches[0]), 
                'element' => [
                    'name' => 'span',
                    'text' => $matches[2],
                    'attributes' => [
                        'style' => 'color: ' . $matches[1],
                    ],
                ],

            ];
        }
        
        return null;
    }
}

Step 2: Instantiate & Register your Extension!

<?php

// Create your Parsedown instance.
$parsedown = new \Nessworthy\ParsedownExtensionManager\Parsedown();

// Register your Parsedown extensions.
$parsedown->registerInlineExtension(new ExampleInlineExtension());

// Use Parsedown as you normally would!
$parsedown->parse('Hello {c:#FF00000}world{/c}!');
// "<p>Hello <span style="color: #FF0000">world!</span></p>

What's the catch?

Mm, good question. Let me know and I'll put it here!

Parsedown is still fundamentally the same, with the added functionality of seperate extension registration. You can still extend this class and add parsedown extensions the original way!

Ignorance aside, this library does leverage __call and tries to do so as sanely as possible.

In addition, because this is yet another extension of Parsedown, it won't work out of the box with any of the other Parsedown extensions out there. However, it's possible to simply convert other Parsedown extensions to work with this library instead!

Distributing Extensions

If you fancy creating and sharing extensions of your own, feel free to use the nessworthy\parsedown-extension metapackage instead which only contains the interfaces you need to implement.

Extension Docs

I won't go into detail on how to write Parsedown Extensions here - the parsedown documentation does a good job of explaining what you need to do to add markdown extensions. The returned data from your methods should still be the same as if you had just extended markdown in the original way.

ParsedownInlineExtension

Inline extensions require two methods:

  • getStartingCharacter(): string - Expects you return a single character which tells Parsedown to start using your extension when it's found in the markdown body.
  • run(array $excerpt): ?array - The equivalent of Parsedown's inlineYourExtension($excerpt) extension method.

ParsedownBlockExtension

Block extensions always require the following four methods:

  • getStartingCharacter(): string - Expects you return a single character which tells Parsedown to start using your extension when it's found in the markdown body.
  • start($line, array $block = null): ?array - The equivalent of Parsedown's blockYourExtension($line, $block) method.
  • continue($line, array $block): ?array - The equivalent of Parsedown's blockYourExtensionContinue($line, $block) method.
  • complete(array $block): ?array - The equivalent of Parsedown's blockYourExtensionComplete($block) method.

What's next?

  • More accurate written tests! (e.g. to account for pre-registered inline special characters and not yet registered ones)