xref: /dokuwiki/_test/tests/Parsing/ParserMode/PreformattedTest.php (revision 75364f13219a5af44f52c564ea0a62df64c3a17f)
1<?php
2
3namespace dokuwiki\test\Parsing\ParserMode;
4
5use dokuwiki\Parsing\ParserMode\Code;
6use dokuwiki\Parsing\ParserMode\Eol;
7use dokuwiki\Parsing\ParserMode\File;
8use dokuwiki\Parsing\ParserMode\Header;
9use dokuwiki\Parsing\ParserMode\Listblock;
10use dokuwiki\Parsing\ParserMode\Preformatted;
11
12class PreformattedTest extends ParserTestBase
13{
14
15    function testFile() {
16        $this->P->addMode('file',new File());
17        $this->P->parse('Foo <file>testing</file> Bar');
18        $calls = [
19            ['document_start',[]],
20            ['p_open',[]],
21            ['cdata',["\n".'Foo ']],
22            ['p_close',[]],
23            ['file',['testing',null,null]],
24            ['p_open',[]],
25            ['cdata',['Bar']],
26            ['p_close',[]],
27            ['document_end',[]],
28        ];
29
30        $this->assertCalls($calls, $this->H->calls);
31    }
32
33    function testCode() {
34        $this->P->addMode('code',new Code());
35        $this->P->parse('Foo <code>testing</code> Bar');
36        $calls = [
37            ['document_start',[]],
38            ['p_open',[]],
39            ['cdata',["\n".'Foo ']],
40            ['p_close',[]],
41            ['code',['testing', null, null]],
42            ['p_open',[]],
43            ['cdata',['Bar']],
44            ['p_close',[]],
45            ['document_end',[]],
46        ];
47        $this->assertCalls($calls, $this->H->calls);
48    }
49
50    function testCodeWhitespace() {
51        $this->P->addMode('code',new Code());
52        $this->P->parse("Foo <code \n>testing</code> Bar");
53        $calls = [
54            ['document_start',[]],
55            ['p_open',[]],
56            ['cdata',["\n".'Foo ']],
57            ['p_close',[]],
58            ['code',['testing', null, null]],
59            ['p_open',[]],
60            ['cdata',['Bar']],
61            ['p_close',[]],
62            ['document_end',[]],
63        ];
64        $this->assertCalls($calls, $this->H->calls);
65    }
66
67    function testCodeLang() {
68        $this->P->addMode('code',new Code());
69        $this->P->parse("Foo <code php>testing</code> Bar");
70        $calls = [
71            ['document_start',[]],
72            ['p_open',[]],
73            ['cdata',["\n".'Foo ']],
74            ['p_close',[]],
75            ['code',['testing', 'php', null]],
76            ['p_open',[]],
77            ['cdata',['Bar']],
78            ['p_close',[]],
79            ['document_end',[]],
80        ];
81        $this->assertCalls($calls, $this->H->calls);
82    }
83
84    function testPreformatted() {
85        $this->P->addMode('preformatted',new Preformatted());
86        $this->P->parse("F  oo\n  x  \n    y  \nBar\n");
87        $calls = [
88            ['document_start',[]],
89            ['p_open',[]],
90            ['cdata',["\nF  oo"]],
91            ['p_close',[]],
92            ['preformatted',["x  \n  y  "]],
93            ['p_open',[]],
94            ['cdata',["\nBar"]],
95            ['p_close',[]],
96            ['document_end',[]],
97        ];
98        $this->assertCalls($calls, $this->H->calls);
99    }
100
101    function testPreformattedWinEOL() {
102        $this->P->addMode('preformatted',new Preformatted());
103        $this->P->parse("F  oo\r\n  x  \r\n    y  \r\nBar\r\n");
104        $calls = [
105            ['document_start',[]],
106            ['p_open',[]],
107            ['cdata',["\nF  oo"]],
108            ['p_close',[]],
109            ['preformatted',["x  \n  y  "]],
110            ['p_open',[]],
111            ['cdata',["\nBar"]],
112            ['p_close',[]],
113            ['document_end',[]],
114        ];
115        $this->assertCalls($calls, $this->H->calls);
116    }
117
118    function testPreformattedTab() {
119        $this->P->addMode('preformatted',new Preformatted());
120        $this->P->parse("F  oo\n\tx\t\n\t\ty\t\nBar\n");
121        $calls = [
122            ['document_start',[]],
123            ['p_open',[]],
124            ['cdata',["\nF  oo"]],
125            ['p_close',[]],
126            ['preformatted',["x\t\n\ty\t"]],
127            ['p_open',[]],
128            ['cdata',["\nBar"]],
129            ['p_close',[]],
130            ['document_end',[]],
131        ];
132        $this->assertCalls($calls, $this->H->calls);
133    }
134
135    function testPreformattedTabWinEOL() {
136        $this->P->addMode('preformatted',new Preformatted());
137        $this->P->parse("F  oo\r\n\tx\t\r\n\t\ty\t\r\nBar\r\n");
138        $calls = [
139            ['document_start',[]],
140            ['p_open',[]],
141            ['cdata',["\nF  oo"]],
142            ['p_close',[]],
143            ['preformatted',["x\t\n\ty\t"]],
144            ['p_open',[]],
145            ['cdata',["\nBar"]],
146            ['p_close',[]],
147            ['document_end',[]],
148        ];
149        $this->assertCalls($calls, $this->H->calls);
150    }
151
152    function testPreformattedList() {
153        // Listblock (sort 10) must be added before Preformatted (sort 20) so
154        // the resulting PCRE alternation matches the canonical mode order.
155        // PCRE picks the first alternative that matches at a given position,
156        // and an indented bullet line like "  - x" matches both modes at the
157        // same offset; Listblock has to come first to win the tie.
158        $this->P->addMode('listblock',new Listblock());
159        $this->P->addMode('preformatted',new Preformatted());
160        $this->P->parse("  - x \n  * y \nF  oo\n  x  \n    y  \n  -X\n  *Y\nBar\n");
161        $calls = [
162            ['document_start',[]],
163            ['listo_open',[]],
164            ['listitem_open',[1]],
165            ['listcontent_open',[]],
166            ['cdata',[" x "]],
167            ['listcontent_close',[]],
168            ['listitem_close',[]],
169            ['listo_close',[]],
170            ['listu_open',[]],
171            ['listitem_open',[1]],
172            ['listcontent_open',[]],
173            ['cdata',[" y "]],
174            ['listcontent_close',[]],
175            ['listitem_close',[]],
176            ['listu_close',[]],
177            ['p_open',[]],
178            ['cdata',["F  oo"]],
179            ['p_close',[]],
180            ['preformatted',["x  \n  y  \n-X\n*Y"]],
181            ['p_open',[]],
182            ['cdata',["\nBar"]],
183            ['p_close',[]],
184            ['document_end',[]],
185        ];
186        $this->assertCalls($calls, $this->H->calls);
187    }
188
189
190    function testMarkdownPreferredUsesFourSpaces() {
191        // In `md` and `md+dw` settings the indent threshold is 4,
192        // matching GFM's indented code block rule. Lines with only 2-3
193        // leading spaces stay as paragraph text.
194        $this->setSyntax('md');
195        $this->P->addMode('preformatted', new Preformatted());
196        $this->P->parse("F  oo\n    x  \n      y  \nBar\n");
197        $calls = [
198            ['document_start', []],
199            ['p_open', []],
200            ['cdata', ["\nF  oo"]],
201            ['p_close', []],
202            ['preformatted', ["x  \n  y  "]],
203            ['p_open', []],
204            ['cdata', ["\nBar"]],
205            ['p_close', []],
206            ['document_end', []],
207        ];
208        $this->assertCalls($calls, $this->H->calls);
209    }
210
211    function testMarkdownPreferredRejectsTwoSpaces() {
212        // 2-space indent in MD-preferred mode does NOT trigger preformatted.
213        $this->setSyntax('md');
214        $this->P->addMode('preformatted', new Preformatted());
215        $this->P->parse("F  oo\n  x\nBar\n");
216        $modes = array_column($this->H->calls, 0);
217        $this->assertNotContains('preformatted', $modes,
218            '2-space indent must not trigger preformatted when Markdown is preferred');
219    }
220
221    function testMarkdownPreferredTabStillTriggers() {
222        // Tab is a trigger regardless of the space threshold.
223        $this->setSyntax('md');
224        $this->P->addMode('preformatted', new Preformatted());
225        $this->P->parse("F  oo\n\tx\nBar\n");
226        $modes = array_column($this->H->calls, 0);
227        $this->assertContains('preformatted', $modes,
228            'A single tab must still trigger preformatted in MD-preferred mode');
229    }
230
231    function testStripsLeadingAndTrailingBlankIndentedLines() {
232        // GFM example #87: leading and trailing blank-but-indented lines
233        // should not appear in the preformatted body. The lexer's
234        // continuation pattern eats their indents, leaving padding `\n`
235        // runs in the rewriter buffer; the rewriter trims them so the
236        // emitted text starts and ends on a non-blank line.
237        $this->setSyntax('md');
238        $this->P->addMode('preformatted', new Preformatted());
239        $this->P->parse("\n    \n    foo\n    \n\n");
240        $calls = [
241            ['document_start', []],
242            ['preformatted', ['foo']],
243            ['document_end', []],
244        ];
245        $this->assertCalls($calls, $this->H->calls);
246    }
247
248    function testWhitespaceOnlyBlockIsSkipped() {
249        // A run of only blank-but-indented lines must not emit a
250        // preformatted call at all - the body would be pure whitespace
251        // and visually meaningless.
252        $this->setSyntax('md');
253        $this->P->addMode('preformatted', new Preformatted());
254        $this->P->parse("\n    \n    \n\n");
255        $modes = array_column($this->H->calls, 0);
256        $this->assertNotContains('preformatted', $modes);
257    }
258
259    function testPreformattedPlusHeaderAndEol() {
260        // Note that EOL must come after preformatted!
261        $this->P->addMode('preformatted',new Preformatted());
262        $this->P->addMode('header',new Header());
263        $this->P->addMode('eol',new Eol());
264        $this->P->parse("F  oo\n  ==Test==\n    y  \nBar\n");
265        $calls = [
266            ['document_start',[]],
267            ['p_open',[]],
268            ['cdata',["F  oo"]],
269            ['p_close',[]],
270            ['preformatted',["==Test==\n  y  "]],
271            ['p_open',[]],
272            ['cdata',['Bar']],
273            ['p_close',[]],
274            ['document_end',[]],
275        ];
276        $this->assertCalls($calls, $this->H->calls);
277    }
278}
279