Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14706579
D8953.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D8953.id.diff
View Options
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
@@ -257,6 +257,7 @@
'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php',
'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php',
'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php',
+ 'PhutilRemarkupEngineRemarkupReplyBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupReplyBlockRule.php',
'PhutilRemarkupEngineRemarkupSimpleTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupSimpleTableBlockRule.php',
'PhutilRemarkupEngineRemarkupTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php',
'PhutilRemarkupEngineRemarkupTestInterpreterRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTestInterpreterRule.php',
@@ -628,6 +629,7 @@
'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'PhutilRemarkupEngineBlockRule',
'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'PhutilRemarkupEngineBlockRule',
'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'PhutilRemarkupEngineBlockRule',
+ 'PhutilRemarkupEngineRemarkupReplyBlockRule' => 'PhutilRemarkupEngineBlockRule',
'PhutilRemarkupEngineRemarkupSimpleTableBlockRule' => 'PhutilRemarkupEngineBlockRule',
'PhutilRemarkupEngineRemarkupTableBlockRule' => 'PhutilRemarkupEngineBlockRule',
'PhutilRemarkupEngineRemarkupTestInterpreterRule' => 'PhutilRemarkupBlockInterpreter',
diff --git a/src/markup/engine/PhutilRemarkupEngine.php b/src/markup/engine/PhutilRemarkupEngine.php
--- a/src/markup/engine/PhutilRemarkupEngine.php
+++ b/src/markup/engine/PhutilRemarkupEngine.php
@@ -1,16 +1,12 @@
<?php
-/**
- * @group markup
- */
final class PhutilRemarkupEngine extends PhutilMarkupEngine {
const MODE_DEFAULT = 0;
const MODE_TEXT = 1;
- /**
- * @var PhutilRemarkupEngineBlockRule[]
- */
+ const MAX_CHILD_DEPTH = 8;
+
private $blockRules = array();
private $config = array();
private $mode;
@@ -119,14 +115,35 @@
$this->metadata = array();
$this->storage = new PhutilRemarkupBlockStorage();
+ $blocks = $this->splitTextIntoBlocks($text);
+
+ $output = array();
+ foreach ($blocks as $block) {
+ $output[] = $this->markupBlock($block);
+ }
+ $output = $this->flattenOutput($output);
+
+ $map = $this->storage->getMap();
+ unset($this->storage);
+ $metadata = $this->metadata;
+
+
+ return array(
+ 'output' => $output,
+ 'storage' => $map,
+ 'metadata' => $metadata,
+ );
+ }
+
+ private function splitTextIntoBlocks($text, $depth = 0) {
// Apply basic block and paragraph normalization to the text. NOTE: We don't
// strip trailing whitespace because it is semantic in some contexts,
// notably inlined diffs that the author intends to show as a code block.
- $text = phutil_split_lines($text, true);
+ $text = phutil_split_lines($text, true);
$block_rules = $this->blockRules;
- $blocks = array();
- $cursor = 0;
- $prev_block = array();
+ $blocks = array();
+ $cursor = 0;
+ $prev_block = array();
while (isset($text[$cursor])) {
$starting_cursor = $cursor;
@@ -139,10 +156,11 @@
}
$curr_block = array(
- "start" => $cursor,
- "num_lines" => $num_lines,
- "rule" => $block_rule,
- "is_empty" => self::isEmptyBlock($text, $cursor, $num_lines),
+ 'start' => $cursor,
+ 'num_lines' => $num_lines,
+ 'rule' => $block_rule,
+ 'is_empty' => self::isEmptyBlock($text, $cursor, $num_lines),
+ 'children' => array(),
);
if ($prev_block
@@ -164,27 +182,58 @@
}
}
- $output = array();
- foreach ($blocks as $block) {
- $output[] = $block['rule']->markupText(
- implode('', array_slice($text, $block['start'], $block['num_lines'])));
+ foreach ($blocks as $key => $block) {
+ $lines = array_slice($text, $block['start'], $block['num_lines']);
+ $blocks[$key]['text'] = implode('', $lines);
}
- $map = $this->storage->getMap();
- unset($this->storage);
- $metadata = $this->metadata;
+ // Stop splitting child blocks apart if we get too deep. This arrests
+ // any blocks which have looping child rules, and stops the stack from
+ // exploding if someone writes a hilarious comment with 5,000 levels of
+ // quoted text.
+
+ if ($depth < self::MAX_CHILD_DEPTH) {
+ foreach ($blocks as $key => $block) {
+ $rule = $block['rule'];
+ if (!$rule->supportsChildBlocks()) {
+ continue;
+ }
+
+ list($parent_text, $child_text) = $rule->extractChildText(
+ $block['text']);
+ $blocks[$key]['text'] = $parent_text;
+ $blocks[$key]['children'] = $this->splitTextIntoBlocks(
+ $child_text,
+ $depth + 1);
+ }
+ }
+
+ return $blocks;
+ }
+
+ private function markupBlock(array $block) {
+ $children = array();
+ foreach ($block['children'] as $child) {
+ $children[] = $this->markupBlock($child);
+ }
+ if ($children) {
+ $children = $this->flattenOutput($children);
+ } else {
+ $children = null;
+ }
+
+ return $block['rule']->markupText($block['text'], $children);
+ }
+
+ private function flattenOutput(array $output) {
if ($this->isTextMode()) {
$output = implode("\n\n", $output)."\n";
} else {
$output = phutil_implode_html("\n\n", $output);
}
- return array(
- 'output' => $output,
- 'storage' => $map,
- 'metadata' => $metadata,
- );
+ return $output;
}
private static function shouldMergeBlocks($text, $prev_block, $curr_block) {
diff --git a/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php b/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php
--- a/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php
+++ b/src/markup/engine/__tests__/PhutilRemarkupEngineTestCase.php
@@ -90,6 +90,7 @@
$blocks = array();
$blocks[] = new PhutilRemarkupEngineRemarkupQuotesBlockRule();
+ $blocks[] = new PhutilRemarkupEngineRemarkupReplyBlockRule();
$blocks[] = new PhutilRemarkupEngineRemarkupHeaderBlockRule();
$blocks[] = new PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule();
$blocks[] = new PhutilRemarkupEngineRemarkupCodeBlockRule();
diff --git a/src/markup/engine/__tests__/remarkup/reply-basic.txt b/src/markup/engine/__tests__/remarkup/reply-basic.txt
new file mode 100644
--- /dev/null
+++ b/src/markup/engine/__tests__/remarkup/reply-basic.txt
@@ -0,0 +1,12 @@
+>>! In comment #123, alincoln wrote:
+> Four score and twenty years ago...
+~~~~~~~~~~
+<blockquote class="remarkup-reply-block">
+<div class="remarkup-reply-head">In comment #123, alincoln wrote:</div>
+<div class="remarkup-reply-body"><p>Four score and twenty years ago...</p></div>
+</blockquote>
+~~~~~~~~~~
+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
new file mode 100644
--- /dev/null
+++ b/src/markup/engine/__tests__/remarkup/reply-nested.txt
@@ -0,0 +1,50 @@
+>>! Previously, fruit:
+>
+> - Apple
+> - Banana
+> - Cherry
+>
+>>>! More previously, vegetables:
+>>
+>> - Potato
+>> - Potato
+>> - Potato
+>
+> The end.
+
+~~~~~~~~~~
+<blockquote class="remarkup-reply-block">
+<div class="remarkup-reply-head">Previously, fruit:</div>
+<div class="remarkup-reply-body"><ul>
+<li>Apple</li>
+<li>Banana</li>
+<li>Cherry</li>
+</ul>
+
+<blockquote class="remarkup-reply-block">
+<div class="remarkup-reply-head">More previously, vegetables:</div>
+<div class="remarkup-reply-body"><ul>
+<li>Potato</li>
+<li>Potato</li>
+<li>Potato</li>
+</ul></div>
+</blockquote>
+
+<p>The end.</p></div>
+</blockquote>
+~~~~~~~~~~
+Previously, fruit:
+
+> - Apple
+> - Banana
+> - Cherry
+>
+> More previously, vegetables:
+>
+> > - Potato
+> > - Potato
+> > - Potato
+>
+>
+> The end.
+
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineBlockRule.php
@@ -24,7 +24,7 @@
return 500.0;
}
- abstract public function markupText($text);
+ abstract public function markupText($text, $children);
/**
* This will get an array of unparsed lines and return the number of lines
@@ -84,6 +84,14 @@
return $text;
}
+ public function supportsChildBlocks() {
+ return false;
+ }
+
+ public function extractChildText($text) {
+ throw new Exception(pht('Not implemnted!'));
+ }
+
protected function renderRemarkupTable(array $out_rows) {
assert_instances_of($out_rows, 'array');
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupCodeBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupCodeBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupCodeBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupCodeBlockRule.php
@@ -46,7 +46,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
if (preg_match('/^```/', $text)) {
// If this is a ```-style block, trim off the backticks and any leading
// blank line.
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupDefaultBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupDefaultBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupDefaultBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupDefaultBlockRule.php
@@ -14,7 +14,7 @@
return 1;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$text = trim($text);
$text = $this->applyRules($text);
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHeaderBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHeaderBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHeaderBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHeaderBlockRule.php
@@ -33,7 +33,7 @@
const KEY_HEADER_TOC = 'headers.toc';
- public function markupText($text) {
+ public function markupText($text, $children) {
$text = trim($text);
$lines = phutil_split_lines($text);
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHorizontalRuleBlockRule.php
@@ -29,7 +29,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
return phutil_tag('hr', array());
}
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInlineBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInlineBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInlineBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInlineBlockRule.php
@@ -10,7 +10,7 @@
return 1;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
return $this->applyRules($text);
}
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInterpreterRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInterpreterRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInterpreterRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInterpreterRule.php
@@ -27,7 +27,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$lines = explode("\n", $text);
$first_key = head_key($lines);
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupListBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupListBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupListBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupListBlockRule.php
@@ -58,7 +58,7 @@
const CONT_BLOCK_PATTERN = '@^\s*(?:[-*#]+|[0-9]+[.)]|\[.?\])\s+@';
const STRIP_BLOCK_PATTERN = '@^\s*(?:[-*#]+|[0-9]+[.)])\s*@';
- public function markupText($text) {
+ public function markupText($text, $children) {
$items = array();
$lines = explode("\n", $text);
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php
@@ -24,7 +24,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$text = preg_replace('/%%%\s*$/', '', substr($text, 3));
if ($this->getEngine()->isTextMode()) {
return $text;
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php
@@ -26,7 +26,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$matches = array();
preg_match($this->getRegEx(), $text, $matches);
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php
@@ -27,7 +27,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$lines = array();
foreach (explode("\n", $text) as $line) {
$lines[] = $this->applyRules(preg_replace('/^>\s*/', '', $line));
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupReplyBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupReplyBlockRule.php
new file mode 100644
--- /dev/null
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupReplyBlockRule.php
@@ -0,0 +1,93 @@
+<?php
+
+final class PhutilRemarkupEngineRemarkupReplyBlockRule
+ extends PhutilRemarkupEngineBlockRule {
+
+ public function getPriority() {
+ return 400.0;
+ }
+
+ public function getMatchingLineCount(array $lines, $cursor) {
+ $pos = $cursor;
+
+ if (preg_match('/^>>!/', $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);
+
+ $head = array();
+ $body = array();
+
+ $head = substr(reset($text), 3);
+
+ $body = array_slice($text, 1);
+
+ // 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));
+ }
+
+ 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);
+
+ return $text."\n\n".$children;
+ }
+
+ return phutil_tag(
+ 'blockquote',
+ array(
+ 'class' => 'remarkup-reply-block',
+ ),
+ array(
+ "\n",
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'remarkup-reply-head',
+ ),
+ $text),
+ "\n",
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'remarkup-reply-body',
+ ),
+ $children),
+ "\n",
+ ));
+ }
+
+}
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupSimpleTableBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupSimpleTableBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupSimpleTableBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupSimpleTableBlockRule.php
@@ -20,7 +20,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$matches = array();
$rows = array();
diff --git a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php
--- a/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php
+++ b/src/markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php
@@ -25,7 +25,7 @@
return $num_lines;
}
- public function markupText($text) {
+ public function markupText($text, $children) {
$matches = array();
if (!preg_match('@^<table>(.*)</table>$@si', $text, $matches)) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jan 18, 2:16 AM (12 h, 11 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6999796
Default Alt Text
D8953.id.diff (18 KB)
Attached To
Mode
D8953: Introduce a quoted reply block rule for Remarkup
Attached
Detach File
Event Timeline
Log In to Comment