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 @@ -403,6 +403,7 @@ 'PhutilRemarkupLiteralBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupLiteralBlockRule.php', 'PhutilRemarkupMonospaceRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupMonospaceRule.php', 'PhutilRemarkupNoteBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupNoteBlockRule.php', + 'PhutilRemarkupQuotedBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupQuotedBlockRule.php', 'PhutilRemarkupQuotesBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php', 'PhutilRemarkupReplyBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupReplyBlockRule.php', 'PhutilRemarkupRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRule.php', @@ -1063,8 +1064,9 @@ 'PhutilRemarkupLiteralBlockRule' => 'PhutilRemarkupBlockRule', 'PhutilRemarkupMonospaceRule' => 'PhutilRemarkupRule', 'PhutilRemarkupNoteBlockRule' => 'PhutilRemarkupBlockRule', - 'PhutilRemarkupQuotesBlockRule' => 'PhutilRemarkupBlockRule', - 'PhutilRemarkupReplyBlockRule' => 'PhutilRemarkupBlockRule', + 'PhutilRemarkupQuotedBlockRule' => 'PhutilRemarkupBlockRule', + 'PhutilRemarkupQuotesBlockRule' => 'PhutilRemarkupQuotedBlockRule', + 'PhutilRemarkupReplyBlockRule' => 'PhutilRemarkupQuotedBlockRule', 'PhutilRemarkupRule' => 'Phobject', 'PhutilRemarkupSimpleTableBlockRule' => 'PhutilRemarkupBlockRule', 'PhutilRemarkupTableBlockRule' => 'PhutilRemarkupBlockRule', diff --git a/src/markup/engine/__tests__/remarkup/quoted-code-block.txt b/src/markup/engine/__tests__/remarkup/quoted-code-block.txt --- a/src/markup/engine/__tests__/remarkup/quoted-code-block.txt +++ b/src/markup/engine/__tests__/remarkup/quoted-code-block.txt @@ -11,6 +11,6 @@ $foo = 'bar'; ~~~~~~~~~~ > This should be a code block: -> +> > $foo = 'bar'; diff --git a/src/markup/engine/__tests__/remarkup/quoted-lists.txt b/src/markup/engine/__tests__/remarkup/quoted-lists.txt --- a/src/markup/engine/__tests__/remarkup/quoted-lists.txt +++ b/src/markup/engine/__tests__/remarkup/quoted-lists.txt @@ -18,7 +18,7 @@ ~~~~~~~~~~ > 1. X > 2. Y -> ~ +> > B -> ~ +> > - C diff --git a/src/markup/engine/__tests__/remarkup/quoted-quote.txt b/src/markup/engine/__tests__/remarkup/quoted-quote.txt new file mode 100644 --- /dev/null +++ b/src/markup/engine/__tests__/remarkup/quoted-quote.txt @@ -0,0 +1,19 @@ +>>! In U, W wrote: +> - Y +> +> Z +~~~~~~~~~~ +
+
In U, W wrote:
+
+ +

Z

