@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 PhutilRemarkupInterpreterBlockRule extends PhutilRemarkupBlockRule {
4
5 /**
6 * Second part of the regex to find stuff like:
7 * interpreterName {{{ stuff }}}
8 * interpreterName (options) {{{ stuff }}}
9 * You have found the kernel of cowsay and figlet.
10 */
11 const END_BLOCK_PATTERN = '/}}}\s*$/';
12
13 /**
14 * Constructs the first part of the regex to find stuff like:
15 * interpreterName {{{ stuff }}}
16 * interpreterName (options) {{{ stuff }}}
17 * The exact regex is constructed from the available interpreters.
18 * @return string First part of interpreters regex
19 */
20 private function getStartBlockPattern() {
21 $interpreters = id(new PhutilClassMapQuery())
22 ->setAncestorClass(PhutilRemarkupBlockInterpreter::class)
23 ->execute();
24 $interpreters_regex = mpull($interpreters, 'getInterpreterName');
25 $interpreters_regex = array_map('preg_quote', $interpreters_regex);
26 $interpreters_regex = implode('|', $interpreters_regex);
27 return "/^($interpreters_regex)\s*(?:\(([^)]+)\)\s*)?{{{/";
28 }
29
30 public function getMatchingLineCount(array $lines, $cursor) {
31 $num_lines = 0;
32
33 if (preg_match(self::getStartBlockPattern(), $lines[$cursor])) {
34 $num_lines++;
35
36 while (isset($lines[$cursor])) {
37 if (preg_match(self::END_BLOCK_PATTERN, $lines[$cursor])) {
38 break;
39 }
40 $num_lines++;
41 $cursor++;
42 }
43 }
44
45 return $num_lines;
46 }
47
48 public function markupText($text, $children) {
49 $lines = explode("\n", $text);
50 $first_key = head_key($lines);
51 $last_key = last_key($lines);
52 while (trim($lines[$last_key]) === '') {
53 unset($lines[$last_key]);
54 $last_key = last_key($lines);
55 }
56 $matches = null;
57
58 preg_match(self::getStartBlockPattern(), head($lines), $matches);
59
60 $argv = array();
61 if (isset($matches[2])) {
62 $argv = id(new PhutilSimpleOptions())->parse($matches[2]);
63 }
64
65 $interpreters = id(new PhutilClassMapQuery())
66 ->setAncestorClass(PhutilRemarkupBlockInterpreter::class)
67 ->execute();
68
69 foreach ($interpreters as $interpreter) {
70 $interpreter->setEngine($this->getEngine());
71 }
72
73 $lines[$first_key] = preg_replace(
74 self::getStartBlockPattern(),
75 '',
76 $lines[$first_key]);
77 $lines[$last_key] = preg_replace(
78 self::END_BLOCK_PATTERN,
79 '',
80 $lines[$last_key]);
81
82 if (trim($lines[$first_key]) === '') {
83 unset($lines[$first_key]);
84 }
85 if (trim($lines[$last_key]) === '') {
86 unset($lines[$last_key]);
87 }
88
89 $content = implode("\n", $lines);
90
91 $interpreters = mpull($interpreters, null, 'getInterpreterName');
92
93 if (isset($interpreters[$matches[1]])) {
94 return $interpreters[$matches[1]]->markupContent($content, $argv);
95 }
96
97 $message = pht('No interpreter found: %s', $matches[1]);
98
99 if ($this->getEngine()->isTextMode()) {
100 return '('.$message.')';
101 }
102
103 return phutil_tag(
104 'div',
105 array(
106 'class' => 'remarkup-interpreter-error',
107 ),
108 $message);
109 }
110
111}