Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
TDHandler
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 4
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handle
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
342
 before
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 after
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Html2Wt\DOMHandlers;
5
6use Wikimedia\Parsoid\DOM\Element;
7use Wikimedia\Parsoid\DOM\Node;
8use Wikimedia\Parsoid\Html2Wt\DiffUtils;
9use Wikimedia\Parsoid\Html2Wt\SerializerState;
10use Wikimedia\Parsoid\Utils\DiffDOMUtils;
11use Wikimedia\Parsoid\Utils\DOMCompat;
12use Wikimedia\Parsoid\Utils\DOMDataUtils;
13
14class TDHandler extends DOMHandler {
15
16    public function __construct() {
17        parent::__construct( false );
18    }
19
20    /** @inheritDoc */
21    public function handle(
22        Element $node, SerializerState $state, bool $wrapperUnmodified = false
23    ): ?Node {
24        $dp = DOMDataUtils::getDataParsoid( $node );
25        $usableDP = $this->stxInfoValidForTableCell( $state, $node );
26        $attrSepSrc = $usableDP ? ( $dp->attrSepSrc ?? null ) : null;
27        $startTagSrc = $usableDP ? ( $dp->startTagSrc ?? null ) : '';
28        if ( !$startTagSrc ) {
29            $startTagSrc = ( $usableDP && ( $dp->stx ?? null ) === 'row' ) ? '||' : '|';
30        }
31
32        // T149209: Special case to deal with scenarios
33        // where the previous sibling put us in a SOL state
34        // (or will put in a SOL state when the separator is emitted)
35        $min = $state->sep->constraints['min'] ?? 0;
36        $max = $state->sep->constraints['max'] ?? 1;
37        if ( $min > 0 || ( $max > 0 && str_contains( $state->sep->src ?? '', "\n" ) ) ) {
38            $startTagSrc = preg_replace( '/\|\|/', '|', $startTagSrc, 1 );
39            $startTagSrc = preg_replace( '/{{!}}{{!}}/', '{{!}}', $startTagSrc, 1 );
40        }
41
42        // If the HTML for the first td is not enclosed in a tr-tag,
43        // we start a new line.  If not, tr will have taken care of it.
44        $tdTag = $this->serializeTableTag(
45            $startTagSrc, $attrSepSrc,
46            $state, $node, $wrapperUnmodified
47        );
48        $inWideTD = (bool)preg_match( '/\|\||^{{!}}{{!}}/', $tdTag );
49        $leadingSpace = $this->getLeadingSpace( $state, $node, '' );
50        $state->emitChunk( $tdTag . $leadingSpace, $node );
51        $tdHandler = static function ( $state, $text, $opts ) use ( $node, $inWideTD ) {
52            return $state->serializer->wteHandlers->tdHandler( $node, $inWideTD, $state, $text, $opts );
53        };
54
55        $nextTd = DiffDOMUtils::nextNonSepSibling( $node );
56        $nextUsesRowSyntax = $nextTd instanceof Element
57            && ( DOMDataUtils::getDataParsoid( $nextTd )->stx ?? null ) === 'row';
58
59        // For empty cells, emit a single whitespace to make wikitext
60        // more readable as well as to eliminate potential misparses.
61        if ( $nextUsesRowSyntax && !DiffDOMUtils::firstNonDeletedChild( $node ) ) {
62            $state->serializer->emitWikitext( ' ', $node );
63            return $node->nextSibling;
64        }
65
66        $state->serializeChildren( $node, $tdHandler );
67
68        if ( !preg_match( '/\s$/D', $state->currLine->text ) ) {
69            $trailingSpace = null;
70            if ( $nextUsesRowSyntax ) {
71                $trailingSpace = $this->getTrailingSpace( $state, $node, '' );
72            }
73            // Recover any trimmed whitespace only on unmodified nodes
74            if ( !$trailingSpace ) {
75                $lastChild = DiffDOMUtils::lastNonSepChild( $node );
76                if ( $lastChild && !DiffUtils::hasDiffMarkers( $lastChild ) ) {
77                    $trailingSpace = $state->recoverTrimmedWhitespace( $node, false );
78                }
79            }
80            if ( $trailingSpace ) {
81                $state->appendSep( $trailingSpace );
82            }
83        }
84
85        return $node->nextSibling;
86    }
87
88    /** @inheritDoc */
89    public function before( Element $node, Node $otherNode, SerializerState $state ): array {
90        $forceSingleLine = DOMCompat::nodeName( $otherNode ) === 'td'
91            && ( DOMDataUtils::getDataParsoid( $node )->stx ?? null ) === 'row';
92        return [ 'min' => $forceSingleLine ? 0 : 1, 'max' => $this->maxNLsInTable( $node, $otherNode ) ];
93    }
94
95    /** @inheritDoc */
96    public function after( Element $node, Node $otherNode, SerializerState $state ): array {
97        return [ 'min' => 0, 'max' => $this->maxNLsInTable( $node, $otherNode ) ];
98    }
99
100}