xref: /dokuwiki/inc/Parsing/ParserMode/Preformatted.php (revision b1c59bed2e3645a1f5f11438cdbe7d1596f4a3a4)
1<?php
2
3namespace dokuwiki\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\Handler\Preformatted as PreformattedHandler;
7use dokuwiki\Parsing\ModeRegistry;
8
9class Preformatted extends AbstractMode
10{
11    /** @inheritdoc */
12    public function getSort()
13    {
14        return 20;
15    }
16
17    /**
18     * Number of leading spaces that trigger a preformatted block.
19     *
20     * DokuWiki's historical value is 2 spaces; Markdown uses 4. When
21     * `$conf['syntax']` is `markdown` or `md+dw` (Markdown preferred),
22     * we flip to 4 so indented code blocks match GFM. A single tab is
23     * always a trigger regardless of the space threshold.
24     */
25    protected function getIndentWidth(): int
26    {
27        global $conf;
28        $syntax = $conf['syntax'] ?? 'dokuwiki';
29        return in_array($syntax, ['markdown', 'md+dw'], true) ? 4 : 2;
30    }
31
32    /** @inheritdoc */
33    public function connectTo($mode)
34    {
35        $markers = ModeRegistry::getInstance()->getLineStartMarkers();
36        $lookahead = $markers ? '(?![' . implode('', $markers) . '])' : '';
37
38        $indent = str_repeat(' ', $this->getIndentWidth());
39
40        $this->Lexer->addEntryPattern('\n' . $indent . $lookahead, $mode, 'preformatted');
41        $this->Lexer->addEntryPattern('\n\t' . $lookahead, $mode, 'preformatted');
42
43        // match continuation lines inside the preformatted block
44        $this->Lexer->addPattern('\n' . $indent, 'preformatted');
45        $this->Lexer->addPattern('\n\t', 'preformatted');
46    }
47
48    /** @inheritdoc */
49    public function postConnect()
50    {
51        $this->Lexer->addExitPattern('\n', 'preformatted');
52    }
53
54    /** @inheritdoc */
55    public function handle($match, $state, $pos, Handler $handler)
56    {
57        switch ($state) {
58            case DOKU_LEXER_ENTER:
59                $handler->setCallWriter(new PreformattedHandler($handler->getCallWriter()));
60                $handler->addCall('preformatted_start', [], $pos);
61                break;
62            case DOKU_LEXER_EXIT:
63                $handler->addCall('preformatted_end', [], $pos);
64                /** @var PreformattedHandler $reWriter */
65                $reWriter = $handler->getCallWriter();
66                $handler->setCallWriter($reWriter->process());
67                break;
68            case DOKU_LEXER_MATCHED:
69                $handler->addCall('preformatted_newline', [], $pos);
70                break;
71            case DOKU_LEXER_UNMATCHED:
72                $handler->addCall('preformatted_content', [$match], $pos);
73                break;
74        }
75
76        return true;
77    }
78}
79