Changeset View
Changeset View
Standalone View
Standalone View
src/markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php
<?php | <?php | ||||
final class PhutilPygmentsSyntaxHighlighter extends Phobject { | final class PhutilPygmentsSyntaxHighlighter extends Phobject { | ||||
const MAX_LINE_LENGTH = 10240; | |||||
const MAX_SOURCE_LENGTH = 1048576; | |||||
private $config = array(); | private $config = array(); | ||||
public function setConfig($key, $value) { | public function setConfig($key, $value) { | ||||
$this->config[$key] = $value; | $this->config[$key] = $value; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getHighlightFuture($source) { | public function getHighlightFuture($source) { | ||||
$language = idx($this->config, 'language'); | $language = idx($this->config, 'language'); | ||||
if (preg_match('/\r(?!\n)/', $source)) { | if (preg_match('/\r(?!\n)/', $source)) { | ||||
// TODO: Pygments converts "\r" newlines into "\n" newlines, so we can't | // TODO: Pygments converts "\r" newlines into "\n" newlines, so we can't | ||||
// use it on files with "\r" newlines. If we have "\r" not followed by | // use it on files with "\r" newlines. If we have "\r" not followed by | ||||
// "\n" in the file, skip highlighting. | // "\n" in the file, skip highlighting. | ||||
$language = null; | $language = null; | ||||
} else if ((strlen($source) > self::MAX_SOURCE_LENGTH) || | |||||
($this->getLongestLineLength($source) > self::MAX_LINE_LENGTH)) { | |||||
// `pygmentize` is really slow and expensive when dealing with long | |||||
// source but especially with long lines. Better skip highlighting than | |||||
// 500 error. | |||||
$language = null; | |||||
} | } | ||||
if ($language) { | if ($language) { | ||||
$language = $this->getPygmentsLexerNameFromLanguageName($language); | $language = $this->getPygmentsLexerNameFromLanguageName($language); | ||||
$future = new ExecFuture( | $future = new ExecFuture( | ||||
'pygmentize -O encoding=utf-8 -O stripnl=False -f html -l %s', | 'pygmentize -O encoding=utf-8 -O stripnl=False -f html -l %s', | ||||
$language); | $language); | ||||
$scrub = false; | $scrub = false; | ||||
if ($language == 'php' && strpos($source, '<?') === false) { | if ($language == 'php' && strpos($source, '<?') === false) { | ||||
$source = "<?php\n".$source; | $source = "<?php\n".$source; | ||||
$scrub = true; | $scrub = true; | ||||
} | } | ||||
$future->write($source); | $future->write($source); | ||||
return new PhutilDefaultSyntaxHighlighterEnginePygmentsFuture( | return new PhutilDefaultSyntaxHighlighterEnginePygmentsFuture( | ||||
$future, | $future, | ||||
$source, | $source, | ||||
$scrub); | $scrub); | ||||
} | } | ||||
return id(new PhutilDefaultSyntaxHighlighter()) | return id(new PhutilDefaultSyntaxHighlighter()) | ||||
->getHighlightFuture($source); | ->getHighlightFuture($source); | ||||
} | } | ||||
private function getLongestLineLength($source) { | |||||
$longest = 0; | |||||
$offset = 0; | |||||
while (($pos = strpos($source, "\n", $offset)) !== false) { | |||||
$longest = max($longest, $pos - $offset); | |||||
$offset = $pos + 1; | |||||
} | |||||
$longest = max($longest, strlen($source) - $offset); | |||||
return $longest; | |||||
} | |||||
private function getPygmentsLexerNameFromLanguageName($language) { | private function getPygmentsLexerNameFromLanguageName($language) { | ||||
static $map = array( | static $map = array( | ||||
'adb' => 'ada', | 'adb' => 'ada', | ||||
'ads' => 'ada', | 'ads' => 'ada', | ||||
'ahkl' => 'ahk', | 'ahkl' => 'ahk', | ||||
'as' => 'as3', | 'as' => 'as3', | ||||
'asax' => 'aspx-vb', | 'asax' => 'aspx-vb', | ||||
'ascx' => 'aspx-vb', | 'ascx' => 'aspx-vb', | ||||
▲ Show 20 Lines • Show All 166 Lines • Show Last 20 Lines |