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