xref: /plugin/commonmark/action.php (revision 80734199223681576dd58b9113e35ae4786f6692)
1<?php
2/*
3 * This file is part of the clockoon/dokuwiki-commonmark-plugin package.
4 *
5 * (c) Sungbin Jeon <clockoon@gmail.com>
6 *
7 * Original code based on the followings:
8 * - CommonMark JS reference parser (https://bitly.com/commonmark-js) (c) John MacFarlane
9 * - league/commonmark (https://github.com/thephpleague/commonmark) (c) Colin O'Dell <colinodell@gmail.com>
10 *
11 * For the full copyright and license information, please view the LICENSE
12 * file that was distributed with this source code.
13 */
14
15 require_once __DIR__.'/src/bootstrap.php';
16
17 use Dokuwiki\Plugin\Commonmark\Commonmark;
18 use Nette\Utils\Strings;
19
20 if(!defined('DOKU_INC')) die();
21
22 class action_plugin_commonmark extends DokuWiki_Action_Plugin {
23    // array for heading positions
24    // [hid] => {[depth], [startline], [endline]}
25    public $headingInfo = [];
26    // positions of newline
27    public $linePosition = [];
28    // flag for checking first run only
29    public $firstRun = true;
30
31    /**
32     * pass text to Commonmark parser before DW parser
33     */
34    public function register(Doku_Event_Handler $controller) {
35        $controller->register_hook('PARSER_WIKITEXT_PREPROCESS', 'BEFORE', $this,
36                                    '_commonmarkparse');
37        $controller->register_hook('HTML_SECEDIT_BUTTON', 'BEFORE', $this,
38                                    '_editbutton');
39    }
40
41    public function _editbutton(Doku_Event $event, $param) {
42        global $conf;
43
44        // get hid
45        $hid = $event->data['hid'];
46        // fetch range on original md
47        // check hid match
48        $keys = array_keys($this->headingInfo);
49        if (array_key_exists($hid,$keys)) {
50            // get max section editing level config
51            $maxsec = $conf['maxseclevel'];
52            // set start position
53            // first, check whether first heading
54            if ($hid == $keys[0]) {
55                $start = 1;
56            } else {
57                $lineStart = $this->headingInfo[$hid]['startline'];
58                // since CommonMark library finds heading marks, we have to declare
59                $start = $this->linePosition[$lineStart];
60            }
61            // find end key & location; proceed while max level or less arrived
62            $endlevel = 52;
63            $index = array_search($hid,$keys);
64            $end = 0; // 0 means end of document
65            $stop = false;
66            while($stop == false) {
67                if (isset($keys[$index+1])) { // check for non-last element
68                    $endlevel = $this->headingInfo[$keys[$index+1]]['level'];
69                    $lineEnd = $this->headingInfo[$keys[$index+1]]['startline'] - 1; // go one line up
70                    $end = $this->linePosition[$lineEnd];
71                    if($maxsec<=$endlevel) { $stop = true; }
72                } else {
73                    $end = 0;
74                    $stop = true;
75                }
76                $index = $index + 1;
77            }
78            if($end == 0) {
79                $event->data['range'] = (string)$start.'-';
80            } else {
81                $event->data['range'] = (string)$start.'-'.$end;
82            }
83
84        }
85        // example: $event->data['range'] = '1-2';
86    }
87
88    public function _commonmarkparse(Doku_Event $event, $param) {
89        $markdown = $event->data;
90        // check force_commonmark option; if 1, ignore doctype
91        if ($this->getConf('force_commonmark')) {
92            $markdown = ltrim($markdown);
93            $result = Commonmark::RendtoDW($markdown, $this->getConf('frontmatter_tag'));
94        }
95        elseif (preg_match('/\A<!DOCTYPE markdown>/',$markdown)) {
96            $markdown = preg_replace('/\A<!DOCTYPE markdown>\n/','',$markdown);
97            $markdown = ltrim($markdown);
98            $result = Commonmark::RendtoDW($markdown, $this->getConf('frontmatter_tag'));
99            $event->data = $result['text'];
100        }
101        $event->data = $result['text'];
102        if ($this->firstRun == true) {
103            // get position of each line
104            $lastPos = 0;
105            while(($lastPos = strpos($markdown,'\n',$lastPos)) !== false){
106                $linePosition[] = $lastPos;
107                $lastPos = $lastPos + strlen('\n');
108            }
109            $this->headingInfo = $this->CleanHeadingInfo($result['heading']);
110            $this->firstRun = false;
111        }
112    }
113
114    public function CleanHeadingInfo(array $input): array {
115        $keys = array_keys($input);
116        foreach($keys as $key) {
117            $new_key = sectionId($key, false);
118            if($new_key != $key) {
119                $input[$new_key] = $input[$key];
120                unset($input[$key]);
121            }
122        }
123        return $input;
124    }
125}
126