Changeset View
Standalone View
src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php
Show All 38 Lines | while (isset($lines[$cursor])) { | ||||
} | } | ||||
$cursor++; | $cursor++; | ||||
} | } | ||||
return $num_lines; | return $num_lines; | ||||
} | } | ||||
public function markupText($text, $children) { | public function markupText($text, $children) { | ||||
if (preg_match('/^\s*```/', $text)) { | // Header/footer eventually useful to be nice with "flavored markdown". | ||||
// When it starts with ```stuff the header is 'stuff' (->language) | |||||
valerio.bozzolan: In short I just added a `(.*)` | |||||
Done Inline ActionsDeclare $matches = null. avivey: Declare `$matches = null`. | |||||
// When it ends with stuff``` the footer is 'stuff' (->garbage) | |||||
$header_line = null; | |||||
$footer_line = null; | |||||
$matches = null; | |||||
if (preg_match('/^\s*```(.*)/', $text, $matches)) { | |||||
if (isset($matches[1])) { | |||||
$header_line = $matches[1]; | |||||
} | |||||
Done Inline Actions$header_line is probably better description. avivey: `$header_line` is probably better description. | |||||
// If this is a ```-style block, trim off the backticks and any leading | // If this is a ```-style block, trim off the backticks and any leading | ||||
// blank line. | // blank line. | ||||
$text = preg_replace('/^\s*```(\s*\n)?/', '', $text); | $text = preg_replace('/^\s*```(\s*\n)?/', '', $text); | ||||
$text = preg_replace('/```\s*$/', '', $text); | $text = preg_replace('/```\s*$/', '', $text); | ||||
} | } | ||||
$lines = explode("\n", $text); | $lines = explode("\n", $text); | ||||
// If we have a flavored header, it has sense to look for the footer. | |||||
if ($header_line !== null && $lines) { | |||||
$footer_line = $lines[last_key($lines)]; | |||||
} | |||||
// Strip final empty lines | |||||
while ($lines && !strlen(last($lines))) { | while ($lines && !strlen(last($lines))) { | ||||
unset($lines[last_key($lines)]); | unset($lines[last_key($lines)]); | ||||
} | } | ||||
$options = array( | $options = array( | ||||
'counterexample' => false, | 'counterexample' => false, | ||||
'lang' => null, | 'lang' => null, | ||||
'name' => null, | 'name' => null, | ||||
'lines' => null, | 'lines' => null, | ||||
); | ); | ||||
$parser = new PhutilSimpleOptions(); | $parser = new PhutilSimpleOptions(); | ||||
$custom = $parser->parse(head($lines)); | $custom = $parser->parse(head($lines)); | ||||
Done Inline ActionsNote that head($lines) was not adopted since it contains whatever first line of content after heavy trim, so it's not necessarily the thing after the backticks. valerio.bozzolan: Note that `head($lines)` was not adopted since it contains whatever first line of content after… | |||||
$valid_options = null; | |||||
if ($custom) { | if ($custom) { | ||||
$valid = true; | $valid_options = true; | ||||
foreach ($custom as $key => $value) { | foreach ($custom as $key => $value) { | ||||
if (!array_key_exists($key, $options)) { | if (!array_key_exists($key, $options)) { | ||||
$valid = false; | $valid_options = false; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if ($valid) { | if ($valid_options) { | ||||
array_shift($lines); | array_shift($lines); | ||||
$options = $custom + $options; | $options = $custom + $options; | ||||
} | } | ||||
} | } | ||||
// Parse flavored markdown strictly to don't eat legitimate Remarkup. | |||||
// Proceed only if we tried to parse options and we failed | |||||
// (no options also mean no language). | |||||
// For example this is not a valid option: ```php | |||||
Done Inline Actionsthis can just be !$valid_options. avivey: this can just be `!$valid_options`.
Actually, I think checking `=== false` should fail in the… | |||||
Done Inline ActionsSince we give high-priority to legitimate Remarkup, probably it's nice here to proceed only if we have parsed the options (not null) and if it was not valid (not true). That is why that === false valerio.bozzolan: Since we give high-priority to legitimate Remarkup, probably it's nice here to proceed only if… | |||||
Not Done Inline ActionsWhat does the parser returns in these cases:
? avivey: What does the parser returns in these cases:
1. ##```##
2. ##```php##
3. this thing:
%%%
```… | |||||
// Proceed only if the footer exists and it is not: blabla``` | |||||
// Accept only 2 lines or more. First line: header; then content. | |||||
if ( | |||||
Done Inline Actionsyou use if ($lines) in line 68, and if (count($lines)) here - is there a difference? avivey: you use `if ($lines)` in line 68, and `if (count($lines))` here - is there a difference? | |||||
Done Inline ActionsYep there is difference The check here is "please 2 lines or more". valerio.bozzolan: Yep there is difference
The check here is "please 2 lines or more". | |||||
$valid_options === false && | |||||
$header_line !== null && | |||||
$footer_line === '' && | |||||
count($lines) > 1 | |||||
) { | |||||
if (self::isKnownLanguageCode($header_line)) { | |||||
array_shift($lines); | |||||
$options['lang'] = $header_line; | |||||
} | |||||
} | |||||
// Normalize the text back to a 0-level indent. | // Normalize the text back to a 0-level indent. | ||||
$min_indent = 80; | $min_indent = 80; | ||||
foreach ($lines as $line) { | foreach ($lines as $line) { | ||||
for ($ii = 0; $ii < strlen($line); $ii++) { | for ($ii = 0; $ii < strlen($line); $ii++) { | ||||
if ($line[$ii] != ' ') { | if ($line[$ii] != ' ') { | ||||
$min_indent = min($ii, $min_indent); | $min_indent = min($ii, $min_indent); | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | return phutil_tag( | ||||
'class' => 'remarkup-code'.$aux_class, | 'class' => 'remarkup-code'.$aux_class, | ||||
'style' => $aux_style, | 'style' => $aux_style, | ||||
), | ), | ||||
PhutilSafeHTML::applyFunction( | PhutilSafeHTML::applyFunction( | ||||
'rtrim', | 'rtrim', | ||||
$engine->highlightSource($options['lang'], $text))); | $engine->highlightSource($options['lang'], $text))); | ||||
} | } | ||||
/** | |||||
* Check if a language code can be used in a generic flavored markdown. | |||||
* @param string $lang Language code | |||||
* @return bool | |||||
*/ | |||||
private static function isKnownLanguageCode($lang) { | |||||
$languages = self::knownLanguageCodes(); | |||||
return isset($languages[$lang]); | |||||
} | |||||
/** | |||||
* Get the available languages for a generic flavored markdown. | |||||
* @return array Languages as array keys. Ignore the value. | |||||
*/ | |||||
private static function knownLanguageCodes() { | |||||
// This is a friendly subset from https://pygments.org/languages/ | |||||
Done Inline Actionsarray( and even better, array( 'xx' => true, ); to make search faster. Making this a static function variable might improve performance as well - see https://we.phorge.it/source/phorge/browse/master/src/infrastructure/markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php$56 avivey: `array(`
and even better,
```lang=php
array(
'xx' => true,
);```
to make search faster. | |||||
Done Inline ActionsOK thanks. What about shorter 'xx' => 1, ? So also parsing is faster, saving 3*37 = 111 bytes :D Since PHP is heavily built over if(1) and if(0) it seems OK to me valerio.bozzolan: OK thanks. What about shorter `'xx' => 1,` ?
So also parsing is faster, saving 3*37 = 111… | |||||
Done Inline Actions🤷♀️ avivey: 🤷♀️ | |||||
static $map = array( | |||||
'arduino' => 1, | |||||
'assembly' => 1, | |||||
'awk' => 1, | |||||
'bash' => 1, | |||||
'bat' => 1, | |||||
'c' => 1, | |||||
'cmake' => 1, | |||||
'cobol' => 1, | |||||
'cpp' => 1, | |||||
'css' => 1, | |||||
'csharp' => 1, | |||||
'dart' => 1, | |||||
'delphi' => 1, | |||||
'fortran' => 1, | |||||
'go' => 1, | |||||
'groovy' => 1, | |||||
'haskell' => 1, | |||||
'java' => 1, | |||||
'javascript' => 1, | |||||
'kotlin' => 1, | |||||
'lisp' => 1, | |||||
'lua' => 1, | |||||
'matlab' => 1, | |||||
'make' => 1, | |||||
'perl' => 1, | |||||
'php' => 1, | |||||
'powershell' => 1, | |||||
'python' => 1, | |||||
'r' => 1, | |||||
'ruby' => 1, | |||||
'rust' => 1, | |||||
'scala' => 1, | |||||
'sh' => 1, | |||||
'sql' => 1, | |||||
'typescript' => 1, | |||||
'vba' => 1, | |||||
); | |||||
return $map; | |||||
} | |||||
} | } |
In short I just added a (.*)