Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 194 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
| ParagraphWrapper | |
0.00% |
0 / 194 |
|
0.00% |
0 / 14 |
8010 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| onNewline | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
| onEnd | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| reset | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
| resetBuffers | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| resetCurrLine | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
| processBuffers | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
| flushBuffers | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| processOneNlTk | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| openPTag | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
132 | |||
| closeOpenPTag | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
132 | |||
| onNewlineOrEOF | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
72 | |||
| processPendingNLs | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
110 | |||
| onAny | |
0.00% |
0 / 62 |
|
0.00% |
0 / 1 |
1260 | |||
| 1 | <?php |
| 2 | declare( strict_types = 1 ); |
| 3 | |
| 4 | namespace Wikimedia\Parsoid\Wt2Html\TT; |
| 5 | |
| 6 | use Wikimedia\Assert\Assert; |
| 7 | use Wikimedia\Assert\UnreachableException; |
| 8 | use Wikimedia\Parsoid\Tokens\CommentTk; |
| 9 | use Wikimedia\Parsoid\Tokens\EndTagTk; |
| 10 | use Wikimedia\Parsoid\Tokens\EOFTk; |
| 11 | use Wikimedia\Parsoid\Tokens\NlTk; |
| 12 | use Wikimedia\Parsoid\Tokens\SelfclosingTagTk; |
| 13 | use Wikimedia\Parsoid\Tokens\TagTk; |
| 14 | use Wikimedia\Parsoid\Tokens\Token; |
| 15 | use Wikimedia\Parsoid\Utils\PHPUtils; |
| 16 | use Wikimedia\Parsoid\Utils\TokenUtils; |
| 17 | use Wikimedia\Parsoid\Wikitext\Consts; |
| 18 | use Wikimedia\Parsoid\Wt2Html\TokenHandlerPipeline; |
| 19 | |
| 20 | /** |
| 21 | * Insert paragraph tags where needed -- smartly and carefully |
| 22 | * -- there is much fun to be had mimicking "wikitext visual newlines" |
| 23 | * behavior as implemented by the PHP parser. |
| 24 | */ |
| 25 | class ParagraphWrapper extends TokenHandler { |
| 26 | |
| 27 | private bool $inPre = false; |
| 28 | private bool $hasOpenPTag = false; |
| 29 | private bool $inBlockElem = false; |
| 30 | private bool $inBlockquote = false; |
| 31 | |
| 32 | /** |
| 33 | * The state machine in the PreHandler is line based and only suppresses |
| 34 | * indent-pres when encountering blocks on a line. However, the legacy |
| 35 | * parser's `doBlockLevels` has a concept of being "$inBlockElem", which |
| 36 | * is mimicked here. Rather than replicate that awareness in both passes, |
| 37 | * we piggyback on it here to undo indent-pres when they're found to be |
| 38 | * undesirable. |
| 39 | */ |
| 40 | private bool $undoIndentPre = false; |
| 41 | private array $tokenBuffer = []; |
| 42 | private array $nlWsTokens = []; |
| 43 | private int $newLineCount = 0; |
| 44 | |
| 45 | /** @var array */ |
| 46 | private $currLineTokens = []; |
| 47 | /** @var bool */ |
| 48 | private $currLineHasWrappableTokens = false; |
| 49 | /** @var bool */ |
| 50 | private $currLineBlockTagSeen = false; |
| 51 | /** @var bool */ |
| 52 | private $currLineBlockTagOpen = false; |
| 53 | |
| 54 | /** |
| 55 | * Constructor for paragraph wrapper. |
| 56 | * @param TokenHandlerPipeline $manager manager enviroment |
| 57 | * @param array $options various configuration options |
| 58 | */ |
| 59 | public function __construct( TokenHandlerPipeline $manager, array $options ) { |
| 60 | parent::__construct( $manager, $options ); |
| 61 | // Disable p-wrapper |
| 62 | $this->disabled = !empty( $this->options['inlineContext'] ); |
| 63 | $this->reset(); |
| 64 | } |
| 65 | |
| 66 | /** |
| 67 | * @inheritDoc |
| 68 | */ |
| 69 | public function onNewline( NlTk $token ): ?array { |
| 70 | return $this->inPre ? null : $this->onNewlineOrEOF( $token ); |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * @inheritDoc |
| 75 | */ |
| 76 | public function onEnd( EOFTk $token ): ?array { |
| 77 | return $this->onNewlineOrEOF( $token ); |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * Reset the token buffer and related info |
| 82 | * This is the ordering of buffered tokens and how they should get emitted: |
| 83 | * |
| 84 | * token-buffer (from previous lines if newLineCount > 0) |
| 85 | * newline-ws-tokens (buffered nl+sol-transparent tokens since last non-nl-token) |
| 86 | * current-line-tokens (all tokens after newline-ws-tokens) |
| 87 | * |
| 88 | * newline-token-count is > 0 only when we encounter multiple "empty lines". |
| 89 | * |
| 90 | * Periodically, when it is clear where an open/close p-tag is required, the buffers |
| 91 | * are collapsed and emitted. Wherever tokens are buffered/emitted, verify that this |
| 92 | * order is preserved. |
| 93 | */ |
| 94 | private function reset(): void { |
| 95 | $this->resetBuffers(); |
| 96 | $this->resetCurrLine(); |
| 97 | $this->hasOpenPTag = false; |
| 98 | $this->inPre = false; |
| 99 | $this->undoIndentPre = false; |
| 100 | // NOTE: This flag is the local equivalent of what we're mimicking with |
| 101 | // the 'inlineContext' pipeline option. |
| 102 | $this->inBlockElem = false; |
| 103 | $this->inBlockquote = false; |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Reset the token buffer and new line info |
| 108 | * |
| 109 | */ |
| 110 | private function resetBuffers(): void { |
| 111 | $this->tokenBuffer = []; |
| 112 | $this->nlWsTokens = []; |
| 113 | $this->newLineCount = 0; |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Reset the current line info |
| 118 | * |
| 119 | */ |
| 120 | private function resetCurrLine(): void { |
| 121 | if ( $this->currLineBlockTagSeen ) { |
| 122 | $this->inBlockElem = $this->currLineBlockTagOpen; |
| 123 | } |
| 124 | $this->currLineTokens = []; |
| 125 | $this->currLineHasWrappableTokens = false; |
| 126 | $this->currLineBlockTagSeen = false; |
| 127 | $this->currLineBlockTagOpen = false; |
| 128 | } |
| 129 | |
| 130 | /** |
| 131 | * Process the current buffer contents and token provided |
| 132 | * |
| 133 | * @param Token|string $token token |
| 134 | * @param bool $flushCurrentLine option to flush current line or preserve it |
| 135 | * @return array<string|Token> |
| 136 | */ |
| 137 | private function processBuffers( $token, bool $flushCurrentLine ): array { |
| 138 | $res = $this->processPendingNLs(); |
| 139 | $this->currLineTokens[] = $token; |
| 140 | if ( $flushCurrentLine ) { |
| 141 | PHPUtils::pushArray( $res, $this->currLineTokens ); |
| 142 | $this->resetCurrLine(); |
| 143 | } |
| 144 | $this->env->trace( 'p-wrap', $this->pipelineId, '----> ', $res ); |
| 145 | return $res; |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * Process and flush existing buffer contents |
| 150 | * |
| 151 | * @param Token|string $token token |
| 152 | * @return array<string|Token> |
| 153 | */ |
| 154 | private function flushBuffers( $token ): array { |
| 155 | Assert::invariant( $this->newLineCount === 0, "PWrap: Trying to flush buffers with pending newlines" ); |
| 156 | |
| 157 | $this->currLineTokens[] = $token; |
| 158 | // Juggle the array reference count to allow us to append to it without |
| 159 | // copying the array |
| 160 | $resToks = $this->tokenBuffer; |
| 161 | $nlWsTokens = $this->nlWsTokens; |
| 162 | $this->resetBuffers(); |
| 163 | PHPUtils::pushArray( $resToks, $nlWsTokens ); |
| 164 | $this->env->trace( 'p-wrap', $this->pipelineId, '----> ', $resToks ); |
| 165 | return $resToks; |
| 166 | } |
| 167 | |
| 168 | /** |
| 169 | * Append tokens from the newline/whitespace buffer to the output array |
| 170 | * until a newline is encountered. Increment the offset reference. Return |
| 171 | * the newline token. |
| 172 | * |
| 173 | * @param array &$out array to append to |
| 174 | * @param int &$offset The offset reference to update |
| 175 | * @return NlTk |
| 176 | */ |
| 177 | public function processOneNlTk( array &$out, &$offset ) { |
| 178 | $n = count( $this->nlWsTokens ); |
| 179 | while ( $offset < $n ) { |
| 180 | $t = $this->nlWsTokens[$offset++]; |
| 181 | if ( $t instanceof NlTk ) { |
| 182 | return $t; |
| 183 | } else { |
| 184 | $out[] = $t; |
| 185 | } |
| 186 | } |
| 187 | throw new UnreachableException( 'nlWsTokens was expected to contain an NlTk.' ); |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * Search for the opening paragraph tag |
| 192 | * |
| 193 | * @param array &$out array to process and update |
| 194 | */ |
| 195 | private function openPTag( array &$out ): void { |
| 196 | if ( !$this->hasOpenPTag ) { |
| 197 | $tplStartIndex = -1; |
| 198 | // Be careful not to expand template ranges unnecessarily. |
| 199 | // Look for open markers before starting a p-tag. |
| 200 | $countOut = count( $out ); |
| 201 | for ( $i = 0; $i < $countOut; $i++ ) { |
| 202 | $t = $out[$i]; |
| 203 | if ( !is_string( $t ) && $t->getName() === 'meta' ) { |
| 204 | if ( TokenUtils::hasTypeOf( $t, 'mw:Transclusion' ) ) { |
| 205 | // We hit a start tag and everything before it is sol-transparent. |
| 206 | $tplStartIndex = $i; |
| 207 | continue; |
| 208 | } elseif ( TokenUtils::matchTypeOf( $t, '#^mw:Transclusion/#' ) ) { |
| 209 | // End tag. All tokens before this are sol-transparent. |
| 210 | // Let us leave them all out of the p-wrapping. |
| 211 | $tplStartIndex = -1; |
| 212 | continue; |
| 213 | } elseif ( TokenUtils::isAnnotationStartToken( $t ) ) { |
| 214 | break; |
| 215 | } |
| 216 | } |
| 217 | // Not a transclusion meta; Check for nl/sol-transparent tokens |
| 218 | // and leave them out of the p-wrapping. |
| 219 | if ( !TokenUtils::isSolTransparent( $this->env, $t ) && !( $t instanceof NlTk ) ) { |
| 220 | break; |
| 221 | } |
| 222 | } |
| 223 | if ( $tplStartIndex > -1 ) { |
| 224 | $i = $tplStartIndex; |
| 225 | } |
| 226 | array_splice( $out, $i, 0, [ new TagTk( 'p' ) ] ); |
| 227 | $this->hasOpenPTag = true; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | /** |
| 232 | * Search for the closing paragraph tag |
| 233 | * |
| 234 | * @param array &$out array to process and update |
| 235 | */ |
| 236 | private function closeOpenPTag( array &$out ): void { |
| 237 | if ( $this->hasOpenPTag ) { |
| 238 | $tplEndIndex = -1; |
| 239 | // Be careful not to expand template ranges unnecessarily. |
| 240 | // Look for open markers before closing. |
| 241 | for ( $i = count( $out ) - 1; $i > -1; $i-- ) { |
| 242 | $t = $out[$i]; |
| 243 | if ( !is_string( $t ) && $t->getName() === 'meta' ) { |
| 244 | if ( TokenUtils::hasTypeOf( $t, 'mw:Transclusion' ) ) { |
| 245 | // We hit a start tag and everything after it is sol-transparent. |
| 246 | // Don't include the sol-transparent tags OR the start tag. |
| 247 | $tplEndIndex = -1; |
| 248 | continue; |
| 249 | } elseif ( TokenUtils::matchTypeOf( $t, '#^mw:Transclusion/#' ) ) { |
| 250 | // End tag. The rest of the tags past this are sol-transparent. |
| 251 | // Let us leave them all out of the p-wrapping. |
| 252 | $tplEndIndex = $i; |
| 253 | continue; |
| 254 | } elseif ( TokenUtils::isAnnotationEndToken( $t ) ) { |
| 255 | break; |
| 256 | } |
| 257 | } |
| 258 | // Not a transclusion meta; Check for nl/sol-transparent tokens |
| 259 | // and leave them out of the p-wrapping. |
| 260 | if ( !TokenUtils::isSolTransparent( $this->env, $t ) && !( $t instanceof NlTk ) ) { |
| 261 | break; |
| 262 | } |
| 263 | } |
| 264 | if ( $tplEndIndex > -1 ) { |
| 265 | $i = $tplEndIndex; |
| 266 | } |
| 267 | array_splice( $out, $i + 1, 0, [ new EndTagTk( 'p' ) ] ); |
| 268 | $this->hasOpenPTag = false; |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * Handle newline tokens |
| 274 | * @return array<string|Token> |
| 275 | */ |
| 276 | private function onNewlineOrEOF( Token $token ): array { |
| 277 | $this->env->trace( 'p-wrap', $this->pipelineId, 'NL | ', $token ); |
| 278 | if ( $this->currLineBlockTagSeen ) { |
| 279 | $this->closeOpenPTag( $this->currLineTokens ); |
| 280 | } elseif ( !$this->inBlockElem && !$this->hasOpenPTag && $this->currLineHasWrappableTokens ) { |
| 281 | $this->openPTag( $this->currLineTokens ); |
| 282 | } |
| 283 | |
| 284 | // Assertion to catch bugs in p-wrapping; both cannot be true. |
| 285 | if ( $this->newLineCount > 0 && count( $this->currLineTokens ) > 0 ) { |
| 286 | $this->env->log( 'error/p-wrap', 'Failed assertion in onNewlineOrEOF: newline-count:', |
| 287 | $this->newLineCount, '; current line tokens: ', $this->currLineTokens ); |
| 288 | } |
| 289 | |
| 290 | PHPUtils::pushArray( $this->tokenBuffer, $this->currLineTokens ); |
| 291 | |
| 292 | if ( $token instanceof EOFTk ) { |
| 293 | $this->nlWsTokens[] = $token; |
| 294 | $this->closeOpenPTag( $this->tokenBuffer ); |
| 295 | $res = $this->processPendingNLs(); |
| 296 | $this->reset(); |
| 297 | $this->env->trace( 'p-wrap', $this->pipelineId, '----> ', $res ); |
| 298 | return $res; |
| 299 | } else { |
| 300 | $this->resetCurrLine(); |
| 301 | $this->newLineCount++; |
| 302 | $this->nlWsTokens[] = $token; |
| 303 | return []; |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | /** |
| 308 | * Process pending newlines |
| 309 | * |
| 310 | * @return array<string|Token> |
| 311 | */ |
| 312 | private function processPendingNLs(): array { |
| 313 | $resToks = $this->tokenBuffer; |
| 314 | $newLineCount = $this->newLineCount; |
| 315 | $nlTk = null; |
| 316 | $nlOffset = 0; |
| 317 | |
| 318 | $this->env->trace( 'p-wrap', $this->pipelineId, ' NL-count: ', $newLineCount ); |
| 319 | |
| 320 | if ( $newLineCount >= 2 && !$this->inBlockElem ) { |
| 321 | $this->closeOpenPTag( $resToks ); |
| 322 | |
| 323 | // First is emitted as a literal newline |
| 324 | $resToks[] = $this->processOneNlTk( $resToks, $nlOffset ); |
| 325 | $newLineCount -= 1; |
| 326 | |
| 327 | $remainder = $newLineCount % 2; |
| 328 | |
| 329 | while ( $newLineCount > 0 ) { |
| 330 | $nlTk = $this->processOneNlTk( $resToks, $nlOffset ); |
| 331 | if ( $newLineCount % 2 === $remainder ) { |
| 332 | if ( $this->hasOpenPTag ) { |
| 333 | $resToks[] = new EndTagTk( 'p' ); |
| 334 | $this->hasOpenPTag = false; |
| 335 | } |
| 336 | if ( $newLineCount > 1 ) { |
| 337 | $resToks[] = new TagTk( 'p' ); |
| 338 | $this->hasOpenPTag = true; |
| 339 | } |
| 340 | } else { |
| 341 | $resToks[] = new SelfclosingTagTk( 'br' ); |
| 342 | } |
| 343 | $resToks[] = $nlTk; |
| 344 | $newLineCount -= 1; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | if ( $this->currLineBlockTagSeen ) { |
| 349 | $this->closeOpenPTag( $resToks ); |
| 350 | if ( $newLineCount === 1 ) { |
| 351 | $resToks[] = $this->processOneNlTk( $resToks, $nlOffset ); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | // Gather remaining ws and nl tokens |
| 356 | for ( $i = $nlOffset; $i < count( $this->nlWsTokens ); $i++ ) { |
| 357 | $resToks[] = $this->nlWsTokens[$i]; |
| 358 | } |
| 359 | |
| 360 | // reset buffers |
| 361 | $this->resetBuffers(); |
| 362 | |
| 363 | return $resToks; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * @inheritDoc |
| 368 | */ |
| 369 | public function onAny( $token ): ?array { |
| 370 | $this->env->trace( 'p-wrap', $this->pipelineId, 'ANY | ', $token ); |
| 371 | $res = null; |
| 372 | if ( $token instanceof TagTk && $token->getName() === 'pre' |
| 373 | && !TokenUtils::isHTMLTag( $token ) |
| 374 | ) { |
| 375 | if ( $this->inBlockElem || $this->inBlockquote ) { |
| 376 | $this->undoIndentPre = true; |
| 377 | if ( $this->newLineCount === 0 ) { |
| 378 | return $this->flushBuffers( '' ); |
| 379 | } else { |
| 380 | return []; |
| 381 | } |
| 382 | } else { |
| 383 | $this->inPre = true; |
| 384 | // This will put us `inBlockElem`, so we need the extra `!inPre` |
| 385 | // condition below. Presumably, we couldn't have entered |
| 386 | // `inBlockElem` while being `inPre`. Alternatively, we could say |
| 387 | // that indent-pre is "never suppressing" and set the `blockTagOpen` |
| 388 | // flag to false. The point of all this is that we want to close |
| 389 | // any open p-tags. |
| 390 | $this->currLineBlockTagSeen = true; |
| 391 | $this->currLineBlockTagOpen = true; |
| 392 | // skip ensures this doesn't hit the AnyHandler |
| 393 | return $this->processBuffers( $token, true ); |
| 394 | } |
| 395 | } elseif ( $token instanceof EndTagTk && $token->getName() === 'pre' && |
| 396 | !TokenUtils::isHTMLTag( $token ) |
| 397 | ) { |
| 398 | if ( ( $this->inBlockElem && !$this->inPre ) || $this->inBlockquote ) { |
| 399 | $this->undoIndentPre = false; |
| 400 | // No pre-tokens inside block tags -- swallow it. |
| 401 | return []; |
| 402 | } else { |
| 403 | $this->inPre = false; |
| 404 | $this->currLineBlockTagSeen = true; |
| 405 | $this->currLineBlockTagOpen = false; |
| 406 | $this->env->trace( 'p-wrap', $this->pipelineId, '----> ', $token ); |
| 407 | return null; |
| 408 | } |
| 409 | } elseif ( $token instanceof EOFTk || $this->inPre ) { |
| 410 | $this->env->trace( 'p-wrap', $this->pipelineId, '----> ', $token ); |
| 411 | // null is a signal of an unmodified token |
| 412 | return null; |
| 413 | } elseif ( |
| 414 | $token instanceof CommentTk |
| 415 | || ( is_string( $token ) && preg_match( '/^[\t ]*$/D', $token ) ) |
| 416 | || TokenUtils::isEmptyLineMetaToken( $token ) |
| 417 | ) { |
| 418 | if ( $this->newLineCount === 0 ) { |
| 419 | // Since we have no pending newlines to trip us up, |
| 420 | // no need to buffer -- just flush everything |
| 421 | return $this->flushBuffers( $token ); |
| 422 | } else { |
| 423 | // We are in buffering mode waiting till we are ready to |
| 424 | // process pending newlines. |
| 425 | $this->nlWsTokens[] = $token; |
| 426 | return []; |
| 427 | } |
| 428 | } elseif ( |
| 429 | // T186965: <style> behaves similarly to sol transparent tokens in |
| 430 | // that it doesn't open/close paragraphs, but also doesn't induce |
| 431 | // a new paragraph by itself. |
| 432 | TokenUtils::isSolTransparent( $this->env, $token ) || |
| 433 | ( !is_string( $token ) && $token->getName() === 'style' ) |
| 434 | ) { |
| 435 | if ( $this->undoIndentPre && PreHandler::isIndentPreWS( $token ) ) { |
| 436 | $this->nlWsTokens[] = ' '; |
| 437 | return []; |
| 438 | } elseif ( $this->newLineCount === 0 ) { |
| 439 | // Since we have no pending newlines to trip us up, |
| 440 | // no need to buffer -- just flush everything |
| 441 | return $this->flushBuffers( $token ); |
| 442 | } elseif ( $this->newLineCount === 1 ) { |
| 443 | // Swallow newline, whitespace, comments, and the current line |
| 444 | PHPUtils::pushArray( $this->tokenBuffer, $this->nlWsTokens ); |
| 445 | PHPUtils::pushArray( $this->tokenBuffer, $this->currLineTokens ); |
| 446 | $this->newLineCount = 0; |
| 447 | $this->nlWsTokens = []; |
| 448 | $this->resetCurrLine(); |
| 449 | |
| 450 | // But, don't process the new token yet. |
| 451 | $this->currLineTokens[] = $token; |
| 452 | return []; |
| 453 | } else { |
| 454 | return $this->processBuffers( $token, false ); |
| 455 | } |
| 456 | } else { |
| 457 | if ( !is_string( $token ) ) { |
| 458 | $name = $token->getName(); |
| 459 | if ( isset( Consts::$wikitextBlockElems[$name] ) ) { |
| 460 | $this->currLineBlockTagSeen = true; |
| 461 | $this->currLineBlockTagOpen = true; |
| 462 | if ( |
| 463 | ( isset( Consts::$blockElems[$name] ) && $token instanceof EndTagTk ) || |
| 464 | ( isset( Consts::$antiBlockElems[$name] ) && !$token instanceof EndTagTk ) || |
| 465 | isset( Consts::$neverBlockElems[$name] ) |
| 466 | ) { |
| 467 | $this->currLineBlockTagOpen = false; |
| 468 | } |
| 469 | } |
| 470 | if ( $name === 'blockquote' ) { |
| 471 | $this->inBlockquote = !( $token instanceof EndTagTk ); |
| 472 | } |
| 473 | } |
| 474 | $this->currLineHasWrappableTokens = true; |
| 475 | return $this->processBuffers( $token, false ); |
| 476 | } |
| 477 | } |
| 478 | } |