Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 43 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
THHandler | |
0.00% |
0 / 43 |
|
0.00% |
0 / 4 |
552 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
handle | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
306 | |||
before | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
after | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\Html2Wt\DOMHandlers; |
5 | |
6 | use Wikimedia\Parsoid\DOM\Element; |
7 | use Wikimedia\Parsoid\DOM\Node; |
8 | use Wikimedia\Parsoid\Html2Wt\DiffUtils; |
9 | use Wikimedia\Parsoid\Html2Wt\SerializerState; |
10 | use Wikimedia\Parsoid\Utils\DiffDOMUtils; |
11 | use Wikimedia\Parsoid\Utils\DOMCompat; |
12 | use Wikimedia\Parsoid\Utils\DOMDataUtils; |
13 | |
14 | class 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 = ''; |
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 | $state->appendSep( $trailingSpace ); |
80 | } |
81 | return $node->nextSibling; |
82 | } |
83 | |
84 | /** @inheritDoc */ |
85 | public function before( Element $node, Node $otherNode, SerializerState $state ): array { |
86 | if ( DOMCompat::nodeName( $otherNode ) === 'th' |
87 | && ( DOMDataUtils::getDataParsoid( $node )->stx ?? null ) === 'row' |
88 | ) { |
89 | // force single line |
90 | return [ 'min' => 0, 'max' => $this->maxNLsInTable( $node, $otherNode ) ]; |
91 | } else { |
92 | return [ 'min' => 1, 'max' => $this->maxNLsInTable( $node, $otherNode ) ]; |
93 | } |
94 | } |
95 | |
96 | /** @inheritDoc */ |
97 | public function after( Element $node, Node $otherNode, SerializerState $state ): array { |
98 | if ( DOMCompat::nodeName( $otherNode ) === 'td' ) { |
99 | // Force a newline break |
100 | return [ 'min' => 1, 'max' => $this->maxNLsInTable( $node, $otherNode ) ]; |
101 | } else { |
102 | return [ 'min' => 0, 'max' => $this->maxNLsInTable( $node, $otherNode ) ]; |
103 | } |
104 | } |
105 | |
106 | } |