diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -295,6 +295,8 @@ 'PhutilProtocolChannel' => 'channel/PhutilProtocolChannel.php', 'PhutilProxyException' => 'error/PhutilProxyException.php', 'PhutilProxyIterator' => 'utils/PhutilProxyIterator.php', + 'PhutilPygmentizeParser' => 'parser/PhutilPygmentizeParser.php', + 'PhutilPygmentizeParserTestCase' => 'parser/__tests__/PhutilPygmentizeParserTestCase.php', 'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php', 'PhutilPythonFragmentLexer' => 'lexer/PhutilPythonFragmentLexer.php', 'PhutilQsprintfInterface' => 'xsprintf/PhutilQsprintfInterface.php', @@ -850,6 +852,8 @@ 'Phobject', 'Iterator', ), + 'PhutilPygmentizeParser' => 'Phobject', + 'PhutilPygmentizeParserTestCase' => 'PhutilTestCase', 'PhutilPygmentsSyntaxHighlighter' => 'Phobject', 'PhutilPythonFragmentLexer' => 'PhutilLexer', 'PhutilQueryStringParser' => 'Phobject', diff --git a/src/parser/PhutilPygmentizeParser.php b/src/parser/PhutilPygmentizeParser.php new file mode 100644 --- /dev/null +++ b/src/parser/PhutilPygmentizeParser.php @@ -0,0 +1,83 @@ +map = $map; + return $this; + } + + public function getMap() { + return $this->map; + } + + public function parse($block) { + $class_look = 'class="'; + $class_len = strlen($class_look); + + $class_start = null; + + $map = $this->map; + + $len = strlen($block); + $out = ''; + $mode = 'text'; + for ($ii = 0; $ii < $len; $ii++) { + $c = $block[$ii]; + switch ($mode) { + case 'text': + // We're in general text between tags, and just passing characers + // through unmodified. + if ($c == '<') { + $mode = 'tag'; + } + $out .= $c; + break; + case 'tag': + // We're inside a tag, and looking for `class="` so we can rewrite + // it. + if ($c == '>') { + $mode = 'text'; + } + if ($c == 'c') { + if (!substr_compare($block, $class_look, $ii, $class_len)) { + $mode = 'class'; + $ii += $class_len; + $class_start = $ii; + } + } + + if ($mode != 'class') { + $out .= $c; + } + break; + case 'class': + // We're inside a `class="..."` tag, and looking for the ending quote + // so we can replace it. + if ($c == '"') { + $class = substr($block, $class_start, $ii - $class_start); + + // If this class is present in the map, rewrite it into an inline + // style attribute. + if (isset($map[$class])) { + $out .= 'style="'.phutil_escape_html($map[$class]).'"'; + } else { + $out .= 'class="'.$class.'"'; + } + + $mode = 'tag'; + } + break; + } + } + + return $out; + } + +} diff --git a/src/parser/__tests__/PhutilPygmentizeParserTestCase.php b/src/parser/__tests__/PhutilPygmentizeParserTestCase.php new file mode 100644 --- /dev/null +++ b/src/parser/__tests__/PhutilPygmentizeParserTestCase.php @@ -0,0 +1,43 @@ +tryParser( + '', + '', + array(), + pht('Empty')); + + $this->tryParser( + '1', + '1', + array( + 'mi' => 'color: #ff0000', + ), + pht('Simple')); + + $this->tryParser( + '1', + '1', + array(), + pht('Missing Class')); + + $this->tryParser( + 'X', + 'X', + array( + 'nc' => 'color: #ff0000', + ), + pht('Extra Attribute')); + } + + private function tryParser($input, $expect, array $map, $label) { + $actual = id(new PhutilPygmentizeParser()) + ->setMap($map) + ->parse($input); + + $this->assertEqual($expect, $actual, pht('Pygmentize Parser: %s', $label)); + } + +}