Page MenuHomec4science

PhutilRemarkupEngineRemarkupCodeBlockRule.php
No OneTemporary

File Metadata

Created
Thu, Feb 20, 09:56

PhutilRemarkupEngineRemarkupCodeBlockRule.php

<?php
/**
* @group markup
*/
final class PhutilRemarkupEngineRemarkupCodeBlockRule
extends PhutilRemarkupEngineBlockRule {
public function getBlockPattern() {
return "/^(\s{2,}|```)/";
}
public function shouldMatchBlock($block) {
if (!preg_match($this->getBlockPattern(), $block)) {
return false;
}
if (preg_match('@^[a-z]+://\S+$@', trim($block))) {
return false;
}
return true;
}
public function shouldContinueWithBlock($block, $last_block) {
// If the first code block begins with ```, we keep matching blocks until
// we hit a terminating ```, regardless of their content.
if (preg_match('/^```/', $last_block)) {
if (preg_match('/```$/', $last_block)) {
return false;
}
return true;
}
// If we just matched a code block based on indentation, always match the
// next block if it is indented, too. This basically means that we'll treat
// lists after code blocks as more code, but usually the "-" is from a diff
// or from objective C or something; it is rare to intentionally follow a
// code block with a list.
if (preg_match('/^\s{2,}/', $block)) {
return true;
}
return false;
}
public function shouldMergeBlocks() {
return true;
}
public function markupText($text) {
if (preg_match('/^```/', $text)) {
// If this is a ```-style block, trim off the backticks.
$text = preg_replace('/```\s*$/', '', substr($text, 3));
}
$lines = explode("\n", $text);
$options = array(
'counterexample' => false,
'lang' => null,
'name' => null,
'lines' => null,
);
$custom = PhutilSimpleOptions::parse(head($lines));
if ($custom) {
$valid = true;
foreach ($custom as $key => $value) {
if (!array_key_exists($key, $options)) {
$valid = false;
break;
}
}
if ($valid) {
array_shift($lines);
$options = $custom + $options;
}
}
if ($options['counterexample']) {
$aux_class = ' remarkup-counterexample';
} else {
$aux_class = null;
}
// Normalize the text back to a 0-level indent.
$min_indent = 80;
foreach ($lines as $line) {
for ($ii = 0; $ii < strlen($line); $ii++) {
if ($line[$ii] != ' ') {
$min_indent = min($ii, $min_indent);
break;
}
}
}
if ($min_indent) {
$indent_string = str_repeat(' ', $min_indent);
$text = preg_replace(
'/^'.$indent_string.'/m',
'',
implode("\n", $lines));
} else {
$text = implode("\n", $lines);
}
if (empty($options['lang'])) {
// If the user hasn't specified "lang=..." explicitly, try to guess the
// language. If we fail, fall back to configured defaults.
$lang = PhutilLanguageGuesser::guessLanguage($text);
if (!$lang) {
$lang = nonempty(
$this->getEngine()->getConfig('phutil.codeblock.language-default'),
'php');
}
$options['lang'] = $lang;
}
$name_header = null;
if ($options['name']) {
$name_header = phutil_render_tag(
'div',
array(
'class' => 'remarkup-code-header',
),
phutil_escape_html($options['name']));
}
$aux_style = null;
if ($options['lines']) {
// Put a minimum size on this because the scrollbar is otherwise
// unusable.
$height = max(6, (int)$options['lines']);
$aux_style = 'max-height: '.(2 * $height).'em;';
}
$engine = $this->getEngine()->getConfig('syntax-highlighter.engine');
if (!$engine) {
$engine = 'PhutilDefaultSyntaxHighlighterEngine';
}
$engine = newv($engine, array());
$engine->setConfig(
'pygments.enabled',
$this->getEngine()->getConfig('pygments.enabled'));
$code_body = phutil_render_tag(
'pre',
array(
'class' => 'remarkup-code'.$aux_class,
'style' => $aux_style,
),
$engine->highlightSource($options['lang'], $text));
return phutil_render_tag(
'div',
array(
'class' => 'remarkup-code-block',
'data-code-lang' => $options['lang'],
),
$name_header.$code_body);
}
}

Event Timeline