1<?php 2/** 3 * DokuWiki Plugin syntaxhighlighter4 (Syntax Component). 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author CrazyMax <contact@crazymax.dev> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) { 11 die(); 12} 13 14if (!defined('DOKU_LF')) { 15 define('DOKU_LF', "\n"); 16} 17if (!defined('DOKU_TAB')) { 18 define('DOKU_TAB', "\t"); 19} 20if (!defined('DOKU_PLUGIN')) { 21 define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 22} 23 24require_once DOKU_PLUGIN.'syntax.php'; 25 26class syntax_plugin_syntaxhighlighter4 extends DokuWiki_Syntax_Plugin { 27 28 protected $syntax; 29 30 /** 31 * @return string Syntax mode type 32 */ 33 public function getType() { 34 return 'protected'; 35 } 36 37 /** 38 * @return string Paragraph type 39 */ 40 public function getPType() { 41 return 'block'; 42 } 43 44 /** 45 * @return int Sort order - Low numbers go before high numbers 46 */ 47 public function getSort() { 48 return 195; 49 } 50 51 /** 52 * Connect lookup pattern to lexer. 53 * 54 * @param string $mode Parser mode 55 */ 56 public function connectTo($mode) { 57 $this->Lexer->addEntryPattern('<sxh(?=[^\r\n]*?>.*?</sxh>)', $mode, 'plugin_syntaxhighlighter4'); 58 $tags = explode(',', $this->getConf('override')); 59 foreach ($tags as $tag) { 60 $this->Lexer->addEntryPattern('<' . $tag . '(?=[^\r\n]*?>.*?</' . $tag . '>)', $mode, 'plugin_syntaxhighlighter4'); 61 } 62 } 63 64 public function postConnect() { 65 $this->Lexer->addExitPattern('</sxh>', 'plugin_syntaxhighlighter4'); 66 $tags = explode(',', $this->getConf('override')); 67 foreach ($tags as $tag) { 68 $this->Lexer->addExitPattern('</' . $tag . '>', 'plugin_syntaxhighlighter4'); 69 } 70 } 71 72 /** 73 * Handle matches of the syntaxhighlighter4 syntax. 74 * 75 * @param string $match The match of the syntax 76 * @param int $state The state of the handler 77 * @param int $pos The position in the document 78 * @param Doku_Handler $handler The handler 79 * 80 * @return array Data for the renderer 81 */ 82 public function handle($match, $state, $pos, Doku_Handler $handler) { 83 switch ($state) { 84 case DOKU_LEXER_ENTER: 85 $this->syntax = strtolower(substr($match, 1)); 86 return false; 87 case DOKU_LEXER_UNMATCHED: 88 // will include everything from <sxh ... to ... </sxh> 89 list($attr, $content) = preg_split('/>/u', $match, 2); 90 if ($this->isSyntaxOk()) { 91 $attr = trim($attr); 92 if ($attr == null) { 93 // No brush and no options, use "text" with default options. 94 $attr = 'text'; 95 } elseif (substr($attr, 0, 1) == ';') { 96 // Options but no brush, add "text" brush. 97 $attr = 'text'.$attr; 98 } 99 } else { 100 $attr = null; 101 } 102 return array($this->syntax, $attr, $content); 103 } 104 return false; 105 } 106 107 /** 108 * Render xhtml output or metadata. 109 * 110 * @param string $mode Renderer mode (supported modes: xhtml) 111 * @param Doku_Renderer $renderer The renderer 112 * @param array $data The data from the handler() function 113 * 114 * @return bool If rendering was successful. 115 */ 116 public function render($mode, Doku_Renderer $renderer, $data) { 117 if ($mode != 'xhtml') { 118 return false; 119 } 120 121 if (count($data) != 3) { 122 return true; 123 } 124 125 list($this->syntax, $attr, $content) = $data; 126 if ($this->isSyntaxOk()) { 127 $title = $this->procTitle($attr); 128 $highlight = $this->procHighlight($attr); 129 $renderer->doc .= '<pre class="brush: '.strtolower($attr.$highlight).'"'.$title.'>'.$renderer->_xmlEntities($content).'</pre>'; 130 } else { 131 $renderer->file($content); 132 } 133 134 return true; 135 } 136 137 private function procTitle($attr) { 138 if ($this->syntax == 'file') { 139 $title = trim(substr($attr, strpos($attr, ' ') + 1)); 140 if (!empty($title)) { 141 return ' title="' . $title . '"'; 142 } 143 } elseif (preg_match('/title:/i', $attr)) { 144 // Extract title(s) from $attr string. 145 $attr_array = explode(';', $attr); 146 $title_array = preg_grep('/title:/i', $attr_array); 147 // Extract everything BUT title(s) from $attr string. 148 $not_title_array = preg_grep('/title:/i', $attr_array, PREG_GREP_INVERT); 149 $attr = implode(';', $not_title_array); 150 // If there are multiple titles, use the last one. 151 $title = array_pop($title_array); 152 return ' title="'.preg_replace("/.*title:\s{0,}(.*)/i", '$1', $title).'"'; 153 } 154 155 return ''; 156 } 157 158 private function procHighlight($attr) { 159 $highlight = ''; 160 161 // Check highlight attr for lines ranges 162 if (preg_match('/highlight:/i', $attr, $matches)) { 163 // Extract highlight from $attr string. 164 $attr_array = explode(';', $attr); 165 $highlight_array = preg_grep('/highlight:/i', $attr_array); 166 // Extract everything BUT highlight from $attr string. 167 $not_highlight_array = preg_grep('/highlight:/i', $attr_array, PREG_GREP_INVERT); 168 $attr = implode(';', $not_highlight_array); 169 // If there are multiple hightlights, use the last one. 170 $highlight_str = array_pop($highlight_array); 171 $highlight_str = preg_replace("/.*highlight:\s{0,}(.*)/i", '$1', $highlight_str); 172 // Remove [ ] 173 $highlight_str = str_replace(array('[', ']'), '', $highlight_str); 174 // Process ranges if exists 175 $highlight_exp = explode(',', $highlight_str); 176 foreach ($highlight_exp as $highlight_elt) { 177 if (!empty($highlight)) { 178 $highlight .= ','; 179 } 180 $highlight_elt = trim($highlight_elt); 181 $highlight_elt_exp = explode('-', $highlight_elt); 182 if (count($highlight_elt_exp) == 2) { 183 foreach (range($highlight_elt_exp[0], $highlight_elt_exp[1]) as $key => $lineNumber) { 184 if ($key > 0) { 185 $highlight .= ','; 186 } 187 $highlight .= $lineNumber; 188 } 189 } else { 190 $highlight .= $highlight_elt; 191 } 192 } 193 $highlight = ' highlight: ['.$highlight.']'; 194 } 195 196 return $highlight; 197 } 198 199 private function isSyntaxOk() { 200 if ($this->syntax == 'sxh') { 201 return true; 202 } 203 $tags = explode(',', $this->getConf('override')); 204 foreach ($tags as $tag) { 205 if ($this->syntax == $tag) { 206 return true; 207 } 208 } 209 return false; 210 } 211} 212