3
final class PhutilRemarkupCodeBlockRule extends PhutilRemarkupBlockRule {
5
public function getMatchingLineCount(array $lines, $cursor) {
8
if (preg_match('/^(\s{2,}).+/', $lines[$cursor])) {
10
} else if (preg_match('/^\s*(```)/', $lines[$cursor])) {
19
preg_match('/^\s*(```)(.*)(```)\s*$/', $lines[$cursor])) {
25
while (isset($lines[$cursor])) {
27
if (preg_match('/```\s*$/', $lines[$cursor])) {
33
if (strlen(trim($lines[$cursor]))) {
34
if (!preg_match('/^\s{2,}/', $lines[$cursor])) {
46
public function markupText($text, $children) {
47
if (preg_match('/^\s*```/', $text)) {
48
// If this is a ```-style block, trim off the backticks and any leading
50
$text = preg_replace('/^\s*```(\s*\n)?/', '', $text);
51
$text = preg_replace('/```\s*$/', '', $text);
54
$lines = explode("\n", $text);
55
while ($lines && !strlen(last($lines))) {
56
unset($lines[last_key($lines)]);
60
'counterexample' => false,
66
$parser = new PhutilSimpleOptions();
67
$custom = $parser->parse(head($lines));
70
foreach ($custom as $key => $value) {
71
if (!array_key_exists($key, $options)) {
78
$options = $custom + $options;
82
// Normalize the text back to a 0-level indent.
84
foreach ($lines as $line) {
85
for ($ii = 0; $ii < strlen($line); $ii++) {
86
if ($line[$ii] != ' ') {
87
$min_indent = min($ii, $min_indent);
93
$text = implode("\n", $lines);
95
$indent_string = str_repeat(' ', $min_indent);
96
$text = preg_replace('/^'.$indent_string.'/m', '', $text);
99
if ($this->getEngine()->isTextMode()) {
103
if ($options['counterexample']) {
104
$header[] = 'counterexample';
106
if ($options['name'] != '') {
107
$header[] = 'name='.$options['name'];
110
$out[] = implode(', ', $header);
113
$text = preg_replace('/^/m', ' ', $text);
116
return implode("\n", $out);
119
if (empty($options['lang'])) {
120
// If the user hasn't specified "lang=..." explicitly, try to guess the
121
// language. If we fail, fall back to configured defaults.
122
$lang = PhutilLanguageGuesser::guessLanguage($text);
125
$this->getEngine()->getConfig('phutil.codeblock.language-default'),
128
$options['lang'] = $lang;
131
$code_body = $this->highlightSource($text, $options);
134
if ($options['name']) {
135
$name_header = phutil_tag(
138
'class' => 'remarkup-code-header',
146
'class' => 'remarkup-code-block',
147
'data-code-lang' => $options['lang'],
148
'data-sigil' => 'remarkup-code-block',
150
array($name_header, $code_body));
153
private function highlightSource($text, array $options) {
154
if ($options['counterexample']) {
155
$aux_class = ' remarkup-counterexample';
161
if ($options['lines']) {
162
// Put a minimum size on this because the scrollbar is otherwise
164
$height = max(6, (int)$options['lines']);
165
$aux_style = 'max-height: '.(2 * $height).'em;';
168
$engine = $this->getEngine()->getConfig('syntax-highlighter.engine');
170
$engine = 'PhutilDefaultSyntaxHighlighterEngine';
172
$engine = newv($engine, array());
175
$this->getEngine()->getConfig('pygments.enabled'));
179
'class' => 'remarkup-code'.$aux_class,
180
'style' => $aux_style,
182
PhutilSafeHTML::applyFunction(
184
$engine->highlightSource($options['lang'], $text)));