xref: /dokuwiki/inc/Parsing/ParserMode/Preformatted.php (revision f57da51cde0cd30236737579a7b446c40f66189e)
1be906b56SAndreas Gohr<?php
2be906b56SAndreas Gohr
3be906b56SAndreas Gohrnamespace dokuwiki\Parsing\ParserMode;
4be906b56SAndreas Gohr
571096e46SAndreas Gohruse dokuwiki\Parsing\Handler;
671096e46SAndreas Gohruse dokuwiki\Parsing\Handler\Preformatted as PreformattedHandler;
77958e698SAndreas Gohr
8be906b56SAndreas Gohrclass Preformatted extends AbstractMode
9be906b56SAndreas Gohr{
10be906b56SAndreas Gohr    /** @inheritdoc */
1171096e46SAndreas Gohr    public function getSort()
1271096e46SAndreas Gohr    {
1371096e46SAndreas Gohr        return 20;
1471096e46SAndreas Gohr    }
1571096e46SAndreas Gohr
16b1c59bedSAndreas Gohr    /**
17b1c59bedSAndreas Gohr     * Number of leading spaces that trigger a preformatted block.
18b1c59bedSAndreas Gohr     *
19b1c59bedSAndreas Gohr     * DokuWiki's historical value is 2 spaces; Markdown uses 4. When
2013a62f81SAndreas Gohr     * `$conf['syntax']` is `md` or `md+dw` (Markdown preferred),
21b1c59bedSAndreas Gohr     * we flip to 4 so indented code blocks match GFM. A single tab is
22b1c59bedSAndreas Gohr     * always a trigger regardless of the space threshold.
23b1c59bedSAndreas Gohr     */
24b1c59bedSAndreas Gohr    protected function getIndentWidth(): int
25b1c59bedSAndreas Gohr    {
26b1c59bedSAndreas Gohr        global $conf;
2713a62f81SAndreas Gohr        return in_array($conf['syntax'], ['md', 'md+dw'], true) ? 4 : 2;
28b1c59bedSAndreas Gohr    }
29b1c59bedSAndreas Gohr
3071096e46SAndreas Gohr    /** @inheritdoc */
31be906b56SAndreas Gohr    public function connectTo($mode)
32be906b56SAndreas Gohr    {
33b1c59bedSAndreas Gohr        $indent = str_repeat(' ', $this->getIndentWidth());
34b1c59bedSAndreas Gohr
3596d096f1SAndreas Gohr        $this->Lexer->addEntryPattern('\n' . $indent, $mode, 'preformatted');
3696d096f1SAndreas Gohr        $this->Lexer->addEntryPattern('\n\t', $mode, 'preformatted');
37be906b56SAndreas Gohr
38259e91d9SAndreas Gohr        // match continuation lines inside the preformatted block
39b1c59bedSAndreas Gohr        $this->Lexer->addPattern('\n' . $indent, 'preformatted');
40be906b56SAndreas Gohr        $this->Lexer->addPattern('\n\t', 'preformatted');
41be906b56SAndreas Gohr    }
42be906b56SAndreas Gohr
43be906b56SAndreas Gohr    /** @inheritdoc */
44be906b56SAndreas Gohr    public function postConnect()
45be906b56SAndreas Gohr    {
46*f57da51cSAndreas Gohr        // Two exits: a zero-width lookahead when the next line starts with
47*f57da51cSAndreas Gohr        // non-whitespace content (so the boundary \n stays in the stream
48*f57da51cSAndreas Gohr        // and downstream block-level modes like GfmHr or GfmHeader can
49*f57da51cSAndreas Gohr        // anchor on it), and a consuming \n fall-through for blank lines
50*f57da51cSAndreas Gohr        // and end-of-input. The lookahead-only branch is registered first
51*f57da51cSAndreas Gohr        // so PCRE's leftmost-first alternation prefers it whenever it
52*f57da51cSAndreas Gohr        // applies; the consuming branch handles the cases where it cannot.
53*f57da51cSAndreas Gohr        $this->Lexer->addExitPattern('(?=\n[^ \t\n])', 'preformatted');
54be906b56SAndreas Gohr        $this->Lexer->addExitPattern('\n', 'preformatted');
55be906b56SAndreas Gohr    }
56be906b56SAndreas Gohr
57be906b56SAndreas Gohr    /** @inheritdoc */
5871096e46SAndreas Gohr    public function handle($match, $state, $pos, Handler $handler)
59be906b56SAndreas Gohr    {
6071096e46SAndreas Gohr        switch ($state) {
6171096e46SAndreas Gohr            case DOKU_LEXER_ENTER:
6271096e46SAndreas Gohr                $handler->setCallWriter(new PreformattedHandler($handler->getCallWriter()));
6371096e46SAndreas Gohr                $handler->addCall('preformatted_start', [], $pos);
6471096e46SAndreas Gohr                break;
6571096e46SAndreas Gohr            case DOKU_LEXER_EXIT:
6671096e46SAndreas Gohr                $handler->addCall('preformatted_end', [], $pos);
6771096e46SAndreas Gohr                /** @var PreformattedHandler $reWriter */
6871096e46SAndreas Gohr                $reWriter = $handler->getCallWriter();
6971096e46SAndreas Gohr                $handler->setCallWriter($reWriter->process());
7071096e46SAndreas Gohr                break;
7171096e46SAndreas Gohr            case DOKU_LEXER_MATCHED:
7271096e46SAndreas Gohr                $handler->addCall('preformatted_newline', [], $pos);
7371096e46SAndreas Gohr                break;
7471096e46SAndreas Gohr            case DOKU_LEXER_UNMATCHED:
7571096e46SAndreas Gohr                $handler->addCall('preformatted_content', [$match], $pos);
7671096e46SAndreas Gohr                break;
7771096e46SAndreas Gohr        }
7871096e46SAndreas Gohr
7971096e46SAndreas Gohr        return true;
80be906b56SAndreas Gohr    }
81be906b56SAndreas Gohr}
82