Skip to content

Commit d95afdc

Browse files
committed
Refactor for V1/V2 common behavior
1 parent cfe7aab commit d95afdc

File tree

8 files changed

+352
-521
lines changed

8 files changed

+352
-521
lines changed
Lines changed: 98 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,37 @@
33
namespace Torchlight\Commonmark;
44

55
use Illuminate\Support\Str;
6-
use League\CommonMark\Environment\EnvironmentBuilderInterface;
6+
use League\CommonMark\Block\Element\AbstractBlock;
7+
use League\CommonMark\Block\Element\FencedCode;
8+
use League\CommonMark\Block\Element\IndentedCode;
9+
use League\CommonMark\Block\Renderer\BlockRendererInterface;
10+
use League\CommonMark\ConfigurableEnvironmentInterface;
11+
use League\CommonMark\ElementRendererInterface;
712
use League\CommonMark\Event\DocumentParsedEvent;
8-
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
9-
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
10-
use League\CommonMark\Extension\ExtensionInterface;
11-
use League\CommonMark\Node\Node;
12-
use League\CommonMark\Renderer\ChildNodeRendererInterface;
13-
use League\CommonMark\Renderer\NodeRendererInterface;
1413
use League\CommonMark\Util\Xml;
1514
use Torchlight\Block;
1615
use Torchlight\Torchlight;
1716

