Add server side highlighting, fixes #139

This commit is contained in:
Stéphane Goetz 2019-09-20 23:20:49 +02:00
parent 57b3848430
commit 92db87e00d
5 changed files with 164 additions and 18 deletions

View File

@ -27,7 +27,8 @@
"symfony/polyfill-intl-icu": "^1.10",
"symfony/process": "^4.0",
"webuni/commonmark-table-extension": "0.9.*",
"webuni/front-matter": "^1.0.0"
"webuni/front-matter": "^1.0.0",
"scrivo/highlight.php": "^9.15"
},
"suggest":{
"ext-intl": "Allows to translate the modified at date"

81
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c2a8f08a3622dfcd70136f48e4f5a0ce",
"content-hash": "3df8e368fea890f0e123788f898cddc9",
"packages": [
{
"name": "guzzlehttp/guzzle",
@ -244,9 +244,9 @@
"authors": [
{
"name": "Colin O'Dell",
"role": "Lead Developer",
"email": "colinodell@gmail.com",
"homepage": "https://www.colinodell.com"
"homepage": "https://www.colinodell.com",
"role": "Lead Developer"
}
],
"description": "PHP Markdown parser based on the CommonMark spec",
@ -500,6 +500,73 @@
"description": "A polyfill for getallheaders.",
"time": "2016-02-11T07:05:27+00:00"
},
{
"name": "scrivo/highlight.php",
"version": "v9.15.10.0",
"source": {
"type": "git",
"url": "https://github.com/scrivo/highlight.php.git",
"reference": "9ad3adb4456dc91196327498dbbce6aa1ba1239e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/scrivo/highlight.php/zipball/9ad3adb4456dc91196327498dbbce6aa1ba1239e",
"reference": "9ad3adb4456dc91196327498dbbce6aa1ba1239e",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7",
"symfony/finder": "^2.8"
},
"suggest": {
"ext-dom": "Needed to make use of the features in the utilities namespace"
},
"type": "library",
"autoload": {
"psr-0": {
"Highlight\\": "",
"HighlightUtilities\\": ""
},
"files": [
"HighlightUtilities/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Geert Bergman",
"role": "Project Author",
"homepage": "http://www.scrivo.org/"
},
{
"name": "Vladimir Jimenez",
"role": "Contributor",
"homepage": "https://allejo.io"
},
{
"name": "Martin Folkers",
"role": "Contributor",
"homepage": "https://twobrain.io"
}
],
"description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js",
"keywords": [
"code",
"highlight",
"highlight.js",
"highlight.php",
"syntax"
],
"time": "2019-08-27T04:27:48+00:00"
},
{
"name": "symfony/console",
"version": "v4.3.4",
@ -1503,8 +1570,8 @@
"authors": [
{
"name": "Frank Kleine",
"homepage": "http://frankkleine.de/",
"role": "Developer"
"role": "Developer",
"homepage": "http://frankkleine.de/"
}
],
"description": "Virtual file system to mock the real file system in unit tests.",
@ -2018,8 +2085,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
"role": "lead",
"email": "sebastian@phpunit.de"
}
],
"description": "Utility class for timing",

View File

@ -2,9 +2,15 @@ Highlight.js highlights syntax in code examples on blogs, forums and in fact on
You can even use [Github Flavored Markdown](!Features/CommonMark_compliant)
We also use the [scrivo/highlight.php](https://github.com/scrivo/highlight.php) package to highlight on rendering when possible.
Highlight.js is a powerful but heavy library, since we don't know which languages you'll use we included all of them.
The good news is; if you use a fenced code block (` ``` `) with the language defined, the rendering is done on server side and entirely skips loading Highlight.js on the page. While still having the same end-result on your code.
**Python**
```python
@requires_authorization
def somefunc(param1='', param2=0):
r'''A docstring'''
@ -16,7 +22,7 @@ You can even use [Github Flavored Markdown](!Features/CommonMark_compliant)
>>> message = '''interpreter
... prompt'''
```
**Python's profiler output**

View File

@ -9,6 +9,8 @@ class CommonMarkConverter extends \Todaymade\Daux\ContentTypes\Markdown\CommonMa
{
parent::extendEnvironment($environment, $config);
$environment->addBlockRenderer('FencedCode', new FencedCodeRenderer());
$environment->addDocumentProcessor(new TOC\Processor($config));
$environment->addBlockRenderer('Todaymade\Daux\ContentTypes\Markdown\TableOfContents', new TOC\Renderer($config));
}

View File

@ -0,0 +1,70 @@
<?php namespace Todaymade\Daux\Format\HTML\ContentTypes\Markdown;
use Highlight\Highlighter;
use League\CommonMark\Block\Element\AbstractBlock;
use League\CommonMark\Block\Element\FencedCode;
use League\CommonMark\Block\Renderer\BlockRendererInterface;
use League\CommonMark\ElementRendererInterface;
use League\CommonMark\HtmlElement;
use League\CommonMark\Util\Xml;
class FencedCodeRenderer implements BlockRendererInterface
{
function __construct() {
$this->hl = new Highlighter();
}
/**
* @param AbstractBlock $block
* @param HtmlRendererInterface $htmlRenderer
* @param bool $inTightList
*
* @return HtmlElement|string
*/
public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
{
if (!($block instanceof FencedCode)) {
throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
}
$attrs = [];
foreach ($block->getData('attributes', []) as $key => $value) {
$attrs[$key] = Xml::escape($value);
}
$content = $block->getStringContent();
$language = $this->getLanguage($block->getInfoWords());
$highlighted = false;
if ($language) {
$attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' ' : '';
try {
$highlighted = $this->hl->highlight($language, $content);
$content = $highlighted->value;
$attrs['class'] .= 'hljs ' . $highlighted->language;
} catch (Exception $e) {
$attrs['class'] .= 'language-' . $language;
}
}
if (!$highlighted) {
$content = Xml::escape($content);
}
return new HtmlElement(
'pre',
[],
new HtmlElement('code', $attrs, $content)
);
}
public function getLanguage($infoWords)
{
if (count($infoWords) === 0 || strlen($infoWords[0]) === 0) {
return false;
}
return Xml::escape($infoWords[0], true);
}
}