xref: /dokuwiki/inc/Parsing/ParserMode/GfmEscape.php (revision e7dae73bcd947f44c901faaac9dd45de67633a3b)
1<?php
2
3namespace dokuwiki\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\Helpers\Escape;
7
8/**
9 * GFM backslash escapes: a backslash before any ASCII punctuation
10 * character produces the literal punctuation character; the backslash
11 * itself is consumed and the following char loses any markup meaning.
12 *
13 * Backslashes before any other character (letters, digits, multibyte,
14 * spaces, tabs, newlines) are NOT escapes — those sequences stay
15 * literal because the pattern doesn't match them and the lexer leaves
16 * them as cdata.
17 *
18 * Sort 5 places this mode ahead of every other inline mode so that
19 * leftmost-then-priority resolution claims `\X` before any competing
20 * delimiter (emphasis `*`, heading `#`, link `[`, …) can match the
21 * unescaped char.
22 *
23 * Category SUBSTITUTION (alongside Smiley and Entity) so the mode is
24 * reachable everywhere those run: inside paragraphs, formatting
25 * modes (emphasis, strong, deleted), list items, table cells, headers
26 * — every container whose allowedModes include SUBSTITUTION. Whole-span
27 * code modes (GfmCode, GfmFile, GfmBacktickSingle, GfmBacktickDouble)
28 * capture their entire body in one regex shot and therefore bypass
29 * GfmEscape on their content — matching GFM's rule that escapes don't
30 * fire inside code blocks or code spans.
31 *
32 * Modes that capture a literal string and need GFM unescape applied
33 * post-hoc (link URL/label, fence info string) call
34 * {@see \dokuwiki\Parsing\Helpers\Escape::unescapeBackslashes()} from
35 * their handle() — same character class.
36 */
37class GfmEscape extends AbstractMode
38{
39    public function __construct()
40    {
41        $this->allowedModes = [];
42    }
43
44    /** @inheritdoc */
45    public function getSort()
46    {
47        return 5;
48    }
49
50    /** @inheritdoc */
51    public function connectTo($mode)
52    {
53        $this->Lexer->addSpecialPattern(
54            '\\\\' . Escape::PUNCTUATION_CHAR_CLASS,
55            $mode,
56            'gfm_escape'
57        );
58    }
59
60    /** @inheritdoc */
61    public function handle($match, $state, $pos, Handler $handler)
62    {
63        $handler->addCall('cdata', [substr($match, 1)], $pos);
64        return true;
65    }
66}
67