[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2019 The s9e Authors 6 * @license http://www.opensource.org/licenses/mit-license.php The MIT License 7 */ 8 namespace s9e\TextFormatter\Plugins\Litedown\Parser\Passes; 9 use s9e\TextFormatter\Parser as Rules; 10 class Blocks extends AbstractPass 11 { 12 protected $setextLines = []; 13 public function parse() 14 { 15 $this->matchSetextLines(); 16 $codeFence = \null; 17 $codeIndent = 4; 18 $codeTag = \null; 19 $lineIsEmpty = \true; 20 $lists = []; 21 $listsCnt = 0; 22 $newContext = \false; 23 $quotes = []; 24 $quotesCnt = 0; 25 $textBoundary = 0; 26 $regexp = '/^(?:(?=[-*+\\d \\t>`~#_])((?: {0,3}> ?)+)?([ \\t]+)?(\\* *\\* *\\*[* ]*$|- *- *-[- ]*$|_ *_ *_[_ ]*$|=+$)?((?:[-*+]|\\d+\\.)[ \\t]+(?=\\S))?[ \\t]*(#{1,6}[ \\t]+|```+[^`\\n]*$|~~~+[^~\\n]*$)?)?/m'; 27 \preg_match_all($regexp, $this->text, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER); 28 foreach ($matches as $m) 29 { 30 $matchPos = $m[0][1]; 31 $matchLen = \strlen($m[0][0]); 32 $ignoreLen = 0; 33 $quoteDepth = 0; 34 $continuation = !$lineIsEmpty; 35 $lfPos = $this->text->indexOf("\n", $matchPos); 36 $lineIsEmpty = ($lfPos === $matchPos + $matchLen && empty($m[3][0]) && empty($m[4][0]) && empty($m[5][0])); 37 $breakParagraph = ($lineIsEmpty && $continuation); 38 if (!empty($m[1][0])) 39 { 40 $quoteDepth = \substr_count($m[1][0], '>'); 41 $ignoreLen = \strlen($m[1][0]); 42 if (isset($codeTag) && $codeTag->hasAttribute('quoteDepth')) 43 { 44 $quoteDepth = \min($quoteDepth, $codeTag->getAttribute('quoteDepth')); 45 $ignoreLen = $this->computeQuoteIgnoreLen($m[1][0], $quoteDepth); 46 } 47 $this->text->overwrite($matchPos, $ignoreLen); 48 } 49 if ($quoteDepth < $quotesCnt && !$continuation) 50 { 51 $newContext = \true; 52 do 53 { 54 $this->parser->addEndTag('QUOTE', $textBoundary, 0) 55 ->pairWith(\array_pop($quotes)); 56 } 57 while ($quoteDepth < --$quotesCnt); 58 } 59 if ($quoteDepth > $quotesCnt && !$lineIsEmpty) 60 { 61 $newContext = \true; 62 do 63 { 64 $tag = $this->parser->addStartTag('QUOTE', $matchPos, 0, $quotesCnt - 999); 65 $quotes[] = $tag; 66 } 67 while ($quoteDepth > ++$quotesCnt); 68 } 69 $indentWidth = 0; 70 $indentPos = 0; 71 if (!empty($m[2][0]) && !$codeFence) 72 { 73 $indentStr = $m[2][0]; 74 $indentLen = \strlen($indentStr); 75 do 76 { 77 if ($indentStr[$indentPos] === ' ') 78 ++$indentWidth; 79 else 80 $indentWidth = ($indentWidth + 4) & ~3; 81 } 82 while (++$indentPos < $indentLen && $indentWidth < $codeIndent); 83 } 84 if (isset($codeTag) && !$codeFence && $indentWidth < $codeIndent && !$lineIsEmpty) 85 $newContext = \true; 86 if ($newContext) 87 { 88 $newContext = \false; 89 if (isset($codeTag)) 90 { 91 if ($textBoundary > $codeTag->getPos()) 92 { 93 $this->text->overwrite($codeTag->getPos(), $textBoundary - $codeTag->getPos()); 94 $endTag = $this->parser->addEndTag('CODE', $textBoundary, 0, -1); 95 $endTag->pairWith($codeTag); 96 } 97 else 98 $codeTag->invalidate(); 99 $codeTag = \null; 100 $codeFence = \null; 101 } 102 foreach ($lists as $list) 103 $this->closeList($list, $textBoundary); 104 $lists = []; 105 $listsCnt = 0; 106 if ($matchPos) 107 $this->text->markBoundary($matchPos - 1); 108 } 109 if ($indentWidth >= $codeIndent) 110 { 111 if (isset($codeTag) || !$continuation) 112 { 113 $ignoreLen += $indentPos; 114 if (!isset($codeTag)) 115 $codeTag = $this->parser->addStartTag('CODE', $matchPos + $ignoreLen, 0, -999); 116 $m = []; 117 } 118 } 119 else 120 { 121 $hasListItem = !empty($m[4][0]); 122 if (!$indentWidth && !$continuation && !$hasListItem) 123 $listIndex = -1; 124 elseif ($continuation && !$hasListItem) 125 $listIndex = $listsCnt - 1; 126 elseif (!$listsCnt) 127 $listIndex = ($hasListItem) ? 0 : -1; 128 else 129 { 130 $listIndex = 0; 131 while ($listIndex < $listsCnt && $indentWidth > $lists[$listIndex]['maxIndent']) 132 ++$listIndex; 133 } 134 while ($listIndex < $listsCnt - 1) 135 { 136 $this->closeList(\array_pop($lists), $textBoundary); 137 --$listsCnt; 138 } 139 if ($listIndex === $listsCnt && !$hasListItem) 140 --$listIndex; 141 if ($hasListItem && $listIndex >= 0) 142 { 143 $breakParagraph = \true; 144 $tagPos = $matchPos + $ignoreLen + $indentPos; 145 $tagLen = \strlen($m[4][0]); 146 $itemTag = $this->parser->addStartTag('LI', $tagPos, $tagLen); 147 $this->text->overwrite($tagPos, $tagLen); 148 if ($listIndex < $listsCnt) 149 { 150 $this->parser->addEndTag('LI', $textBoundary, 0) 151 ->pairWith($lists[$listIndex]['itemTag']); 152 $lists[$listIndex]['itemTag'] = $itemTag; 153 $lists[$listIndex]['itemTags'][] = $itemTag; 154 } 155 else 156 { 157 ++$listsCnt; 158 if ($listIndex) 159 { 160 $minIndent = $lists[$listIndex - 1]['maxIndent'] + 1; 161 $maxIndent = \max($minIndent, $listIndex * 4); 162 } 163 else 164 { 165 $minIndent = 0; 166 $maxIndent = $indentWidth; 167 } 168 $listTag = $this->parser->addStartTag('LIST', $tagPos, 0); 169 if (\strpos($m[4][0], '.') !== \false) 170 { 171 $listTag->setAttribute('type', 'decimal'); 172 $start = (int) $m[4][0]; 173 if ($start !== 1) 174 $listTag->setAttribute('start', $start); 175 } 176 $lists[] = [ 177 'listTag' => $listTag, 178 'itemTag' => $itemTag, 179 'itemTags' => [$itemTag], 180 'minIndent' => $minIndent, 181 'maxIndent' => $maxIndent, 182 'tight' => \true 183 ]; 184 } 185 } 186 if ($listsCnt && !$continuation && !$lineIsEmpty) 187 if (\count($lists[0]['itemTags']) > 1 || !$hasListItem) 188 { 189 foreach ($lists as &$list) 190 $list['tight'] = \false; 191 unset($list); 192 } 193 $codeIndent = ($listsCnt + 1) * 4; 194 } 195 if (isset($m[5])) 196 { 197 if ($m[5][0][0] === '#') 198 { 199 $startLen = \strlen($m[5][0]); 200 $startPos = $matchPos + $matchLen - $startLen; 201 $endLen = $this->getAtxHeaderEndTagLen($matchPos + $matchLen, $lfPos); 202 $endPos = $lfPos - $endLen; 203 $this->parser->addTagPair('H' . \strspn($m[5][0], '#', 0, 6), $startPos, $startLen, $endPos, $endLen); 204 $this->text->markBoundary($startPos); 205 $this->text->markBoundary($lfPos); 206 if ($continuation) 207 $breakParagraph = \true; 208 } 209 elseif ($m[5][0][0] === '`' || $m[5][0][0] === '~') 210 { 211 $tagPos = $matchPos + $ignoreLen; 212 $tagLen = $lfPos - $tagPos; 213 if (isset($codeTag) && $m[5][0] === $codeFence) 214 { 215 $endTag = $this->parser->addEndTag('CODE', $tagPos, $tagLen, -1); 216 $endTag->pairWith($codeTag); 217 $this->parser->addIgnoreTag($textBoundary, $tagPos - $textBoundary); 218 $this->text->overwrite($codeTag->getPos(), $tagPos + $tagLen - $codeTag->getPos()); 219 $codeTag = \null; 220 $codeFence = \null; 221 } 222 elseif (!isset($codeTag)) 223 { 224 $codeTag = $this->parser->addStartTag('CODE', $tagPos, $tagLen); 225 $codeFence = \substr($m[5][0], 0, \strspn($m[5][0], '`~')); 226 $codeTag->setAttribute('quoteDepth', $quoteDepth); 227 $this->parser->addIgnoreTag($tagPos + $tagLen, 1); 228 $lang = \trim(\trim($m[5][0], '`~')); 229 if ($lang !== '') 230 $codeTag->setAttribute('lang', $lang); 231 } 232 } 233 } 234 elseif (!empty($m[3][0]) && !$listsCnt && $this->text->charAt($matchPos + $matchLen) !== "\x17") 235 { 236 $this->parser->addSelfClosingTag('HR', $matchPos + $ignoreLen, $matchLen - $ignoreLen); 237 $breakParagraph = \true; 238 $this->text->markBoundary($lfPos); 239 } 240 elseif (isset($this->setextLines[$lfPos]) && $this->setextLines[$lfPos]['quoteDepth'] === $quoteDepth && !$lineIsEmpty && !$listsCnt && !isset($codeTag)) 241 { 242 $this->parser->addTagPair( 243 $this->setextLines[$lfPos]['tagName'], 244 $matchPos + $ignoreLen, 245 0, 246 $this->setextLines[$lfPos]['endPos'], 247 $this->setextLines[$lfPos]['endLen'] 248 ); 249 $this->text->markBoundary($this->setextLines[$lfPos]['endPos'] + $this->setextLines[$lfPos]['endLen']); 250 } 251 if ($breakParagraph) 252 { 253 $this->parser->addParagraphBreak($textBoundary); 254 $this->text->markBoundary($textBoundary); 255 } 256 if (!$lineIsEmpty) 257 $textBoundary = $lfPos; 258 if ($ignoreLen) 259 $this->parser->addIgnoreTag($matchPos, $ignoreLen, 1000); 260 } 261 } 262 protected function closeList(array $list, $textBoundary) 263 { 264 $this->parser->addEndTag('LIST', $textBoundary, 0)->pairWith($list['listTag']); 265 $this->parser->addEndTag('LI', $textBoundary, 0)->pairWith($list['itemTag']); 266 if ($list['tight']) 267 foreach ($list['itemTags'] as $itemTag) 268 $itemTag->removeFlags(Rules::RULE_CREATE_PARAGRAPHS); 269 } 270 protected function computeQuoteIgnoreLen($str, $maxQuoteDepth) 271 { 272 $remaining = $str; 273 while (--$maxQuoteDepth >= 0) 274 $remaining = \preg_replace('/^ *> ?/', '', $remaining); 275 return \strlen($str) - \strlen($remaining); 276 } 277 protected function getAtxHeaderEndTagLen($startPos, $endPos) 278 { 279 $content = \substr($this->text, $startPos, $endPos - $startPos); 280 \preg_match('/[ \\t]*#*[ \\t]*$/', $content, $m); 281 return \strlen($m[0]); 282 } 283 protected function matchSetextLines() 284 { 285 if ($this->text->indexOf('-') === \false && $this->text->indexOf('=') === \false) 286 return; 287 $regexp = '/^(?=[-=>])(?:> ?)*(?=[-=])(?:-+|=+) *$/m'; 288 if (!\preg_match_all($regexp, $this->text, $matches, \PREG_OFFSET_CAPTURE)) 289 return; 290 foreach ($matches[0] as $_4b034d25) 291 { 292 list($match, $matchPos) = $_4b034d25; 293 $endPos = $matchPos - 1; 294 while ($endPos > 0 && $this->text->charAt($endPos - 1) === ' ') 295 --$endPos; 296 $this->setextLines[$matchPos - 1] = [ 297 'endLen' => $matchPos + \strlen($match) - $endPos, 298 'endPos' => $endPos, 299 'quoteDepth' => \substr_count($match, '>'), 300 'tagName' => ($match[0] === '=') ? 'H1' : 'H2' 301 ]; 302 } 303 } 304 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |