1<?php 2 3 4use ComboStrap\CallStack; 5use ComboStrap\Dimension; 6use ComboStrap\Html; 7use ComboStrap\PluginUtility; 8use ComboStrap\Prism; 9use ComboStrap\TagAttributes; 10use ComboStrap\XmlTagProcessing; 11 12require_once(__DIR__ . '/../vendor/autoload.php'); 13 14/** 15 * Class syntax_plugin_combo_code 16 * 17 * Implementation of https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code 18 * with Prism 19 * 20 * Support <a href="https://github.github.com/gfm/#fenced-code-blocks">Github code block</a> 21 * 22 * The original code markdown code block is the {@link syntax_plugin_combo_preformatted} 23 * 24 * Mdx Syntax: https://mdxjs.com/guides/syntax-highlighting/ 25 * Rehype Plugin used by Floating-ui: https://rehype-pretty-code.netlify.app/ 26 */ 27class syntax_plugin_combo_code extends DokuWiki_Syntax_Plugin 28{ 29 30 /** 31 * Enable or disable the code component 32 */ 33 const CONF_CODE_ENABLE = 'codeEnable'; 34 35 36 /** 37 * The tag of the ui component 38 */ 39 const CODE_TAG = "code"; 40 const FILE_PATH_KEY = "file-path"; 41 42 43 function getType() 44 { 45 /** 46 * You can't write in a code block 47 */ 48 return 'protected'; 49 } 50 51 /** 52 * How DokuWiki will add P element 53 * 54 * * 'normal' - The plugin can be used inside paragraphs 55 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 56 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 57 * 58 * @see DokuWiki_Syntax_Plugin::getPType() 59 */ 60 function getPType() 61 { 62 return 'block'; 63 } 64 65 /** 66 * @return array 67 * Allow which kind of plugin inside 68 * 69 * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 70 * because we manage self the content and we call self the parser 71 * 72 * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 73 */ 74 function getAllowedTypes() 75 { 76 return array(); 77 } 78 79 function getSort(): int 80 { 81 /** 82 * Should be less than the code syntax plugin 83 * which is 200 84 **/ 85 return 199; 86 } 87 88 89 function connectTo($mode) 90 { 91 92 if ($this->getConf(self::CONF_CODE_ENABLE)) { 93 $pattern = XmlTagProcessing::getContainerTagPattern(self::CODE_TAG); 94 $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 95 } 96 97 98 } 99 100 101 function postConnect() 102 { 103 if ($this->getConf(self::CONF_CODE_ENABLE)) { 104 $this->Lexer->addExitPattern('</' . self::CODE_TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent())); 105 } 106 107 108 } 109 110 /** 111 * 112 * The handle function goal is to parse the matched syntax through the pattern function 113 * and to return the result for use in the renderer 114 * This result is always cached until the page is modified. 115 * @param string $match 116 * @param int $state 117 * @param int $pos - byte position in the original source file 118 * @param Doku_Handler $handler 119 * @return array|bool 120 * @see DokuWiki_Syntax_Plugin::handle() 121 * 122 */ 123 function handle($match, $state, $pos, Doku_Handler $handler) 124 { 125 126 switch ($state) { 127 128 case DOKU_LEXER_ENTER : 129 $tagAttributes = PluginUtility::getQualifiedTagAttributes($match, true, self::FILE_PATH_KEY, [], true); 130 return array( 131 PluginUtility::STATE => $state, 132 PluginUtility::ATTRIBUTES => $tagAttributes 133 ); 134 135 case DOKU_LEXER_UNMATCHED : 136 137 $data = PluginUtility::handleAndReturnUnmatchedData(self::CODE_TAG, $match, $handler); 138 /** 139 * Attribute are send for the 140 * export of code functionality 141 * and display = none 142 */ 143 $callStack = CallStack::createFromHandler($handler); 144 $parentTag = $callStack->moveToParent(); 145 $tagAttributes = $parentTag->getAttributes(); 146 $data[PluginUtility::ATTRIBUTES] = $tagAttributes; 147 return $data; 148 149 150 case DOKU_LEXER_EXIT : 151 /** 152 * Tag Attributes are passed 153 * because it's possible to not display a code with the display attributes = none 154 */ 155 $callStack = CallStack::createFromHandler($handler); 156 Dimension::addScrollToggleOnClickIfNoControl($callStack); 157 158 $callStack->moveToEnd(); 159 $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 160 return array( 161 PluginUtility::STATE => $state, 162 PluginUtility::ATTRIBUTES => $openingTag->getAttributes() 163 ); 164 165 166 } 167 return array(); 168 169 } 170 171 /** 172 * Render the output 173 * @param string $format 174 * @param Doku_Renderer $renderer 175 * @param array $data - what the function handle() return'ed 176 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 177 * @see DokuWiki_Syntax_Plugin::render() 178 * 179 * 180 */ 181 function render($format, Doku_Renderer $renderer, $data) 182 { 183 184 185 if ($format == 'xhtml') { 186 187 /** @var Doku_Renderer_xhtml $renderer */ 188 $state = $data [PluginUtility::STATE]; 189 switch ($state) { 190 case DOKU_LEXER_ENTER : 191 $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES], self::CODE_TAG); 192 Prism::htmlEnter($renderer, $this, $attributes); 193 break; 194 195 case DOKU_LEXER_UNMATCHED : 196 197 $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 198 $display = $attributes->getValue("display"); 199 if ($display != "none") { 200 // Delete the eol at the beginning and end 201 // otherwise we get a big block 202 $payload = trim($data[PluginUtility::PAYLOAD], "\n\r"); 203 $renderer->doc .= Html::encode($payload); 204 } 205 break; 206 207 case DOKU_LEXER_EXIT : 208 $attributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 209 Prism::htmlExit($renderer, $attributes); 210 break; 211 212 } 213 return true; 214 } else if ($format == 'code') { 215 216 /** 217 * The renderer to download the code 218 * @var Doku_Renderer_code $renderer 219 */ 220 $state = $data [PluginUtility::STATE]; 221 if ($state == DOKU_LEXER_UNMATCHED) { 222 223 $attributes = $data[PluginUtility::ATTRIBUTES]; 224 $text = $data[PluginUtility::PAYLOAD]; 225 $filename = $attributes[self::FILE_PATH_KEY]; 226 $language = strtolower($attributes["type"]); 227 $renderer->code($text, $language, $filename); 228 229 } 230 } 231 232 // unsupported $mode 233 return false; 234 235 } 236 237 238} 239 240