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