diff --git a/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php b/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php index 74932d9..c3b4960 100644 --- a/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php +++ b/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php @@ -1,121 +1,132 @@ markupText($root.$file); } } private function markupText($markup_file) { $contents = Filesystem::readFile($markup_file); $file = basename($markup_file); $parts = explode("\n~~~~~~~~~~\n", $contents); $this->assertEqual(3, count($parts), $markup_file); list($input_remarkup, $expected_output, $expected_text) = $parts; + $input_remarkup = $this->unescapeTrailingWhitespace($input_remarkup); + $expected_output = $this->unescapeTrailingWhitespace($expected_output); + $expected_text = $this->unescapeTrailingWhitespace($expected_text); + $engine = $this->buildNewTestEngine(); switch ($file) { case 'raw-escape.txt': // NOTE: Here, we want to test PhutilRemarkupEscapeRemarkupRule and // PhutilRemarkupBlockStorage, which are triggered by "\1". In the // test, "~" is used as a placeholder for "\1" since it's hard to type // "\1". $input_remarkup = str_replace('~', "\1", $input_remarkup); $expected_output = str_replace('~', "\1", $expected_output); $expected_text = str_replace('~', "\1", $expected_text); break; case 'toc.txt': $engine->setConfig('header.generate-toc', true); break; case 'link-same-window.txt': $engine->setConfig('uri.same-window', true); break; case 'link-square.txt': $engine->setConfig('uri.base', 'http://www.example.com/'); $engine->setConfig('uri.here', 'http://www.example.com/page/'); break; } $actual_output = (string)$engine->markupText($input_remarkup); switch ($file) { case 'toc.txt': $table_of_contents = PhutilRemarkupHeaderBlockRule::renderTableOfContents($engine); $actual_output = $table_of_contents."\n\n".$actual_output; break; } $this->assertEqual( $expected_output, $actual_output, pht("Failed to markup HTML in file '%s'.", $file)); $engine->setMode(PhutilRemarkupEngine::MODE_TEXT); $actual_output = (string)$engine->markupText($input_remarkup); $this->assertEqual( $expected_text, $actual_output, pht("Failed to markup text in file '%s'.", $file)); } private function buildNewTestEngine() { $engine = new PhutilRemarkupEngine(); $engine->setConfig( 'uri.allowed-protocols', array( 'http' => true, 'mailto' => true, 'tel' => true, )); $rules = array(); $rules[] = new PhutilRemarkupEscapeRemarkupRule(); $rules[] = new PhutilRemarkupMonospaceRule(); $rules[] = new PhutilRemarkupDocumentLinkRule(); $rules[] = new PhutilRemarkupHyperlinkRule(); $rules[] = new PhutilRemarkupBoldRule(); $rules[] = new PhutilRemarkupItalicRule(); $rules[] = new PhutilRemarkupDelRule(); $rules[] = new PhutilRemarkupUnderlineRule(); $rules[] = new PhutilRemarkupHighlightRule(); $blocks = array(); $blocks[] = new PhutilRemarkupQuotesBlockRule(); $blocks[] = new PhutilRemarkupReplyBlockRule(); $blocks[] = new PhutilRemarkupHeaderBlockRule(); $blocks[] = new PhutilRemarkupHorizontalRuleBlockRule(); $blocks[] = new PhutilRemarkupCodeBlockRule(); $blocks[] = new PhutilRemarkupLiteralBlockRule(); $blocks[] = new PhutilRemarkupNoteBlockRule(); $blocks[] = new PhutilRemarkupTableBlockRule(); $blocks[] = new PhutilRemarkupSimpleTableBlockRule(); $blocks[] = new PhutilRemarkupDefaultBlockRule(); $blocks[] = new PhutilRemarkupListBlockRule(); $blocks[] = new PhutilRemarkupInterpreterBlockRule(); foreach ($blocks as $block) { if (!($block instanceof PhutilRemarkupCodeBlockRule)) { $block->setMarkupRules($rules); } } $engine->setBlockRules($blocks); return $engine; } + + private function unescapeTrailingWhitespace($input) { + // Remove up to one "~" at the end of each line so trailing whitespace may + // be written in tests as " ~". + return preg_replace('/~$/m', '', $input); + } + } diff --git a/src/markup/engine/__tests__/remarkup/del.txt b/src/markup/engine/__tests__/remarkup/del.txt index c1120bf..955ad86 100644 --- a/src/markup/engine/__tests__/remarkup/del.txt +++ b/src/markup/engine/__tests__/remarkup/del.txt @@ -1,11 +1,11 @@ -omg~~ wtf~~~~~ bbq~~~ lol~~ -~~deleted text~~ +omg~~ wtf~~~~~ bbq~~~ lol~~~ +~~deleted text~~~ ~~This is a great idea~~~ die forever please -~~~~~~ +~~~~~~~ ~~~~~~~~~~ -

omg~~ wtf~~~~~ bbq~~~ lol~~ +

omg~~ wtf~~~~~ bbq~~~ lol~~~ deleted text This is a great idea~ die forever please ~~~~~~

~~~~~~~~~~ -omg~~ wtf~~~~~ bbq~~~ lol~~ ~~deleted text~~ ~~This is a great idea~~~ die forever please ~~~~~~ +omg~~ wtf~~~~~ bbq~~~ lol~~ ~~deleted text~~ ~~This is a great idea~~~ die forever please ~~~~~~~ diff --git a/src/markup/engine/__tests__/remarkup/link-with-tilde.txt b/src/markup/engine/__tests__/remarkup/link-with-tilde.txt index 9f48f60..615814d 100644 --- a/src/markup/engine/__tests__/remarkup/link-with-tilde.txt +++ b/src/markup/engine/__tests__/remarkup/link-with-tilde.txt @@ -1,5 +1,5 @@ -http://www.example.com/~ +http://www.example.com/~~ ~~~~~~~~~~

http://www.example.com/~

~~~~~~~~~~ -http://www.example.com/~ +http://www.example.com/~~ diff --git a/src/markup/engine/__tests__/remarkup/quoted-indent-block.txt b/src/markup/engine/__tests__/remarkup/quoted-indent-block.txt new file mode 100644 index 0000000..80a7428 --- /dev/null +++ b/src/markup/engine/__tests__/remarkup/quoted-indent-block.txt @@ -0,0 +1,5 @@ +> xyz +~~~~~~~~~~ +
xyz
+~~~~~~~~~~ +> xyz diff --git a/src/markup/engine/__tests__/remarkup/quoted-lists.txt b/src/markup/engine/__tests__/remarkup/quoted-lists.txt new file mode 100644 index 0000000..bfe622b --- /dev/null +++ b/src/markup/engine/__tests__/remarkup/quoted-lists.txt @@ -0,0 +1,24 @@ +> # X +> # Y +> +> B +> +> * C +~~~~~~~~~~ +
    +
  1. X
  2. +
  3. Y
  4. +
+ +

B

+ +
+~~~~~~~~~~ +> 1. X +> 2. Y +> ~ +> B +> ~ +> - C diff --git a/src/markup/engine/__tests__/remarkup/quotes.txt b/src/markup/engine/__tests__/remarkup/quotes.txt index afa02d5..212f872 100644 --- a/src/markup/engine/__tests__/remarkup/quotes.txt +++ b/src/markup/engine/__tests__/remarkup/quotes.txt @@ -1,9 +1,9 @@ > Dear Sir, > I am utterly disgusted with the quality > of your inflight food service. ~~~~~~~~~~

Dear Sir, - I am utterly disgusted with the quality - of your inflight food service.

+I am utterly disgusted with the quality +of your inflight food service.

~~~~~~~~~~ > Dear Sir, I am utterly disgusted with the quality of your inflight food service. diff --git a/src/markup/engine/__tests__/remarkup/raw-escape.txt b/src/markup/engine/__tests__/remarkup/raw-escape.txt index ee850b3..4cb635e 100644 --- a/src/markup/engine/__tests__/remarkup/raw-escape.txt +++ b/src/markup/engine/__tests__/remarkup/raw-escape.txt @@ -1,17 +1,17 @@ -~1~ +~1~~ ~2Z ~a ~~~~~~~~~~

~1~

~2Z

~a

~~~~~~~~~~ -~1~ +~1~~ ~2Z ~a diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php index 6b0dc3f..7c90b34 100644 --- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php +++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php @@ -1,65 +1,106 @@ /', $lines[$pos])) { do { ++$pos; } while (isset($lines[$pos]) && preg_match('/^>/', $lines[$pos])); } return ($pos - $cursor); } public function supportsChildBlocks() { return true; } public function extractChildText($text) { $text = phutil_split_lines($text, true); foreach ($text as $key => $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)); } 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); } $attributes = array(); if ($this->getEngine()->isHTMLMailMode()) { $style = array( 'border-left: 3px solid #a7b5bf;', 'color: #464c5c;', 'font-style: italic;', 'margin: 4px 0 12px 0;', 'padding: 4px 12px;', 'background-color: #f8f9fc;', ); $attributes['style'] = implode(' ', $style); } return phutil_tag( 'blockquote', $attributes, $children); } }