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
THHandler
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 4
600
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 / 36
0.00% covered (danger)
0.00%
0 / 1
342
 before
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 after
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
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 THHandler 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            // You can use both "!!" and "||" for same-row headings (ugh!)
39            $startTagSrc = preg_replace( '/!!/', '!', $startTagSrc, 1 );
40            $startTagSrc = preg_replace( '/\|\|/', '!', $startTagSrc, 1 );
41            $startTagSrc = preg_replace( '/{{!}}{{!}}/', '{{!}}', $startTagSrc, 1 );
42        }
43
44        $thTag = $this->serializeTableTag( $startTagSrc, $attrSepSrc, $state, $node, $wrapperUnmodified );
45        $leadingSpace = $this->getLeadingSpace( $state, $node, '' );
46        // If the HTML for the first th is not enclosed in a tr-tag,
47        // we start a new line.  If not, tr will have taken care of it.
48        $state->emitChunk( $thTag . $leadingSpace, $node );
49        $thHandler = static function ( $state, $text, $opts ) use ( $node ) {
50            return $state->serializer->wteHandlers->thHandler( $node, $state, $text, $opts );
51        };
52
53        $nextTh = DiffDOMUtils::nextNonSepSibling( $node );
54        $nextUsesRowSyntax = $nextTh instanceof Element
55            && ( DOMDataUtils::getDataParsoid( $nextTh )->stx ?? null ) === 'row';
56
57        // For empty cells, emit a single whitespace to make wikitext
58        // more readable as well as to eliminate potential misparses.
59        if ( $nextUsesRowSyntax && !DiffDOMUtils::firstNonDeletedChild( $node ) ) {
60            $state->serializer->emitWikitext( ' ', $node );
61            return $node->nextSibling;
62        }
63
64        $state->serializeChildren( $node, $thHandler );
65
66        // PORT-FIXME does regexp whitespace semantics change matter?
67        if ( !preg_match( '/\s$/D', $state->currLine->text ) ) {
68            $trailingSpace = null;
69            if ( $nextUsesRowSyntax ) {
70                $trailingSpace = $this->getTrailingSpace( $state, $node, '' );
71            }
72            // Recover any trimmed whitespace only on unmodified nodes
73            if ( !$trailingSpace ) {
74                $lastChild = DiffDOMUtils::lastNonSepChild( $node );
75                if ( $lastChild && !DiffUtils::hasDiffMarkers( $lastChild ) ) {
76                    $trailingSpace = $state->recoverTrimmedWhitespace( $node, false );
77                }
78            }
79            if ( $trailingSpace ) {
80                $state->appendSep( $trailingSpace );
81            }
82        }
83        return $node->nextSibling;
84    }
85
86    /** @inheritDoc */
87    public function before( Element $node, Node $otherNode, SerializerState $state ): array {
88        if ( DOMCompat::nodeName( $otherNode ) === 'th'
89            && ( DOMDataUtils::getDataParsoid( $node )->stx ?? null ) === 'row'
90        ) {
91            // force single line
92            return [ 'min' => 0, 'max' => $this->maxNLsInTable( $node, $otherNode ) ];
93        } else {
94            return [ 'min' => 1, 'max' => $this->maxNLsInTable( $node, $otherNode ) ];
95        }
96    }
97
98    /** @inheritDoc */
99    public function after( Element $node, Node $otherNode, SerializerState $state ): array {
100        if ( DOMCompat::nodeName( $otherNode ) === 'td' ) {
101            // Force a newline break
102            return [ 'min' => 1, 'max' => $this->maxNLsInTable( $node, $otherNode ) ];
103        } else {
104            return [ 'min' => 0, 'max' => $this->maxNLsInTable( $node, $otherNode ) ];
105        }
106    }
107
108}