1<?php 2 3namespace dokuwiki\Parsing\ParserMode; 4 5use dokuwiki\Parsing\Handler; 6 7/** 8 * GFM / CommonMark em-wrapping-strong via triple asterisks: `***text***`. 9 * 10 * Renders as <em><strong>text</strong></em>. Only the exact 3+3 symmetric 11 * variant is supported. Longer symmetric runs (`****foo****`, 12 * `******foo******`) or asymmetric runs (`***foo**`) require CommonMark's 13 * full delimiter-pairing algorithm and are out of scope. 14 * 15 * Sort 65 is below Strong (70) so this mode wins the lexer race for 16 * `***...***` patterns. 17 */ 18class GfmEmphasisStrong extends AbstractFormatting 19{ 20 /** @inheritdoc */ 21 public function getSort() 22 { 23 return 65; 24 } 25 26 /** @inheritdoc */ 27 protected function getModeName(): string 28 { 29 return 'gfm_emphasis_strong'; 30 } 31 32 /** @inheritdoc */ 33 protected function getEntryPattern(): string 34 { 35 // Broken down: 36 // (?<!\*) — opener not preceded by `*` (so we 37 // don't match inside `****...` runs) 38 // \*\*\* — exactly three opening `*` 39 // (?=[^\s*]) — next body char: not whitespace, not `*` 40 // (flanking-opener rule) 41 // (?= — lookahead: a valid closer must exist 42 // CONTENT_UNTIL_PARA — body that doesn't cross a paragraph 43 // [^\s] — last body char: not whitespace 44 // (flanking-closer rule) 45 // \*\*\* — closing `***` 46 // (?!\*) — and not followed by another `*` 47 // (exactly 3, not 4+) 48 // ) 49 return '(?<!\*)\*\*\*(?=[^\s*])' 50 . '(?=' . self::CONTENT_UNTIL_PARA . '[^\s]\*\*\*(?!\*))'; 51 } 52 53 /** @inheritdoc */ 54 protected function getExitPattern(): string 55 { 56 return '(?<=[^\s])\*\*\*(?!\*)'; 57 } 58 59 /** 60 * Emit em wrapping strong (and their closers in reverse order). 61 * Overridden because AbstractFormatting's default emits a single 62 * open/close pair — we need two each. 63 * 64 * @inheritdoc 65 */ 66 public function handle($match, $state, $pos, Handler $handler) 67 { 68 switch ($state) { 69 case DOKU_LEXER_ENTER: 70 $handler->addCall('emphasis_open', [], $pos); 71 $handler->addCall('strong_open', [], $pos); 72 break; 73 case DOKU_LEXER_EXIT: 74 $handler->addCall('strong_close', [], $pos); 75 $handler->addCall('emphasis_close', [], $pos); 76 break; 77 case DOKU_LEXER_UNMATCHED: 78 $handler->addCall('cdata', [$match], $pos); 79 break; 80 } 81 return true; 82 } 83} 84