diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php --- a/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php @@ -22,51 +22,69 @@ // output text and assists automated escaping of blocks coming into the // system. - $num_lines = 0; - while (preg_match('/^\s*%%%/', $lines[$cursor])) { - $num_lines++; - - // If the line has ONLY "%%%", the block opener doesn't get to double - // up as a block terminator. - if (preg_match('/^\s*%%%\s*\z/', $lines[$cursor])) { - $num_lines++; - $cursor++; + $start_pattern = '(^\s*%%%)'; + $end_pattern = '(%%%\s*$)'; + $trivial_pattern = '(^\s*%%%\s*$)'; + + if (!preg_match($start_pattern, $lines[$cursor])) { + return 0; + } + + $start_cursor = $cursor; + + $found_empty = false; + $block_start = null; + while (true) { + if (!isset($lines[$cursor])) { + break; } - while (isset($lines[$cursor])) { - if (!preg_match('/%%%\s*$/', $lines[$cursor])) { - $num_lines++; - $cursor++; - continue; + $line = $lines[$cursor]; + + if ($block_start === null) { + $is_start = preg_match($start_pattern, $line); + + // If we've matched a block and then consumed one or more empty lines + // after it, stop merging more blocks into the match. + if ($found_empty) { + break; + } + + if ($is_start) { + $block_start = $cursor; } - break; } - $cursor++; + if ($block_start !== null) { + $is_end = preg_match($end_pattern, $line); + + // If a line contains only "%%%", it will match both the start and + // end patterns, but it only counts as a block start. + if ($is_end && ($cursor === $block_start)) { + $is_trivial = preg_match($trivial_pattern, $line); + if ($is_trivial) { + $is_end = false; + } + } - $found_empty = false; - while (isset($lines[$cursor])) { - if (!strlen(trim($lines[$cursor]))) { - $num_lines++; + if ($is_end) { + $block_start = null; $cursor++; - $found_empty = true; continue; } - break; } - if ($found_empty) { - // If there's an empty line after the block, stop merging blocks. - break; + if ($block_start === null) { + if (strlen(trim($line))) { + break; + } + $found_empty = true; } - if (!isset($lines[$cursor])) { - // If we're at the end of the input, stop looking for more lines. - break; - } + $cursor++; } - return $num_lines; + return ($cursor - $start_cursor); } public function markupText($text, $children) { diff --git a/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php b/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php --- a/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php +++ b/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php @@ -204,6 +204,7 @@ } $cursor += $num_lines; + break; } } diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/percent-block-unterminated.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/percent-block-unterminated.txt new file mode 100644 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/percent-block-unterminated.txt @@ -0,0 +1,5 @@ +%%%xyz +~~~~~~~~~~ +

xyz

+~~~~~~~~~~ +xyz