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