1*e89aeebdSAndreas Gohr<?php 2*e89aeebdSAndreas Gohr 3*e89aeebdSAndreas Gohrnamespace dokuwiki\Parsing\ParserMode; 4*e89aeebdSAndreas Gohr 5*e89aeebdSAndreas Gohruse dokuwiki\Parsing\Handler; 6*e89aeebdSAndreas Gohr 7*e89aeebdSAndreas Gohr/** 8*e89aeebdSAndreas Gohr * GFM inline link [text](url) with optional title [text](url "title"). 9*e89aeebdSAndreas Gohr * 10*e89aeebdSAndreas Gohr * Deliberately not supported (see skip.php for the affected spec examples): 11*e89aeebdSAndreas Gohr * 12*e89aeebdSAndreas Gohr * - Reference links [text][id] / [text][] / [foo] — the single-pass 13*e89aeebdSAndreas Gohr * lexer cannot resolve forward references to [foo]: url definitions. 14*e89aeebdSAndreas Gohr * - Pointy-bracket destinations [link](<foo bar>) — rarely used, 15*e89aeebdSAndreas Gohr * regex cost outweighs the benefit. 16*e89aeebdSAndreas Gohr * - Balanced-parens inside URLs [link](foo(bar)) — uncommon, complex. 17*e89aeebdSAndreas Gohr * - Image-in-link [](url) — requires GfmMedia plus nested 18*e89aeebdSAndreas Gohr * recursion across modes. 19*e89aeebdSAndreas Gohr * - Title HTML attribute — DokuWiki link handler instructions have no 20*e89aeebdSAndreas Gohr * title-attribute slot, and plumbing one through every renderer just 21*e89aeebdSAndreas Gohr * for this is out of scope. The title parses cleanly but is discarded. 22*e89aeebdSAndreas Gohr */ 23*e89aeebdSAndreas Gohrclass GfmLink extends AbstractMode 24*e89aeebdSAndreas Gohr{ 25*e89aeebdSAndreas Gohr use LinkDispatch; 26*e89aeebdSAndreas Gohr 27*e89aeebdSAndreas Gohr /** @inheritdoc */ 28*e89aeebdSAndreas Gohr public function getSort() 29*e89aeebdSAndreas Gohr { 30*e89aeebdSAndreas Gohr return 300; 31*e89aeebdSAndreas Gohr } 32*e89aeebdSAndreas Gohr 33*e89aeebdSAndreas Gohr /** @inheritdoc */ 34*e89aeebdSAndreas Gohr public function connectTo($mode) 35*e89aeebdSAndreas Gohr { 36*e89aeebdSAndreas Gohr // Pattern breakdown: 37*e89aeebdSAndreas Gohr // \[(?!\[) — single `[`, not part of DW `[[` 38*e89aeebdSAndreas Gohr // [^\[\]\n]+ — text: no nested brackets, single line 39*e89aeebdSAndreas Gohr // \]\( — `]` immediately followed by `(` (GFM 40*e89aeebdSAndreas Gohr // forbids whitespace between them) 41*e89aeebdSAndreas Gohr // \s* — optional whitespace around the URL 42*e89aeebdSAndreas Gohr // [^\s()\n]+ — URL: no whitespace, no parens, single line 43*e89aeebdSAndreas Gohr // (?:\s+(?:"[^"\n]*" 44*e89aeebdSAndreas Gohr // |'[^'\n]*'))? — optional title in "..." or '...' 45*e89aeebdSAndreas Gohr // \s*\) — optional trailing whitespace, close paren 46*e89aeebdSAndreas Gohr $pattern = '\[(?!\[)[^\[\]\n]+\]\(' 47*e89aeebdSAndreas Gohr . '\s*[^\s()\n]+' 48*e89aeebdSAndreas Gohr . '(?:\s+(?:"[^"\n]*"|\'[^\'\n]*\'))?' 49*e89aeebdSAndreas Gohr . '\s*\)'; 50*e89aeebdSAndreas Gohr $this->Lexer->addSpecialPattern($pattern, $mode, 'gfm_link'); 51*e89aeebdSAndreas Gohr } 52*e89aeebdSAndreas Gohr 53*e89aeebdSAndreas Gohr /** @inheritdoc */ 54*e89aeebdSAndreas Gohr public function handle($match, $state, $pos, Handler $handler) 55*e89aeebdSAndreas Gohr { 56*e89aeebdSAndreas Gohr // The entry pattern has already validated the `[text](url)` 57*e89aeebdSAndreas Gohr // shape, so we can destructure with plain string ops. Split on 58*e89aeebdSAndreas Gohr // `](` to separate text from "url and optional title"; the URL 59*e89aeebdSAndreas Gohr // is the first whitespace-delimited token of the remainder, and 60*e89aeebdSAndreas Gohr // anything after it is the title — discarded, since DokuWiki 61*e89aeebdSAndreas Gohr // link instructions have no title-attribute slot. 62*e89aeebdSAndreas Gohr $sep = strpos($match, ']('); 63*e89aeebdSAndreas Gohr $text = substr($match, 1, $sep - 1); 64*e89aeebdSAndreas Gohr $inside = trim(substr($match, $sep + 2, -1)); 65*e89aeebdSAndreas Gohr $url = substr($inside, 0, strcspn($inside, " \t\n")); 66*e89aeebdSAndreas Gohr 67*e89aeebdSAndreas Gohr $this->dispatchLink($url, $text, $pos, $handler); 68*e89aeebdSAndreas Gohr return true; 69*e89aeebdSAndreas Gohr } 70*e89aeebdSAndreas Gohr} 71