@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhutilRemarkupLiteralBlockRule extends PhutilRemarkupBlockRule {
4
5 public function getPriority() {
6 return 450;
7 }
8
9 public function getMatchingLineCount(array $lines, $cursor) {
10 // NOTE: We're consuming all contiguous blocks of %%% literals, so this:
11 //
12 // %%%a%%%
13 // %%%b%%%
14 //
15 // ...is equivalent to:
16 //
17 // %%%a
18 // b%%%
19 //
20 // If they are separated by a blank newline, they are parsed as two
21 // different blocks. This more clearly represents the original text in the
22 // output text and assists automated escaping of blocks coming into the
23 // system.
24
25 $start_pattern = '(^\s*%%%)';
26 $end_pattern = '(%%%\s*$)';
27 $trivial_pattern = '(^\s*%%%\s*$)';
28
29 if (!preg_match($start_pattern, $lines[$cursor])) {
30 return 0;
31 }
32
33 $start_cursor = $cursor;
34
35 $found_empty = false;
36 $block_start = null;
37 while (true) {
38 if (!isset($lines[$cursor])) {
39 break;
40 }
41
42 $line = $lines[$cursor];
43
44 if ($block_start === null) {
45 $is_start = preg_match($start_pattern, $line);
46
47 // If we've matched a block and then consumed one or more empty lines
48 // after it, stop merging more blocks into the match.
49 if ($found_empty) {
50 break;
51 }
52
53 if ($is_start) {
54 $block_start = $cursor;
55 }
56 }
57
58 if ($block_start !== null) {
59 $is_end = preg_match($end_pattern, $line);
60
61 // If a line contains only "%%%", it will match both the start and
62 // end patterns, but it only counts as a block start.
63 if ($is_end && ($cursor === $block_start)) {
64 $is_trivial = preg_match($trivial_pattern, $line);
65 if ($is_trivial) {
66 $is_end = false;
67 }
68 }
69
70 if ($is_end) {
71 $block_start = null;
72 $cursor++;
73 continue;
74 }
75 }
76
77 if ($block_start === null) {
78 if (strlen(trim($line))) {
79 break;
80 }
81 $found_empty = true;
82 }
83
84 $cursor++;
85 }
86
87 return ($cursor - $start_cursor);
88 }
89
90 public function markupText($text, $children) {
91 $text = rtrim($text);
92 $text = phutil_split_lines($text, $retain_endings = true);
93 foreach ($text as $key => $line) {
94 $line = preg_replace('/^\s*%%%/', '', $line);
95 $line = preg_replace('/%%%(\s*)\z/', '\1', $line);
96 $text[$key] = $line;
97 }
98
99 if ($this->getEngine()->isTextMode()) {
100 return implode('', $text);
101 }
102
103 return phutil_tag(
104 'p',
105 array(
106 'class' => 'remarkup-literal',
107 ),
108 phutil_implode_html(phutil_tag('br', array()), $text));
109 }
110
111}