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