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