8000 First · torchlight-api/torchlight-commonmark-php@2d70fe7 · GitHub
Skip to content

Commit 2d70fe7

Browse files
committed
First
0 parents  commit 2d70fe7

4 files changed

Lines changed: 177 additions & 0 deletions

File tree

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# osx noise
2+
.DS_Store
3+
profile
4+
5+
# xcode noise
6+
build/*
7+
*.mode1
8+
*.mode1v3
9+
*.mode2v3
10+
*.perspective
11+
*.perspectivev3
12+
*.pbxuser
13+
*.xcworkspace
14+
xcuserdata
15+
16+
# svn & cvs
17+
.svn
18+
CVS

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 torchlight-api
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

composer.json

Lines changed: 38 additions & 0 deletions
12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "torchlight/commonmark-php",
3+
"description": "A Commonmark extension for Torchlight, a syntax highlighting API.",
4+
"homepage": "https://github.com/hammerstonedev/torchlight-commonmark-php",
5+
"type": "library",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Aaron Francis",
10+
"email": "aaron@hammerstone.dev"
11+
}
+
],
13+
"require": {
14+
"php": "^7.2|^8.0",
15+
"torchlight/laravel": "^0.0.1",
16+
"league/commonmark": "^1.5"
17+
},
18+
"require-dev": {
19+
"phpunit/phpunit": "^8.4"
20+
},
21+
"autoload": {
22+
"psr-4": {
23+
"Hammerstone\\Torchlight\\Commonmark\\": "src/"
24+
}
25+
},
26+
"autoload-dev": {
27+
"psr-4": {
28+
"Hammerstone\\Torchlight\\Commonmark\\Tests\\": "tests/"
29+
}
30+
},
31+
"extra": {
32+
"laravel": {
33+
"providers": [
34+
"Hammerstone\\Torchlight\\TorchlightServiceProvider"
35+
]
36+
}
37+
}
38+
}

src/TorchlightExtension.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Hammerstone\Torchlight\Commonmark;
4+
5+
use Hammerstone\Torchlight\Block;
6+
use Hammerstone\Torchlight\Client;
7+
use League\CommonMark\Block\Element\AbstractBlock;
8+
use League\CommonMark\Block\Element\FencedCode;
9+
use League\CommonMark\Block\Element\IndentedCode;
10+
use League\CommonMark\Block\Renderer\BlockRendererInterface;
11+
use League\CommonMark\ConfigurableEnvironmentInterface;
12+
use League\CommonMark\ElementRendererInterface;
13+
use League\CommonMark\Event\DocumentParsedEvent;
14+
use League\CommonMark\Extension\ExtensionInterface;
15+
use League\CommonMark\Util\Xml;
16+
17+
class TorchlightExtension implements ExtensionInterface, BlockRendererInterface
18+
{
19+
public static $torchlightBlocks = [];
20+
21+
public function register(ConfigurableEnvironmentInterface $environment)
22+
{
23+
// We start by walking the document immediately after it's parsed
24+
// to gather up all the code blocks and send off our requests.
25+
$environment->addEventListener(DocumentParsedEvent::class, [$this, 'onDocumentParsed']);
26+
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->addBlockRenderer(FencedCode::class, $this, 10);
32+
$environment->addBlockRenderer(IndentedCode::class, $this, 10);
33+
34+
}
35+
36+
/**
37+
* @param DocumentParsedEvent $event
38+
*/
39+
public function onDocumentParsed(DocumentParsedEvent $event)
40+
{
41+
$walker = $event->getDocument()->walker();
42+
43+
while ($event = $walker->next()) {
44+
$node = $event->getNode();
45+
46+
// Only look for code nodes, and only process them upon entering.
47+
if (!$this->isCodeNode($node) || !$event->isEntering()) {
48+
continue;
49+
}
50+
51+
$block = $this->makeTorchlightBlock($node);
52+
53+
// Set by hash instead of ID, because we'll be remaking all the
54+
// blocks in the `render` function so the ID will be different,
55+
// but the hash will always remain the same.
56+
static::$torchlightBlocks[$block->hash()] = $block;
57+
}
58+
59+
// All we need to do is fire the request, which will store
60+
// the results in the cache. In the render function we
61+
// use that cached value.
62+
(new Client)->highlight(static::$torchlightBlocks);
63+
}
64+
65+
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
66+
{
67+
$hash = $this->makeTorchlightBlock($block)->hash();
68+
69+
if (array_key_exists(static::$torchlightBlocks, $hash)) {
70+
return static::$torchlightBlocks[$hash]->html;
71+
}
72+
}
73+
74+
protected function makeTorchlightBlock($node)
75+
{
76+
return Block::make()
77+
->setLanguage($this->getLanguage($node))
78+
->setCode($node->getStringContent());
79+
}
80+
81+
protected function isCodeNode($node)
82+
{
83+
return $node instanceof FencedCode || $node instanceof IndentedCode;
84+
}
85+
86+
protected function getLanguage($node)
87+
{
88+
if (!$this->isCodeNode($node) || $node instanceof IndentedCode) {
89+
return null;
90+
}
91+
92+
$infoWords = $node->getInfoWords();
93+
94+
if (empty($infoWords) || empty($infoWords[0])) {
95+
return null;
96+
}
97+
98+
return Xml::escape($infoWords[0], true);
99+
}
100+
}

0 commit comments

Comments
 (0)