1*8ed75a23SAndreas Gohr<?php 2*8ed75a23SAndreas Gohr 3*8ed75a23SAndreas Gohrnamespace dokuwiki\test\Parsing\ParserMode; 4*8ed75a23SAndreas Gohr 5*8ed75a23SAndreas Gohruse dokuwiki\Parsing\ModeRegistry; 6*8ed75a23SAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmBacktickDouble; 7*8ed75a23SAndreas Gohr 8*8ed75a23SAndreas Gohr/** 9*8ed75a23SAndreas Gohr * Tests for the GFM double-backtick code-span mode. 10*8ed75a23SAndreas Gohr * 11*8ed75a23SAndreas Gohr * The whole point of this length is being able to embed a lone 12*8ed75a23SAndreas Gohr * backtick in inline code — input ``foo`bar`` renders as 13*8ed75a23SAndreas Gohr * <code>foo`bar</code>. Combined with the edge-space strip rule, 14*8ed75a23SAndreas Gohr * the boundaries can hold backticks too: input `` `foo` `` 15*8ed75a23SAndreas Gohr * renders as <code>`foo`</code>. 16*8ed75a23SAndreas Gohr */ 17*8ed75a23SAndreas Gohrclass GfmBacktickDoubleTest extends ParserTestBase 18*8ed75a23SAndreas Gohr{ 19*8ed75a23SAndreas Gohr public function setUp(): void 20*8ed75a23SAndreas Gohr { 21*8ed75a23SAndreas Gohr parent::setUp(); 22*8ed75a23SAndreas Gohr global $conf; 23*8ed75a23SAndreas Gohr $conf['syntax'] = 'markdown'; 24*8ed75a23SAndreas Gohr ModeRegistry::reset(); 25*8ed75a23SAndreas Gohr } 26*8ed75a23SAndreas Gohr 27*8ed75a23SAndreas Gohr public function tearDown(): void 28*8ed75a23SAndreas Gohr { 29*8ed75a23SAndreas Gohr ModeRegistry::reset(); 30*8ed75a23SAndreas Gohr parent::tearDown(); 31*8ed75a23SAndreas Gohr } 32*8ed75a23SAndreas Gohr 33*8ed75a23SAndreas Gohr function testBasicSpan() 34*8ed75a23SAndreas Gohr { 35*8ed75a23SAndreas Gohr $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble()); 36*8ed75a23SAndreas Gohr $this->P->parse('foo ``bar`` baz'); 37*8ed75a23SAndreas Gohr $calls = [ 38*8ed75a23SAndreas Gohr ['document_start', []], 39*8ed75a23SAndreas Gohr ['p_open', []], 40*8ed75a23SAndreas Gohr ['cdata', ["\nfoo "]], 41*8ed75a23SAndreas Gohr ['monospace_open', []], 42*8ed75a23SAndreas Gohr ['unformatted', ['bar']], 43*8ed75a23SAndreas Gohr ['monospace_close', []], 44*8ed75a23SAndreas Gohr ['cdata', [' baz']], 45*8ed75a23SAndreas Gohr ['p_close', []], 46*8ed75a23SAndreas Gohr ['document_end', []], 47*8ed75a23SAndreas Gohr ]; 48*8ed75a23SAndreas Gohr $this->assertCalls($calls, $this->H->calls); 49*8ed75a23SAndreas Gohr } 50*8ed75a23SAndreas Gohr 51*8ed75a23SAndreas Gohr function testAllowsInteriorSingleBacktick() 52*8ed75a23SAndreas Gohr { 53*8ed75a23SAndreas Gohr // GFM example 349. Input ``foo`bar`` — a lone backtick in the 54*8ed75a23SAndreas Gohr // body cannot be a valid n=2 closer, so it stays as content. 55*8ed75a23SAndreas Gohr $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble()); 56*8ed75a23SAndreas Gohr $this->P->parse('``foo`bar``'); 57*8ed75a23SAndreas Gohr $calls = [ 58*8ed75a23SAndreas Gohr ['document_start', []], 59*8ed75a23SAndreas Gohr ['p_open', []], 60*8ed75a23SAndreas Gohr ['cdata', ["\n"]], 61*8ed75a23SAndreas Gohr ['monospace_open', []], 62*8ed75a23SAndreas Gohr ['unformatted', ['foo`bar']], 63*8ed75a23SAndreas Gohr ['monospace_close', []], 64*8ed75a23SAndreas Gohr ['cdata', ['']], 65*8ed75a23SAndreas Gohr ['p_close', []], 66*8ed75a23SAndreas Gohr ['document_end', []], 67*8ed75a23SAndreas Gohr ]; 68*8ed75a23SAndreas Gohr $this->assertCalls($calls, $this->H->calls); 69*8ed75a23SAndreas Gohr } 70*8ed75a23SAndreas Gohr 71*8ed75a23SAndreas Gohr function testStripsEdgeSpaces() 72*8ed75a23SAndreas Gohr { 73*8ed75a23SAndreas Gohr // GFM example 339. Input `` foo ` bar `` — one leading and one 74*8ed75a23SAndreas Gohr // trailing space stripped; the interior lone backtick stays. 75*8ed75a23SAndreas Gohr $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble()); 76*8ed75a23SAndreas Gohr $this->P->parse('`` foo ` bar ``'); 77*8ed75a23SAndreas Gohr $calls = [ 78*8ed75a23SAndreas Gohr ['document_start', []], 79*8ed75a23SAndreas Gohr ['p_open', []], 80*8ed75a23SAndreas Gohr ['cdata', ["\n"]], 81*8ed75a23SAndreas Gohr ['monospace_open', []], 82*8ed75a23SAndreas Gohr ['unformatted', ['foo ` bar']], 83*8ed75a23SAndreas Gohr ['monospace_close', []], 84*8ed75a23SAndreas Gohr ['cdata', ['']], 85*8ed75a23SAndreas Gohr ['p_close', []], 86*8ed75a23SAndreas Gohr ['document_end', []], 87*8ed75a23SAndreas Gohr ]; 88*8ed75a23SAndreas Gohr $this->assertCalls($calls, $this->H->calls); 89*8ed75a23SAndreas Gohr } 90*8ed75a23SAndreas Gohr 91*8ed75a23SAndreas Gohr function testConvertsNewlinesToSpaces() 92*8ed75a23SAndreas Gohr { 93*8ed75a23SAndreas Gohr // GFM example 345: newlines inside a span become single spaces, 94*8ed75a23SAndreas Gohr // then edge-space stripping applies to the normalized body. 95*8ed75a23SAndreas Gohr $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble()); 96*8ed75a23SAndreas Gohr $this->P->parse("``\nfoo\nbar \nbaz\n``"); 97*8ed75a23SAndreas Gohr $modes = array_column($this->H->calls, 0); 98*8ed75a23SAndreas Gohr $this->assertContains('monospace_open', $modes); 99*8ed75a23SAndreas Gohr 100*8ed75a23SAndreas Gohr $unformatted = array_values(array_filter( 101*8ed75a23SAndreas Gohr $this->H->calls, 102*8ed75a23SAndreas Gohr static fn($c) => $c[0] === 'unformatted' 103*8ed75a23SAndreas Gohr )); 104*8ed75a23SAndreas Gohr $this->assertCount(1, $unformatted); 105*8ed75a23SAndreas Gohr $this->assertSame('foo bar baz', $unformatted[0][1][0]); 106*8ed75a23SAndreas Gohr } 107*8ed75a23SAndreas Gohr 108*8ed75a23SAndreas Gohr function testAllWhitespaceBodyIsPreserved() 109*8ed75a23SAndreas Gohr { 110*8ed75a23SAndreas Gohr $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble()); 111*8ed75a23SAndreas Gohr $this->P->parse('a `` `` b'); 112*8ed75a23SAndreas Gohr $calls = [ 113*8ed75a23SAndreas Gohr ['document_start', []], 114*8ed75a23SAndreas Gohr ['p_open', []], 115*8ed75a23SAndreas Gohr ['cdata', ["\na "]], 116*8ed75a23SAndreas Gohr ['monospace_open', []], 117*8ed75a23SAndreas Gohr ['unformatted', [' ']], 118*8ed75a23SAndreas Gohr ['monospace_close', []], 119*8ed75a23SAndreas Gohr ['cdata', [' b']], 120*8ed75a23SAndreas Gohr ['p_close', []], 121*8ed75a23SAndreas Gohr ['document_end', []], 122*8ed75a23SAndreas Gohr ]; 123*8ed75a23SAndreas Gohr $this->assertCalls($calls, $this->H->calls); 124*8ed75a23SAndreas Gohr } 125*8ed75a23SAndreas Gohr 126*8ed75a23SAndreas Gohr function testEmptyDelimiterDoesNotMatch() 127*8ed75a23SAndreas Gohr { 128*8ed75a23SAndreas Gohr // A run of four backticks — the length-boundary guards reject it 129*8ed75a23SAndreas Gohr // as an n=2 opener followed immediately by an n=2 closer with 130*8ed75a23SAndreas Gohr // empty body. 131*8ed75a23SAndreas Gohr $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble()); 132*8ed75a23SAndreas Gohr $this->P->parse('foo ```` bar'); 133*8ed75a23SAndreas Gohr $modes = array_column($this->H->calls, 0); 134*8ed75a23SAndreas Gohr $this->assertNotContains('monospace_open', $modes, 135*8ed75a23SAndreas Gohr 'Run of 4 backticks must stay literal'); 136*8ed75a23SAndreas Gohr } 137*8ed75a23SAndreas Gohr 138*8ed75a23SAndreas Gohr function testSortValue() 139*8ed75a23SAndreas Gohr { 140*8ed75a23SAndreas Gohr // Shares the n=1 sort — the length-boundary guards on both modes 141*8ed75a23SAndreas Gohr // mean they never compete for the same input anyway. 142*8ed75a23SAndreas Gohr $mode = new GfmBacktickDouble(); 143*8ed75a23SAndreas Gohr $this->assertSame(165, $mode->getSort()); 144*8ed75a23SAndreas Gohr } 145*8ed75a23SAndreas Gohr} 146