Skip to content

Commit

Permalink
Forward-port 9.18.1.6 changes into master
Browse files Browse the repository at this point in the history
  • Loading branch information
allejo committed Mar 24, 2021
1 parent f153e83 commit c66fe5c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 51 deletions.
3 changes: 3 additions & 0 deletions .php_cs.dist
Expand Up @@ -17,6 +17,9 @@ return PhpCsFixer\Config::create()
'array_indentation' => true,
'array_syntax' => ['syntax' => 'long'],
'concat_space' => ['spacing' => 'one'],
'echo_tag_syntax' => ['format' => 'short'],
'no_alias_language_construct_call' => false,
'no_alternative_syntax' => false,
'no_useless_else' => true,
'no_useless_return' => true,
'phpdoc_align' => true,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -111,7 +111,7 @@ Available functions:
- [`getLanguagesFolder(): string`](src/HighlightUtilities/Functions.php#L136-L144)
- [`getLanguageDefinitionPath(string $name): string`](src/HighlightUtilities/Functions.php#L150-L160)
- [`getThemeBackgroundColor(string $name): float[]`](src/HighlightUtilities/Functions.php#L80-L93)
- [`splitCodeIntoArray(string $html): false|string[]`](src/HighlightUtilities/Functions.php#L197-L210)
- [`splitCodeIntoArray(string $html): false|string[]`](src/HighlightUtilities/Functions.php#L199-L210)

## Versioning

Expand Down
3 changes: 0 additions & 3 deletions composer.json
Expand Up @@ -53,9 +53,6 @@
"symfony/finder": "^2.8|^3.4",
"symfony/var-dumper": "^2.8|^3.4"
},
"suggest": {
"ext-dom": "Needed to make use of the features in the utilities namespace"
},
"extra": {
"branch-alias": {
"dev-master": "10.0-dev"
Expand Down
2 changes: 1 addition & 1 deletion demo/example.php
Expand Up @@ -41,6 +41,6 @@
<link rel="stylesheet" type="text/css" href="../styles/default.css">
</head>
<body>
<pre><code class="hljs <?=$r->language; ?>"><?=$r->value; ?></code></pre>
<pre><code class="hljs <?= $r->language; ?>"><?= $r->value; ?></code></pre>
</body>
</html>
63 changes: 33 additions & 30 deletions src/HighlightUtilities/Functions.php
Expand Up @@ -201,58 +201,61 @@ public static function getStyleSheetPath($name)
*
* @api
*
* @since 9.18.1.6 `RuntimeException` and `UnexpectedValueException` can no longer be thrown.
* @since 9.15.6.1
*
* @param string $html An HTML string generated by `Highlighter::highlight()`
*
* @throws \RuntimeException when the DOM extension is not available
* @throws \UnexpectedValueException when the given HTML could not be parsed
*
* @return string[]|false An array of lines of code as strings. False if an error occurred in splitting up by lines
*/
public static function splitCodeIntoArray($html)
{
if (!extension_loaded("dom")) {
throw new \RuntimeException("The DOM extension is not loaded but is required.");
}

if (trim($html) === "") {
return array();
}

$dom = new \DOMDocument();
$queuedPrefix = '';
$regexWorkspace = array();
$rawLines = preg_split('/\R/u', $html);

// https://stackoverflow.com/a/8218649
if (!$dom->loadHTML(mb_convert_encoding($html, "HTML-ENTITIES", "UTF-8"))) {
throw new \UnexpectedValueException("The given HTML could not be parsed correctly.");
if ($rawLines === false) {
return false;
}

$xpath = new \DOMXPath($dom);
$spans = $xpath->query("//span[contains(text(), '\n') or contains(text(), '\r\n')]");
foreach ($rawLines as &$rawLine) {
// If the previous line has been marked as "open", then we'll have something
// in our queue
if ($queuedPrefix !== '') {
$rawLine = $queuedPrefix . $rawLine;
$queuedPrefix = '';
}

/** @var \DOMElement $span */
foreach ($spans as $span) {
$closingTags = '';
$openingTags = '';
$curr = $span;
// Find how many opening `<span>` tags exist on this line
preg_match_all('/<span[^>]*+>/u', $rawLine, $regexWorkspace);
$openingTags = count($regexWorkspace[0]);

while ($curr->tagName === 'span') {
$closingTags .= '</span>';
$openingTags = sprintf('<span class="%s">%s', $curr->getAttribute("class"), $openingTags);
// Find all of the closing `</span>` tags that exist on this line
preg_match_all('/<\/span>/u', $rawLine, $regexWorkspace);
$closingTags = count($regexWorkspace[0]);

$curr = $curr->parentNode;
// If the number of opening tags matches the number of closing tags, then
// we don't have any new tags that span multiple lines
if ($openingTags === $closingTags) {
continue;
}

$renderedSpan = $dom->saveHTML($span);
$finished = preg_replace(
'/\R/u',
$closingTags . PHP_EOL . $openingTags,
$renderedSpan
);
$html = str_replace($renderedSpan, $finished, $html);
// Find all of the complete `<span>` tags and remove them from a working
// copy of the line. Then we'll be left with just opening tags.
$workingLine = preg_replace('/<span[^>]*+>[^<]*+<\/span>/u', '', $rawLine);
preg_match_all('/<span[^>]*+>/u', $workingLine, $regexWorkspace);
$queuedPrefix = implode('', $regexWorkspace[0]);

// Close all of the remaining open tags on this line
$diff = str_repeat('</span>', $openingTags - $closingTags);
$rawLine .= $diff;
}

return preg_split('/\R/u', $html);
return $rawLines;
}

/**
Expand Down
80 changes: 64 additions & 16 deletions test/HighlightUtilitiesTest.php
Expand Up @@ -37,7 +37,7 @@ protected function setUp()
$this->hl = new \Highlight\Highlighter();
}

public function testGetAvailableStyleSheets_NamesOnly()
public function testGetAvailableStyleSheetsNamesOnly()
{
$results = \HighlightUtilities\Functions::getAvailableStyleSheets();

Expand All @@ -49,7 +49,7 @@ public function testGetAvailableStyleSheets_NamesOnly()
}
}

public function testGetAvailableStyleSheets_FilePaths()
public function testGetAvailableStyleSheetsFilePaths()
{
$results = \HighlightUtilities\Functions::getAvailableStyleSheets(true);

Expand All @@ -63,15 +63,15 @@ public function testGetAvailableStyleSheets_FilePaths()
}
}

public function testGetAvailableStyleSheets_SameCount()
public function testGetAvailableStyleSheetsSameCount()
{
$namesOnly = \HighlightUtilities\Functions::getAvailableStyleSheets();
$filePaths = \HighlightUtilities\Functions::getAvailableStyleSheets(true);

$this->assertCount(count($namesOnly), $filePaths);
}

public function testGetStyleSheet_Exists()
public function testGetStyleSheetExists()
{
$yesExt = \HighlightUtilities\Functions::getStyleSheet("a11y-dark.css");
$noExt = \HighlightUtilities\Functions::getStyleSheet("a11y-dark");
Expand All @@ -80,14 +80,14 @@ public function testGetStyleSheet_Exists()
$this->assertEquals($yesExt, $noExt);
}

public function testGetStyleSheet_NotExists()
public function testGetStyleSheetNotExists()
{
$this->setExpectedException('\DomainException');

\HighlightUtilities\Functions::getStyleSheet("strawberry.png");
}

public function testSplitCodeIntoArray_MultilineComment()
public function testSplitCodeIntoArrayMultilineComment()
{
$raw = <<<PHP
/**
Expand Down Expand Up @@ -116,7 +116,7 @@ public function testSplitCodeIntoArray_MultilineComment()
}
}

public function testSplitCodeIntoArray_Emojis()
public function testSplitCodeIntoArrayEmojis()
{
$raw = <<<'PHP'
// ✅ ...
Expand All @@ -127,16 +127,16 @@ public function testSplitCodeIntoArray_Emojis()
$split = \HighlightUtilities\Functions::splitCodeIntoArray($highlighted->value);

$this->assertEquals(
$split,
array(
'<span class="hljs-comment">// ✅ ...</span>',
'$user = <span class="hljs-keyword">new</span> \stdClass();',
'$isUserPending = $user-&gt;isStatus(<span class="hljs-string">\'pending\'</span>);',
)
),
$split
);
}

public function testSplitCodeIntoArray_DeeplyNestedSpans()
public function testSplitCodeIntoArrayDeeplyNestedSpans()
{
$raw = <<<'JAVA'
public QuoteEntity(
Expand All @@ -146,27 +146,75 @@ public function testSplitCodeIntoArray_DeeplyNestedSpans()
$split = \HighlightUtilities\Functions::splitCodeIntoArray($highlighted->value);

$this->assertEquals(
$split,
array(
'<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">QuoteEntity</span><span class="hljs-params">(</span></span>',
'<span class="hljs-function"><span class="hljs-params">)</span></span>',
)
),
$split
);
}

public function testSplitCodeIntoArray_DeeplyNestedSpansCRLF()
public function testSplitCodeIntoArrayXmlWithAttributesOnNewLines()
{
$raw = <<<'XML'
<?xml version="1.0" encoding="utf-8" ?>
<tag a="t"
b="t">
</tag>
XML;
$highlighted = $this->hl->highlight('xml', $raw);
$split = \HighlightUtilities\Functions::splitCodeIntoArray($highlighted->value);

$this->assertEquals(
array(
'<span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>',
' <span class="hljs-tag">&lt;<span class="hljs-name">tag</span> <span class="hljs-attr">a</span>=<span class="hljs-string">"t"</span></span>',
'<span class="hljs-tag"> <span class="hljs-attr">b</span>=<span class="hljs-string">"t"</span>&gt;</span>',
' <span class="hljs-tag">&lt;/<span class="hljs-name">tag</span>&gt;</span>',
),
$split
);
}

public function testSplitCodeIntoArrayXmlWithAttributesSpanningMultipleLines()
{
$raw = <<<'XML'
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true">
</nlog>
XML;
$highlighted = $this->hl->highlight('xml', $raw);
$split = \HighlightUtilities\Functions::splitCodeIntoArray($highlighted->value);

$this->assertEquals(
array(
'<span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>',
'<span class="hljs-tag">&lt;<span class="hljs-name">nlog</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.nlog-project.org/schemas/NLog.xsd"</span></span>',
'<span class="hljs-tag"> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span></span>',
'<span class="hljs-tag"> <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"</span></span>',
'<span class="hljs-tag"> <span class="hljs-attr">autoReload</span>=<span class="hljs-string">"true"</span>&gt;</span>',
'<span class="hljs-tag">&lt;/<span class="hljs-name">nlog</span>&gt;</span>',
),
$split
);
}

public function testSplitCodeIntoArrayDeeplyNestedSpansCRLF()
{
$raw = "public QuoteEntity(\r\n)";

$highlighted = $this->hl->highlight('java', $raw);
$split = \HighlightUtilities\Functions::splitCodeIntoArray($highlighted->value);

$this->assertEquals(
$split,
array(
'<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">QuoteEntity</span><span class="hljs-params">(</span></span>',
'<span class="hljs-function"><span class="hljs-params">)</span></span>',
)
),
$split
);
}

Expand All @@ -182,7 +230,7 @@ public static function dataProvider_emptyStrings()
/**
* @dataProvider dataProvider_emptyStrings
*/
public function testSplitCodeIntoArray_EmptyString($string)
public function testSplitCodeIntoArrayEmptyString($string)
{
$this->assertEquals(array(), \HighlightUtilities\Functions::splitCodeIntoArray($string));
}
Expand Down

0 comments on commit c66fe5c

Please sign in to comment.