18-
class TorchlightExtensionV2 implements ExtensionInterface, NodeRendererInterface
17+
abstract class BaseExtension
1918
{
19+
/**
20+
* @var array
21+
*/
2022
public static $torchlightBlocks = [];
2123

22-
public function register(EnvironmentBuilderInterface $environment): void
23-
{
24-
// We start by walking the document immediately after it's parsed
25-
// to gather up all the code blocks and send off our requests.
26-
$environment->addEventListener(DocumentParsedEvent::class, [$this, 'onDocumentParsed']);
27-
28-
// After the document is parsed, it's rendered. We register our
29-
// renderers with a higher priority than the default ones,
30-
// and we'll fetch the blocks straight from the cache.
31-
$environment->addRenderer(FencedCode::class, $this, 10);
32-
$environment->addRenderer(IndentedCode::class, $this, 10);
33-
}
24+
/**
25+
* @var callable
26+
*/
27+
protected $customBlockRenderer;
3428

29+
/**
30+
* @param DocumentParsedEvent $event
31+
*/
3532
public function onDocumentParsed(DocumentParsedEvent $event)
3633
{
3734
$walker = $event->getDocument()->walker();
3835

3936
while ($event = $walker->next()) {
40-
/** @var FencedCode|IndentedCode $node */
4137
$node = $event->getNode();
4238

4339
// Only look for code nodes, and only process them upon entering.
@@ -59,31 +55,71 @@ public function onDocumentParsed(DocumentParsedEvent $event)
5955
Torchlight::highlight(static::$torchlightBlocks);
6056
}
6157

62-
public function render(Node $node, ChildNodeRendererInterface $childRenderer)
63-
{
64-
$hash = $this->makeTorchlightBlock($node)->hash();
65-
66-
if (array_key_exists($hash, static::$torchlightBlocks)) {
67-
$renderer = $this->customBlockRenderer ?? $this->defaultBlockRenderer();
68-
69-
return call_user_func($renderer, static::$torchlightBlocks[$hash]);
70-
}
71-
}
72-
58+
/**
59+
* @param callable $callback
60+
* @return $this
61+
*/
7362
public function useCustomBlockRenderer($callback)
7463
{
7564
$this->customBlockRenderer = $callback;
7665

7766
return $this;
7867
}
7968

69+
/**
70+
* @return \Closure
71+
*/
8072
public function defaultBlockRenderer()
8173
{
8274
return function (Block $block) {
8375
return "<pre><code class='{$block->classes}' style='{$block->styles}'>{$block->highlighted}</code></pre>";
8476
};
8577
}
8678

79+
/**
80+
* @return array
81+
*/
82+
abstract protected function codeNodes();
83+
84+
/**
85+
* @param $node
86+
* @return string
87+
*/
88+
abstract protected function getLiteralContent($node);
89+
90+
/**
91+
* Bind into a Commonmark V1 or V2 environment.
92+
*
93+
* @param $environment
94+
* @param string $renderMethod
95+
*/
96+
protected function bind($environment, $renderMethod)
97+
{
98+
// We start by walking the document immediately after it's parsed
99+
// to gather up all the code blocks and send off our requests.
100+
$environment->addEventListener(DocumentParsedEvent::class, [$this, 'onDocumentParsed']);
101+
102+
foreach ($this->codeNodes() as $blockType) {
103+
// After the document is parsed, it's rendered. We register our
104+
// renderers with a higher priority than the default ones,
105+
// and we'll fetch the blocks straight from the cache.
106+
$environment->{$renderMethod}($blockType, $this, 10);
107+
}
108+
}
109+
110+
/**
111+
* @param $node
112+
* @return bool
113+
*/
114+
protected function isCodeNode($node)
115+
{
116+
return in_array(get_class($node), $this->codeNodes());
117+
}
118+
119+
/**
120+
* @param $node
121+
* @return Block
122+
*/
87123
protected function makeTorchlightBlock($node)
88124
{
89125
return Block::make()
@@ -92,15 +128,30 @@ protected function makeTorchlightBlock($node)
92128
->code($this->getContent($node));
93129
}
94130

95-
protected function isCodeNode($node)
131+
/**
132+
* @param $node
133+
* @return string
134+
*/
135+
protected function renderNode($node)
96136
{
97-
return $node instanceof FencedCode || $node instanceof IndentedCode;
137+
$hash = $this->makeTorchlightBlock($node)->hash();
138+
139+
if (array_key_exists($hash, static::$torchlightBlocks)) {
140+
$renderer = $this->customBlockRenderer ?? $this->defaultBlockRenderer();
141+
142+
return call_user_func($renderer, static::$torchlightBlocks[$hash]);
143+
}
98144
}
99145

146+
/**
147+
* @param $node
148+
* @return string
149+
*/
100150
protected function getContent($node)
101151
{
102-
$content = $node->getLiteral();
152+
$content = $this->getLiteralContent($node);
103153

154+
// Check for our file loading convention.
104155
if (!Str::startsWith($content, '<<<')) {
105156
return $content;
106157
}
@@ -115,6 +166,10 @@ protected function getContent($node)
115166
return Torchlight::processFileContents($file) ?: $content;
116167
}
117168

169+
/**
170+
* @param $node
171+
* @return array|mixed|null
172+
*/
118173
protected function getInfo($node)
119174
{
120175
if (!$this->isCodeNode($node) || $node instanceof IndentedCode) {
@@ -126,13 +181,21 @@ protected function getInfo($node)
126181
return empty($infoWords) ? [] : $infoWords;
127182
}
128183

184+
/**
185+
* @param $node
186+
* @return string|null
187+
*/
129188
protected function getLanguage($node)
130189
{
131190
$language = $this->getInfo($node)[0];
132191

133192
return $language ? Xml::escape($language, true) : null;
134193
}
135194

195+
/**
196+
* @param $node
197+
* @return string
198+
*/
136199
protected function getTheme($node)
137200
{
138201
foreach ($this->getInfo($node) as $item) {

src/TorchlightExtension.php

Lines changed: 8 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -2,147 +2,13 @@
22

33
namespace Torchlight\Commonmark;
44

5-
use Illuminate\Support\Str;
6-
use League\CommonMark\Block\Element\AbstractBlock;
7-
use League\CommonMark\Block\Element\FencedCode;
8-
use League\CommonMark\Block\Element\IndentedCode;
9-
use League\CommonMark\Block\Renderer\BlockRendererInterface;
10-
use League\CommonMark\ConfigurableEnvironmentInterface;
11-
use League\CommonMark\ElementRendererInterface;
12-
use League\CommonMark\Event\DocumentParsedEvent;
13-
use League\CommonMark\Extension\ExtensionInterface;
14-
use League\CommonMark\Util\Xml;
15-
use Torchlight\Block;
16-
use Torchlight\Torchlight;
17-
18-
class TorchlightExtension implements ExtensionInterface, BlockRendererInterface
5+
/**
6+
* Class TorchlightExtension
7+
* @deprecated use \Torchlight\Commonmark\V1\TorchlightExtension instead.
8+
* @see \Torchlight\Commonmark\V1\TorchlightExtension
9+
* @package Torchlight\Commonmark
10+
*/
11+
class TorchlightExtension extends \Torchlight\Commonmark\V1\TorchlightExtension
1912
{
20-
public static $torchlightBlocks = [];
21-
22-
protected $customBlockRenderer;
23-
24-
public function register(ConfigurableEnvironmentInterface $environment)
25-
{
26-
// We start by walking the document immediately after it's parsed
27-
// to gather up all the code blocks and send off our requests.
28-
$environment->addEventListener(DocumentParsedEvent::class, [$this, 'onDocumentParsed']);
29-
30-
// After the document is parsed, it's rendered. We register our
31-
// renderers with a higher priority than the default ones,
32-
// and we'll fetch the blocks straight from the cache.
33-
$environment->addBlockRenderer(FencedCode::class, $this, 10);
34-
$environment->addBlockRenderer(IndentedCode::class, $this, 10);
35-
}
36-
37-
/**
38-
* @param DocumentParsedEvent $event
39-
*/
40-
public function onDocumentParsed(DocumentParsedEvent $event)
41-
{
42-
$walker = $event->getDocument()->walker();
43-
44-
while ($event = $walker->next()) {
45-
$node = $event->getNode();
46-
47-
// Only look for code nodes, and only process them upon entering.
48-
if (!$this->isCodeNode($node) || !$event->isEntering()) {
49-
continue;
50-
}
51-
52-
$block = $this->makeTorchlightBlock($node);
53-
54-
// Set by hash instead of ID, because we'll be remaking all the
55-
// blocks in the `render` function so the ID will be different,
56-
// but the hash will always remain the same.
57-
static::$torchlightBlocks[$block->hash()] = $block;
58-
}
59-
60-
// All we need to do is fire the request, which will store
61-
// the results in the cache. In the render function we
62-
// use that cached value.
63-
Torchlight::highlight(static::$torchlightBlocks);
64-
}
65-
66-
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
67-
{
68-
$hash = $this->makeTorchlightBlock($block)->hash();
69-
70-
if (array_key_exists($hash, static::$torchlightBlocks)) {
71-
$renderer = $this->customBlockRenderer ?? $this->defaultBlockRenderer();
72-
73-
return call_user_func($renderer, static::$torchlightBlocks[$hash]);
74-
}
75-
}
76-
77-
public function useCustomBlockRenderer($callback)
78-
{
79-
$this->customBlockRenderer = $callback;
80-
81-
return $this;
82-
}
83-
84-
public function defaultBlockRenderer()
85-
{
86-
return function (Block $block) {
87-
return "<pre><code class='{$block->classes}' style='{$block->styles}'>{$block->highlighted}</code></pre>";
88-
};
89-
}
90-
91-
protected function makeTorchlightBlock($node)
92-
{
93-
return Block::make()
94-
->language($this->getLanguage($node))
95-
->theme($this->getTheme($node))
96-
->code($this->getContent($node));
97-
}
98-
99-
protected function isCodeNode($node)
100-
{
101-
return $node instanceof FencedCode || $node instanceof IndentedCode;
102-
}
103-
104-
protected function getContent($node)
105-
{
106-
$content = $node->getStringContent();
107-
108-
if (!Str::startsWith($content, '<<<')) {
109-
return $content;
110-
}
111-
112-
$file = trim(Str::after($content, '<<<'));
113-
114-
// It must be only one line, because otherwise it might be a heredoc.
115-
if (count(explode("\n", $file)) > 1) {
116-
return $content;
117-
}
118-
119-
return Torchlight::processFileContents($file) ?: $content;
120-
}
121-
122-
protected function getInfo($node)
123-
{
124-
if (!$this->isCodeNode($node) || $node instanceof IndentedCode) {
125-
return null;
126-
}
127-
128-
$infoWords = $node->getInfoWords();
129-
130-
return empty($infoWords) ? [] : $infoWords;
131-
}
132-
133-
protected function getLanguage($node)
134-
{
135-
$language = $this->getInfo($node)[0];
136-
137-
return $language ? Xml::escape($language, true) : null;
138-
}
139-
140-
protected function getTheme($node)
141-
{
142-
foreach ($this->getInfo($node) as $item) {
143-
if (Str::startsWith($item, 'theme:')) {
144-
return Str::after($item, 'theme:');
145-
}
146-
}
147-
}
13+
// @see \Torchlight\Commonmark\V1\TorchlightExtension
14814
}

0 commit comments

Comments
 (0)