+
+~~~~~~~~~~ +In U, W wrote: + +> - Y +> +> Z diff --git a/src/markup/engine/__tests__/remarkup/reply-basic.txt b/src/markup/engine/__tests__/remarkup/reply-basic.txt --- a/src/markup/engine/__tests__/remarkup/reply-basic.txt +++ b/src/markup/engine/__tests__/remarkup/reply-basic.txt @@ -9,4 +9,3 @@ In comment #123, alincoln wrote: > Four score and twenty years ago... - diff --git a/src/markup/engine/__tests__/remarkup/reply-nested.txt b/src/markup/engine/__tests__/remarkup/reply-nested.txt --- a/src/markup/engine/__tests__/remarkup/reply-nested.txt +++ b/src/markup/engine/__tests__/remarkup/reply-nested.txt @@ -41,10 +41,8 @@ > > More previously, vegetables: > -> > - Potato -> > - Potato -> > - Potato -> +>> - Potato +>> - Potato +>> - Potato > > The end. - diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotedBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotedBlockRule.php new file mode 100644 --- /dev/null +++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotedBlockRule.php @@ -0,0 +1,108 @@ + $line) { + $text[$key] = substr($line, 1); + } + + // If every line in the block is empty or begins with at least one leading + // space, strip the initial space off each line. When we quote text, we + // normally add "> " (with a space) to the beginning of each line, which + // can disrupt some other rules. If the block appears to have this space + // in front of each line, remove it. + + $strip_space = true; + foreach ($text as $key => $line) { + $len = strlen($line); + + if (!$len) { + // We'll still strip spaces if there are some completely empty + // lines, they may have just had trailing whitespace trimmed. + continue; + } + + // If this line is part of a nested quote block, just ignore it when + // realigning this quote block. It's either an author attribution + // line with ">>!", or we'll deal with it in a subrule when processing + // the nested quote block. + if ($line[0] == '>') { + continue; + } + + if ($line[0] == ' ' || $line[0] == "\n") { + continue; + } + + // The first character of this line is something other than a space, so + // we can't strip spaces. + $strip_space = false; + break; + } + + if ($strip_space) { + foreach ($text as $key => $line) { + $len = strlen($line); + if (!$len) { + continue; + } + + if ($line[0] !== ' ') { + continue; + } + + $text[$key] = substr($line, 1); + } + } + + // Strip leading empty lines. + foreach ($text as $key => $line) { + if (!strlen(trim($line))) { + unset($text[$key]); + } + } + + return implode('', $text); + } + + final protected function getQuotedText($text) { + $text = rtrim($text, "\n"); + + $no_whitespace = array( + // For readability, we render nested quotes as ">> quack", + // not "> > quack". + '>' => true, + + // If the line is empty except for a newline, do not add an + // unnecessary dangling space. + "\n" => true, + ); + + $text = phutil_split_lines($text, true); + foreach ($text as $key => $line) { + $c = null; + if (isset($line[0])) { + $c = $line[0]; + } else { + $c = null; + } + + if (isset($no_whitespace[$c])) { + $text[$key] = '>'.$line; + } else { + $text[$key] = '> '.$line; + } + } + $text = implode('', $text); + + return $text; + } + +} diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php --- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php +++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php @@ -1,6 +1,7 @@ $line) { - $text[$key] = substr($line, 1); - } - - // If every line in the block is empty or begins with at least one leading - // space, strip the initial space off each line. When we quote text, we - // normally add "> " (with a space) to the beginning of each line, which - // can disrupt some other rules. If the block appears to have this space - // in front of each line, remove it. - - $strip_space = true; - foreach ($text as $key => $line) { - $len = strlen($line); - - if (!$len) { - // We'll still strip spaces if there are some completely empty - // lines, they may have just had trailing whitespace trimmed. - continue; - } - - if ($line[0] == ' ' || $line[0] == "\n") { - continue; - } - - // The first character of this line is something other than a space, so - // we can't strip spaces. - $strip_space = false; - break; - } - - if ($strip_space) { - foreach ($text as $key => $line) { - $len = strlen($line); - if (!$len) { - continue; - } - - if ($line[0] !== ' ') { - continue; - } - - $text[$key] = substr($line, 1); - } - } - - return array('', implode('', $text)); + return array('', $this->normalizeQuotedBody($text)); } public function markupText($text, $children) { if ($this->getEngine()->isTextMode()) { - $lines = rtrim($children, "\n"); - $lines = phutil_split_lines($lines); - foreach ($lines as $key => $line) { - if (isset($line[0]) && ($line[0] == '>')) { - $line = '>'.$line; - } else { - $line = '> '.$line; - } - $lines[$key] = $line; - } - return implode('', $lines); + return $this->getQuotedText($children); } $attributes = array(); diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupReplyBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupReplyBlockRule.php --- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupReplyBlockRule.php +++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupReplyBlockRule.php @@ -1,6 +1,7 @@ normalizeQuotedBody($body); - // Remove the carets. - foreach ($body as $key => $line) { - $body[$key] = substr($line, 1); - } - - // Strip leading empty lines. - foreach ($body as $key => $line) { - if (strlen(trim($line))) { - break; - } - unset($body[$key]); - } - - return array(trim($head), implode('', $body)); + return array(trim($head), $body); } public function markupText($text, $children) { $text = $this->applyRules($text); if ($this->getEngine()->isTextMode()) { - $children = phutil_split_lines($children, true); - foreach ($children as $key => $child) { - if (strlen(trim($child))) { - $children[$key] = '> '.$child; - } else { - $children[$key] = '>'.$child; - } - } - $children = implode('', $children); - + $children = $this->getQuotedText($children); return $text."\n\n".$children; }