1*531e725cSNickeau<?php 2*531e725cSNickeau 3*531e725cSNickeau// implementation of 4*531e725cSNickeau// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code 5*531e725cSNickeau 6*531e725cSNickeau// must be run within Dokuwiki 7*531e725cSNickeauuse ComboStrap\PluginUtility; 8*531e725cSNickeauuse ComboStrap\Prism; 9*531e725cSNickeauuse ComboStrap\Tag; 10*531e725cSNickeauuse ComboStrap\TagAttributes; 11*531e725cSNickeau 12*531e725cSNickeaurequire_once(__DIR__ . '/../class/StringUtility.php'); 13*531e725cSNickeaurequire_once(__DIR__ . '/../class/Prism.php'); 14*531e725cSNickeau 15*531e725cSNickeauif (!defined('DOKU_INC')) die(); 16*531e725cSNickeau 17*531e725cSNickeau/** 18*531e725cSNickeau * 19*531e725cSNickeau * Support <a href="https://github.github.com/gfm/#fenced-code-blocks">Github code block</a> 20*531e725cSNickeau * 21*531e725cSNickeau * The original code markdown code block is the {@link syntax_plugin_combo_preformatted} 22*531e725cSNickeau */ 23*531e725cSNickeauclass syntax_plugin_combo_codemarkdown extends DokuWiki_Syntax_Plugin 24*531e725cSNickeau{ 25*531e725cSNickeau 26*531e725cSNickeau 27*531e725cSNickeau /** 28*531e725cSNickeau * The exit pattern of markdown 29*531e725cSNickeau * use in the enter pattern as regexp look-ahead 30*531e725cSNickeau */ 31*531e725cSNickeau const MARKDOWN_EXIT_PATTERN = "^\s*(?:\`|~){3}\s*$"; 32*531e725cSNickeau 33*531e725cSNickeau /** 34*531e725cSNickeau * The syntax name, not a tag 35*531e725cSNickeau * This must be the same that the last part name 36*531e725cSNickeau * of the class (without any space !) 37*531e725cSNickeau * This is the id in the callstack 38*531e725cSNickeau * and gives us just a way to get the 39*531e725cSNickeau * {@link \dokuwiki\Extension\SyntaxPlugin::getPluginComponent()} 40*531e725cSNickeau * value (tag = plugin component) 41*531e725cSNickeau */ 42*531e725cSNickeau const TAG = "codemarkdown"; 43*531e725cSNickeau 44*531e725cSNickeau 45*531e725cSNickeau function getType() 46*531e725cSNickeau { 47*531e725cSNickeau /** 48*531e725cSNickeau * You can't write in a code block 49*531e725cSNickeau */ 50*531e725cSNickeau return 'protected'; 51*531e725cSNickeau } 52*531e725cSNickeau 53*531e725cSNickeau /** 54*531e725cSNickeau * How DokuWiki will add P element 55*531e725cSNickeau * 56*531e725cSNickeau * * 'normal' - The plugin can be used inside paragraphs 57*531e725cSNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 58*531e725cSNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 59*531e725cSNickeau * 60*531e725cSNickeau * @see DokuWiki_Syntax_Plugin::getPType() 61*531e725cSNickeau */ 62*531e725cSNickeau function getPType() 63*531e725cSNickeau { 64*531e725cSNickeau return 'block'; 65*531e725cSNickeau } 66*531e725cSNickeau 67*531e725cSNickeau /** 68*531e725cSNickeau * @return array 69*531e725cSNickeau * Allow which kind of plugin inside 70*531e725cSNickeau * 71*531e725cSNickeau * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 72*531e725cSNickeau * because we manage self the content and we call self the parser 73*531e725cSNickeau * 74*531e725cSNickeau * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 75*531e725cSNickeau */ 76*531e725cSNickeau function getAllowedTypes() 77*531e725cSNickeau { 78*531e725cSNickeau return array(); 79*531e725cSNickeau } 80*531e725cSNickeau 81*531e725cSNickeau function getSort() 82*531e725cSNickeau { 83*531e725cSNickeau 84*531e725cSNickeau return 199; 85*531e725cSNickeau } 86*531e725cSNickeau 87*531e725cSNickeau 88*531e725cSNickeau function connectTo($mode) 89*531e725cSNickeau { 90*531e725cSNickeau 91*531e725cSNickeau // 92*531e725cSNickeau // This pattern works with mgs flag 93*531e725cSNickeau // 94*531e725cSNickeau // Match: 95*531e725cSNickeau // * the start of a line 96*531e725cSNickeau // * any consecutive blank character 97*531e725cSNickeau // * 3 consecutive backtick characters (`) or tildes (~) 98*531e725cSNickeau // * a word (info string - ie the language) 99*531e725cSNickeau // * any consecutive blank character 100*531e725cSNickeau // * the end of a line 101*531e725cSNickeau // * a look ahead to see if we have the exit pattern 102*531e725cSNickeau // https://github.github.com/gfm/#fenced-code-blocks 103*531e725cSNickeau $gitHubMarkdownPattern = "^\s*(?:\`|~){3}\w*\s*$(?=.*?" . self::MARKDOWN_EXIT_PATTERN . ")"; 104*531e725cSNickeau $this->Lexer->addEntryPattern($gitHubMarkdownPattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 105*531e725cSNickeau 106*531e725cSNickeau 107*531e725cSNickeau } 108*531e725cSNickeau 109*531e725cSNickeau 110*531e725cSNickeau function postConnect() 111*531e725cSNickeau { 112*531e725cSNickeau 113*531e725cSNickeau 114*531e725cSNickeau $this->Lexer->addExitPattern(self::MARKDOWN_EXIT_PATTERN, PluginUtility::getModeForComponent($this->getPluginComponent())); 115*531e725cSNickeau 116*531e725cSNickeau 117*531e725cSNickeau } 118*531e725cSNickeau 119*531e725cSNickeau /** 120*531e725cSNickeau * 121*531e725cSNickeau * The handle function goal is to parse the matched syntax through the pattern function 122*531e725cSNickeau * and to return the result for use in the renderer 123*531e725cSNickeau * This result is always cached until the page is modified. 124*531e725cSNickeau * @param string $match 125*531e725cSNickeau * @param int $state 126*531e725cSNickeau * @param int $pos - byte position in the original source file 127*531e725cSNickeau * @param Doku_Handler $handler 128*531e725cSNickeau * @return array|bool 129*531e725cSNickeau * @see DokuWiki_Syntax_Plugin::handle() 130*531e725cSNickeau * 131*531e725cSNickeau */ 132*531e725cSNickeau function handle($match, $state, $pos, Doku_Handler $handler) 133*531e725cSNickeau { 134*531e725cSNickeau 135*531e725cSNickeau switch ($state) { 136*531e725cSNickeau 137*531e725cSNickeau case DOKU_LEXER_ENTER: 138*531e725cSNickeau $trimmedMatch = trim($match); 139*531e725cSNickeau $language = preg_replace("(`{3}|~{3})", "", $trimmedMatch); 140*531e725cSNickeau 141*531e725cSNickeau $attributes = [TagAttributes::TYPE_KEY => $language]; 142*531e725cSNickeau return array( 143*531e725cSNickeau PluginUtility::STATE => $state, 144*531e725cSNickeau PluginUtility::ATTRIBUTES => $attributes 145*531e725cSNickeau ); 146*531e725cSNickeau 147*531e725cSNickeau case DOKU_LEXER_UNMATCHED : 148*531e725cSNickeau 149*531e725cSNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 150*531e725cSNickeau 151*531e725cSNickeau 152*531e725cSNickeau case DOKU_LEXER_EXIT : 153*531e725cSNickeau 154*531e725cSNickeau return array(PluginUtility::STATE => $state); 155*531e725cSNickeau 156*531e725cSNickeau 157*531e725cSNickeau } 158*531e725cSNickeau return array(); 159*531e725cSNickeau 160*531e725cSNickeau } 161*531e725cSNickeau 162*531e725cSNickeau /** 163*531e725cSNickeau * Render the output 164*531e725cSNickeau * @param string $format 165*531e725cSNickeau * @param Doku_Renderer $renderer 166*531e725cSNickeau * @param array $data - what the function handle() return'ed 167*531e725cSNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 168*531e725cSNickeau * @see DokuWiki_Syntax_Plugin::render() 169*531e725cSNickeau * 170*531e725cSNickeau * 171*531e725cSNickeau */ 172*531e725cSNickeau function render($format, Doku_Renderer $renderer, $data) 173*531e725cSNickeau { 174*531e725cSNickeau 175*531e725cSNickeau 176*531e725cSNickeau if ($format == 'xhtml') { 177*531e725cSNickeau 178*531e725cSNickeau /** @var Doku_Renderer_xhtml $renderer */ 179*531e725cSNickeau $state = $data [PluginUtility::STATE]; 180*531e725cSNickeau switch ($state) { 181*531e725cSNickeau case DOKU_LEXER_ENTER : 182*531e725cSNickeau $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES], syntax_plugin_combo_code::CODE_TAG); 183*531e725cSNickeau Prism::htmlEnter($renderer, $this, $attributes); 184*531e725cSNickeau break; 185*531e725cSNickeau 186*531e725cSNickeau case DOKU_LEXER_UNMATCHED : 187*531e725cSNickeau 188*531e725cSNickeau // Delete the eol at the beginning and end 189*531e725cSNickeau // otherwise we get a big block 190*531e725cSNickeau $payload = trim($data[PluginUtility::PAYLOAD], "\n\r"); 191*531e725cSNickeau $renderer->doc .= PluginUtility::htmlEncode($payload); 192*531e725cSNickeau break; 193*531e725cSNickeau 194*531e725cSNickeau case DOKU_LEXER_EXIT : 195*531e725cSNickeau 196*531e725cSNickeau Prism::htmlExit($renderer); 197*531e725cSNickeau break; 198*531e725cSNickeau 199*531e725cSNickeau } 200*531e725cSNickeau return true; 201*531e725cSNickeau } else if ($format == 'code') { 202*531e725cSNickeau 203*531e725cSNickeau /** 204*531e725cSNickeau * The renderer to download the code 205*531e725cSNickeau * @var Doku_Renderer_code $renderer 206*531e725cSNickeau */ 207*531e725cSNickeau $state = $data [PluginUtility::STATE]; 208*531e725cSNickeau if ($state == DOKU_LEXER_UNMATCHED) { 209*531e725cSNickeau 210*531e725cSNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 211*531e725cSNickeau $text = $data[PluginUtility::PAYLOAD]; 212*531e725cSNickeau $language = strtolower($attributes["type"]); 213*531e725cSNickeau $filename = $language; 214*531e725cSNickeau $renderer->code($text, $language, $filename); 215*531e725cSNickeau 216*531e725cSNickeau } 217*531e725cSNickeau } 218*531e725cSNickeau 219*531e725cSNickeau // unsupported $mode 220*531e725cSNickeau return false; 221*531e725cSNickeau 222*531e725cSNickeau } 223*531e725cSNickeau 224*531e725cSNickeau 225*531e725cSNickeau} 226*531e725cSNickeau 227