xref: /dokuwiki/inc/Parsing/ParserMode/GfmLinebreak.php (revision c4bcbc2e0c397783de26c1e3c211d82d1ac21bb4)
1*c4bcbc2eSAndreas Gohr<?php
2*c4bcbc2eSAndreas Gohr
3*c4bcbc2eSAndreas Gohrnamespace dokuwiki\Parsing\ParserMode;
4*c4bcbc2eSAndreas Gohr
5*c4bcbc2eSAndreas Gohruse dokuwiki\Parsing\Handler;
6*c4bcbc2eSAndreas Gohr
7*c4bcbc2eSAndreas Gohr/**
8*c4bcbc2eSAndreas Gohr * GFM hard line break: two-or-more trailing spaces, or a single
9*c4bcbc2eSAndreas Gohr * backslash, immediately before a non-final newline.
10*c4bcbc2eSAndreas Gohr *
11*c4bcbc2eSAndreas Gohr * Both delimiter forms land in one mode because they share semantics
12*c4bcbc2eSAndreas Gohr * (emit linebreak), share the block-boundary rule (no break at the
13*c4bcbc2eSAndreas Gohr * end of a paragraph or other block), and share the next-line
14*c4bcbc2eSAndreas Gohr * leading-whitespace consumption (GFM strips it). Keeping all hard-
15*c4bcbc2eSAndreas Gohr * break logic in one pattern is cheaper than two and matches GFM
16*c4bcbc2eSAndreas Gohr * spec section 6.7 directly.
17*c4bcbc2eSAndreas Gohr *
18*c4bcbc2eSAndreas Gohr * Bypass inside code spans and fenced blocks falls out for free:
19*c4bcbc2eSAndreas Gohr * those are whole-span PROTECTED / FORMATTING modes that capture
20*c4bcbc2eSAndreas Gohr * their body in one regex match, so SUBSTITION patterns never see
21*c4bcbc2eSAndreas Gohr * the inner text — same mechanism that exempts GfmEscape from
22*c4bcbc2eSAndreas Gohr * code spans.
23*c4bcbc2eSAndreas Gohr *
24*c4bcbc2eSAndreas Gohr * No collision with the existing DokuWiki Linebreak mode (also at
25*c4bcbc2eSAndreas Gohr * sort 140): DW's pattern is a literal double backslash `\\`,
26*c4bcbc2eSAndreas Gohr * unrelated to either GFM delimiter form. In mixed syntax settings
27*c4bcbc2eSAndreas Gohr * both modes can load and the leftmost match wins position-by-
28*c4bcbc2eSAndreas Gohr * position. GfmEscape (sort 5) does not steal the backslash form
29*c4bcbc2eSAndreas Gohr * either: its pattern requires the next char to be ASCII
30*c4bcbc2eSAndreas Gohr * punctuation, and `\n` is not punctuation.
31*c4bcbc2eSAndreas Gohr */
32*c4bcbc2eSAndreas Gohrclass GfmLinebreak extends AbstractMode
33*c4bcbc2eSAndreas Gohr{
34*c4bcbc2eSAndreas Gohr    /** @inheritdoc */
35*c4bcbc2eSAndreas Gohr    public function getSort()
36*c4bcbc2eSAndreas Gohr    {
37*c4bcbc2eSAndreas Gohr        return 140;
38*c4bcbc2eSAndreas Gohr    }
39*c4bcbc2eSAndreas Gohr
40*c4bcbc2eSAndreas Gohr    /** @inheritdoc */
41*c4bcbc2eSAndreas Gohr    public function connectTo($mode)
42*c4bcbc2eSAndreas Gohr    {
43*c4bcbc2eSAndreas Gohr        // (?:[ ]{2,}|\\)            two+ spaces OR one backslash
44*c4bcbc2eSAndreas Gohr        // \n                        the line ending
45*c4bcbc2eSAndreas Gohr        // (?![ \t]*(?:\n|\z))       not at a paragraph break or EOF
46*c4bcbc2eSAndreas Gohr        // [ \t]*                    swallow leading WS of the next line
47*c4bcbc2eSAndreas Gohr        $this->Lexer->addSpecialPattern(
48*c4bcbc2eSAndreas Gohr            '(?:[ ]{2,}|\\\\)\n(?![ \t]*(?:\n|\z))[ \t]*',
49*c4bcbc2eSAndreas Gohr            $mode,
50*c4bcbc2eSAndreas Gohr            'gfm_linebreak'
51*c4bcbc2eSAndreas Gohr        );
52*c4bcbc2eSAndreas Gohr    }
53*c4bcbc2eSAndreas Gohr
54*c4bcbc2eSAndreas Gohr    /** @inheritdoc */
55*c4bcbc2eSAndreas Gohr    public function handle($match, $state, $pos, Handler $handler)
56*c4bcbc2eSAndreas Gohr    {
57*c4bcbc2eSAndreas Gohr        $handler->addCall('linebreak', [], $pos);
58*c4bcbc2eSAndreas Gohr        return true;
59*c4bcbc2eSAndreas Gohr    }
60*c4bcbc2eSAndreas Gohr}
61