Skip to content
This repository has been archived by the owner on Jul 1, 2023. It is now read-only.

Commit

Permalink
Introduce markdown environment
Browse files Browse the repository at this point in the history
  • Loading branch information
azjezz committed Jul 3, 2021
1 parent c9384ee commit e0e9e34
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 19 deletions.
63 changes: 44 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# FBMarkdown

FBMarkdown is an extensible parser and renderer for [GitHub Flavored Markdown](https://github.github.com/gfm/),
written in [Hack](http://hacklang.org).

Expand All @@ -22,26 +23,24 @@ FBMarkdown exists to address all of these goals.

## Requirements

- HHVM 3.24 or above.
- HHVM 3.56 or above.
- [hhvm-autoload](https://github.com/hhvm/hhvm-autoload)

## Installing FBMarkdown

hhvm composer.phar require facebook/fbmarkdown
```sh
composer require facebook/fbmarkdown
```

## Using FBMarkdown

```Hack
```hack
use namespace Facebook\Markdown;
function render(string $markdown): string {
$ast = Markdown\parse(new Markdown\ParserContext(), $markdown);
$environment = Markdown\html_environment();
$html = (new Markdown\HTMLRenderer(
new Markdown\RenderContext()
))->render($ast);
return $html;
return $environment->convert($markdown);
}
```

Expand All @@ -54,8 +53,9 @@ FBMarkdown currently supports three types of Markdown sources, with plans to exp
- __User-generated content__: All HTML is disabled, as are links and images regardless of schemes. If links are re-enabled, `rel="nofollow ugc"` will be added to all links.

To make changes to these default settings:

- You may alter the keyset of allowed URI schemes by calling the Parser function `setAllowedURISchemes()`.
- You may enable embedded HTML by calling the Parser function `enableHTML_UNSAFE()`. __N.B.: For complete compatibility with GitHub Flavored Markdown, support for embedded HTML must be enabled.__
- You may enable embedded HTML by calling the Parser function `enableHTML_UNSAFE()`. __N.B.: For complete compatibility with GitHub Flavored Markdown, support for embedded HTML must be enabled.__
- You may disable image filtering by calling the Renderer function `disableImageFiltering()`.
- You may add `rel="nofollow ugc"` to all links by calling the Renderer function `addNoFollowUGCAllLinks()`.

Expand Down Expand Up @@ -89,27 +89,52 @@ Extend `Facebook\Markdown\Inlines\Inline` or a subclass, and pass your classname
`$render_ctx->getInlineContext()->prependInlineTypes(...)`.

There are then several approaches to rendering:
- instantiate your subclass, and add support for it to a custom renderer
- instantiate your subclass, and make it implement the `Facebook\Markdown\RenderableAsHTML` interface
- if it could be replaced with several existing inlines, return a
`Facebook\Markdown\Inlines\InlineSequence`, then you won't need to extend the renderer.

- instantiate your subclass, and add support for it to a custom renderer
- instantiate your subclass, and make it implement the `Facebook\Markdown\RenderableAsHTML` interface
- if it could be replaced with several existing inlines, return a
`Facebook\Markdown\Inlines\InlineSequence`, then you won't need to extend the renderer.

#### Blocks

You will need to implement the `Facebook\Markdown\UnparsedBlocks\BlockProducer` interface, and pass your classname
to `$render_ctx->getBlockContext()->prependBlockTypes(...)`.

There are then several approaches to rendering:
- create a subclass of `Block`, and add support for it to a custom renderer
- create a subclass of `Block`, and make it implement the `Facebook\Markdown\RenderableAsHTML` interface
- if it could be replaced with several existing blocks, return a
`Facebook\Markdown\Blocks\BlockSequence`
- if it could be replaced with a paragraph of inlines, return a `Facebook\Markdown\Blocks\InlineSequenceBlock`

- create a subclass of `Block`, and add support for it to a custom renderer
- create a subclass of `Block`, and make it implement the `Facebook\Markdown\RenderableAsHTML` interface
- if it could be replaced with several existing blocks, return a
`Facebook\Markdown\Blocks\BlockSequence`
- if it could be replaced with a paragraph of inlines, return a `Facebook\Markdown\Blocks\InlineSequenceBlock`

### Transforming The AST

Extend `Facebook\Markdown\RenderFilter`, and pass it to `$render_ctx->appendFilters(...)`.

## FBMarkdown Environment

FBMarkdown provides a `Environment` class which acts as a glow between the parser and renderer.

By default, FBMarkdown ships with 2 functions which allow you to create an HTML environment, and unsafe HTML environment.

- `Facebook\Markdown\html_environment`
- `Facebook\Markdown\unsafe_html_environment`

The `Environment` makes it easy to extend the parser, and renderer via the `Plugin` system.

```hack
$environment = Markdown\html_environment();
$environment->use(new UserMentionPlugin());
$environment->user(new ExternalLinkPlugin());
$html = $environment->convert($markdown);
```

each plugin must extend the `Facebook\Markdown\Plugin` class, and can provide
multiple [block producers](#blocks), [inline types](#inlines), and [render filters](#transforming-the-ast).

### Examples

The Hack and HHVM documentation uses most of these approaches; see:
Expand Down
63 changes: 63 additions & 0 deletions src/Environment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?hh // strict
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

namespace Facebook\Markdown;

final class Environment<T> {
public function __construct(
private ParserContext $parser,
private RenderContext $context,
private Renderer<T> $renderer,
) {}

public function getParser(): ParserContext {
return $this->parser;
}

public function getContext(): RenderContext {
return $this->context;
}

public function getRenderer(): Renderer<T> {
return $this->renderer;
}

public function getInlineContext(): Inlines\Context {
return $this->parser->getInlineContext();
}

public function getBlockContext(): UnparsedBlocks\Context {
return $this->parser->getBlockContext();
}

public function getFilters(): Container<RenderFilter> {
return $this->context->getFilters();
}

public function use(Plugin $plugin): void {
$this->getBlockContext()
->prependBlockTypes(...$plugin->getBlockProducers());
$this->getInlineContext()
->prependInlineTypes(...$plugin->getInlineTypes());
$this->getContext()->appendFilters(...$plugin->getRenderFilters());
}

public function parse(string $markdown): ASTNode {
return parse($this->parser, $markdown);
}

public function render(ASTNode $markdown): T {
return $this->renderer->render($markdown);
}

public function convert(string $markdown): T {
return $this->render($this->parse($markdown));
}
}
35 changes: 35 additions & 0 deletions src/Plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?hh // strict
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

namespace Facebook\Markdown;

abstract class Plugin {
/**
* @see Facebook\Markdown\RenderContext::appendFilters()
*/
public function getRenderFilters(): Container<RenderFilter> {
return vec[];
}

/**
* @see Facebook\Markdown\Inlines\Context::prependInlineTypes()
*/
public function getInlineTypes(): Container<classname<Inlines\Inline>> {
return vec[];
}

/**
* @see Facebook\Markdown\UnparsedBlocks\Context::prependBlockTypes()
*/
public function getBlockProducers(
): Container<classname<UnparsedBlocks\BlockProducer>> {
return vec[];
}
}
29 changes: 29 additions & 0 deletions src/html_environment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?hh // strict
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

namespace Facebook\Markdown;

function html_environment(): Environment<string> {
$parser = new ParserContext();
$context = new RenderContext();
$renderer = new HTMLRenderer($context);

return new Environment($parser, $context, $renderer);
}

function unsafe_html_environment(): Environment<string> {
$parser = new ParserContext();
$context = new RenderContext();
$renderer = new HTMLRenderer($context);

$parser->enableHTML_UNSAFE();

return new Environment($parser, $context, $renderer);
}

0 comments on commit e0e9e34

Please sign in to comment.