xref: /dokuwiki/inc/Parsing/ParserMode/Footnote.php (revision 884caed926ca0aa0af6ce3f34ae3aa7317a3361a)
1<?php
2
3namespace dokuwiki\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\Handler\Nest;
7use dokuwiki\Parsing\ModeRegistry;
8
9class Footnote extends AbstractMode
10{
11    /** @inheritdoc */
12    protected function allowedCategories(): array
13    {
14        return [
15            ModeRegistry::CATEGORY_CONTAINER,
16            ModeRegistry::CATEGORY_FORMATTING,
17            ModeRegistry::CATEGORY_SUBSTITUTION,
18            ModeRegistry::CATEGORY_PROTECTED,
19            ModeRegistry::CATEGORY_DISABLED,
20        ];
21    }
22
23    /**
24     * Footnotes cannot nest, so footnote is excluded from its own content.
25     *
26     * @inheritdoc
27     */
28    protected function filterAllowedModes(array $modes): array
29    {
30        return array_values(array_filter($modes, static fn($m) => $m !== 'footnote'));
31    }
32
33    /** @inheritdoc */
34    public function getSort()
35    {
36        return 150;
37    }
38
39    /** @inheritdoc */
40    public function connectTo($mode)
41    {
42        $this->Lexer->addEntryPattern(
43            '\x28\x28(?=.*\x29\x29)',
44            $mode,
45            'footnote'
46        );
47    }
48
49    /** @inheritdoc */
50    public function postConnect()
51    {
52        $this->Lexer->addExitPattern(
53            '\x29\x29',
54            'footnote'
55        );
56    }
57
58    /** @inheritdoc */
59    public function handle($match, $state, $pos, Handler $handler)
60    {
61        switch ($state) {
62            case DOKU_LEXER_ENTER:
63                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
64                // we will still enter a new footnote mode, we just do nothing
65                if ($handler->getStatus('footnote')) {
66                    $handler->addCall('cdata', [$match], $pos);
67                    break;
68                }
69                $handler->setStatus('footnote', true);
70
71                $handler->setCallWriter(new Nest($handler->getCallWriter(), 'footnote_close'));
72                $handler->addCall('footnote_open', [], $pos);
73                break;
74            case DOKU_LEXER_EXIT:
75                // check whether we have already exited the footnote mode, can happen if the modes were nested
76                if (!$handler->getStatus('footnote')) {
77                    $handler->addCall('cdata', [$match], $pos);
78                    break;
79                }
80
81                $handler->setStatus('footnote', false);
82                $handler->addCall('footnote_close', [], $pos);
83
84                /** @var Nest $reWriter */
85                $reWriter = $handler->getCallWriter();
86                $handler->setCallWriter($reWriter->process());
87                break;
88            case DOKU_LEXER_UNMATCHED:
89                $handler->addCall('cdata', [$match], $pos);
90                break;
91        }
92        return true;
93    }
94}
95