1be906b56SAndreas Gohr<?php 2be906b56SAndreas Gohr 3be906b56SAndreas Gohrnamespace dokuwiki\Parsing\Handler; 4be906b56SAndreas Gohr 5533aca44SAndreas Gohrclass Lists extends AbstractRewriter 6be906b56SAndreas Gohr{ 7bcaec9f4SAndreas Gohr protected $listCalls = []; 8bcaec9f4SAndreas Gohr protected $listStack = []; 9be906b56SAndreas Gohr 10be906b56SAndreas Gohr protected $initialDepth = 0; 11be906b56SAndreas Gohr 1274981a4eSAndreas Gohr public const NODE = 1; 13be906b56SAndreas Gohr 14be906b56SAndreas Gohr /** @inheritdoc */ 15be906b56SAndreas Gohr public function finalise() 16be906b56SAndreas Gohr { 17be906b56SAndreas Gohr $last_call = end($this->calls); 18bcaec9f4SAndreas Gohr $this->writeCall(['list_close', [], $last_call[2]]); 19be906b56SAndreas Gohr 20be906b56SAndreas Gohr $this->process(); 21be906b56SAndreas Gohr $this->callWriter->finalise(); 22be906b56SAndreas Gohr unset($this->callWriter); 23be906b56SAndreas Gohr } 24be906b56SAndreas Gohr 25be906b56SAndreas Gohr /** @inheritdoc */ 26be906b56SAndreas Gohr public function process() 27be906b56SAndreas Gohr { 28be906b56SAndreas Gohr 29be906b56SAndreas Gohr foreach ($this->calls as $call) { 30be906b56SAndreas Gohr switch ($call[0]) { 31be906b56SAndreas Gohr case 'list_item': 32be906b56SAndreas Gohr $this->listOpen($call); 33be906b56SAndreas Gohr break; 34be906b56SAndreas Gohr case 'list_open': 35be906b56SAndreas Gohr $this->listStart($call); 36be906b56SAndreas Gohr break; 37be906b56SAndreas Gohr case 'list_close': 38be906b56SAndreas Gohr $this->listEnd($call); 39be906b56SAndreas Gohr break; 40be906b56SAndreas Gohr default: 41be906b56SAndreas Gohr $this->listContent($call); 42be906b56SAndreas Gohr break; 43be906b56SAndreas Gohr } 44be906b56SAndreas Gohr } 45be906b56SAndreas Gohr 46be906b56SAndreas Gohr $this->callWriter->writeCalls($this->listCalls); 47be906b56SAndreas Gohr return $this->callWriter; 48be906b56SAndreas Gohr } 49be906b56SAndreas Gohr 50be906b56SAndreas Gohr protected function listStart($call) 51be906b56SAndreas Gohr { 52be906b56SAndreas Gohr $depth = $this->interpretSyntax($call[1][0], $listType); 53be906b56SAndreas Gohr 54be906b56SAndreas Gohr $this->initialDepth = $depth; 55be906b56SAndreas Gohr // array(list type, current depth, index of current listitem_open) 56bcaec9f4SAndreas Gohr $this->listStack[] = [$listType, $depth, 1]; 57be906b56SAndreas Gohr 58bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $listType . '_open', [], $call[2]]; 59bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_open', [1], $call[2]]; 60bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_open', [], $call[2]]; 61be906b56SAndreas Gohr } 62be906b56SAndreas Gohr 63be906b56SAndreas Gohr 64be906b56SAndreas Gohr protected function listEnd($call) 65be906b56SAndreas Gohr { 66be906b56SAndreas Gohr $closeContent = true; 67be906b56SAndreas Gohr 68be906b56SAndreas Gohr while ($list = array_pop($this->listStack)) { 69be906b56SAndreas Gohr if ($closeContent) { 70bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_close', [], $call[2]]; 71be906b56SAndreas Gohr $closeContent = false; 72be906b56SAndreas Gohr } 73bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_close', [], $call[2]]; 74bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $list[0] . '_close', [], $call[2]]; 75be906b56SAndreas Gohr } 76be906b56SAndreas Gohr } 77be906b56SAndreas Gohr 78be906b56SAndreas Gohr protected function listOpen($call) 79be906b56SAndreas Gohr { 80be906b56SAndreas Gohr $depth = $this->interpretSyntax($call[1][0], $listType); 81be906b56SAndreas Gohr $end = end($this->listStack); 82be906b56SAndreas Gohr $key = key($this->listStack); 83be906b56SAndreas Gohr 84be906b56SAndreas Gohr // Not allowed to be shallower than initialDepth 85be906b56SAndreas Gohr if ($depth < $this->initialDepth) { 86be906b56SAndreas Gohr $depth = $this->initialDepth; 87be906b56SAndreas Gohr } 88be906b56SAndreas Gohr 89be906b56SAndreas Gohr if ($depth == $end[1]) { 90be906b56SAndreas Gohr // Just another item in the list... 91be906b56SAndreas Gohr if ($listType == $end[0]) { 92bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_close', [], $call[2]]; 93bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_close', [], $call[2]]; 94bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_open', [$depth - 1], $call[2]]; 95bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_open', [], $call[2]]; 96be906b56SAndreas Gohr 97be906b56SAndreas Gohr // new list item, update list stack's index into current listitem_open 98be906b56SAndreas Gohr $this->listStack[$key][2] = count($this->listCalls) - 2; 99be906b56SAndreas Gohr 100be906b56SAndreas Gohr // Switched list type... 101be906b56SAndreas Gohr } else { 102bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_close', [], $call[2]]; 103bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_close', [], $call[2]]; 104bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $end[0] . '_close', [], $call[2]]; 105bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $listType . '_open', [], $call[2]]; 106bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_open', [$depth - 1], $call[2]]; 107bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_open', [], $call[2]]; 108be906b56SAndreas Gohr 109be906b56SAndreas Gohr array_pop($this->listStack); 110bcaec9f4SAndreas Gohr $this->listStack[] = [$listType, $depth, count($this->listCalls) - 2]; 111be906b56SAndreas Gohr } 112be906b56SAndreas Gohr } elseif ($depth > $end[1]) { // Getting deeper... 113bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_close', [], $call[2]]; 114bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $listType . '_open', [], $call[2]]; 115bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_open', [$depth - 1], $call[2]]; 116bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_open', [], $call[2]]; 117be906b56SAndreas Gohr 118be906b56SAndreas Gohr // set the node/leaf state of this item's parent listitem_open to NODE 119be906b56SAndreas Gohr $this->listCalls[$this->listStack[$key][2]][1][1] = self::NODE; 120be906b56SAndreas Gohr 121bcaec9f4SAndreas Gohr $this->listStack[] = [$listType, $depth, count($this->listCalls) - 2]; 122be906b56SAndreas Gohr } else { // Getting shallower ( $depth < $end[1] ) 123bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_close', [], $call[2]]; 124bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_close', [], $call[2]]; 125bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $end[0] . '_close', [], $call[2]]; 126be906b56SAndreas Gohr 127be906b56SAndreas Gohr // Throw away the end - done 128be906b56SAndreas Gohr array_pop($this->listStack); 129be906b56SAndreas Gohr 130be906b56SAndreas Gohr while (1) { 131be906b56SAndreas Gohr $end = end($this->listStack); 132be906b56SAndreas Gohr $key = key($this->listStack); 133be906b56SAndreas Gohr 134be906b56SAndreas Gohr if ($end[1] <= $depth) { 135be906b56SAndreas Gohr // Normalize depths 136be906b56SAndreas Gohr $depth = $end[1]; 137be906b56SAndreas Gohr 138bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_close', [], $call[2]]; 139be906b56SAndreas Gohr 140be906b56SAndreas Gohr if ($end[0] == $listType) { 141bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_open', [$depth - 1], $call[2]]; 142bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_open', [], $call[2]]; 143be906b56SAndreas Gohr 144be906b56SAndreas Gohr // new list item, update list stack's index into current listitem_open 145be906b56SAndreas Gohr $this->listStack[$key][2] = count($this->listCalls) - 2; 146be906b56SAndreas Gohr } else { 147be906b56SAndreas Gohr // Switching list type... 148bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $end[0] . '_close', [], $call[2]]; 149bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $listType . '_open', [], $call[2]]; 150bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_open', [$depth - 1], $call[2]]; 151bcaec9f4SAndreas Gohr $this->listCalls[] = ['listcontent_open', [], $call[2]]; 152be906b56SAndreas Gohr 153be906b56SAndreas Gohr array_pop($this->listStack); 154bcaec9f4SAndreas Gohr $this->listStack[] = [$listType, $depth, count($this->listCalls) - 2]; 155be906b56SAndreas Gohr } 156be906b56SAndreas Gohr 157be906b56SAndreas Gohr break; 158be906b56SAndreas Gohr 159be906b56SAndreas Gohr // Haven't dropped down far enough yet.... ( $end[1] > $depth ) 160be906b56SAndreas Gohr } else { 161bcaec9f4SAndreas Gohr $this->listCalls[] = ['listitem_close', [], $call[2]]; 162bcaec9f4SAndreas Gohr $this->listCalls[] = ['list' . $end[0] . '_close', [], $call[2]]; 163be906b56SAndreas Gohr 164be906b56SAndreas Gohr array_pop($this->listStack); 165be906b56SAndreas Gohr } 166be906b56SAndreas Gohr } 167be906b56SAndreas Gohr } 168be906b56SAndreas Gohr } 169be906b56SAndreas Gohr 170be906b56SAndreas Gohr protected function listContent($call) 171be906b56SAndreas Gohr { 172be906b56SAndreas Gohr $this->listCalls[] = $call; 173be906b56SAndreas Gohr } 174be906b56SAndreas Gohr 175be906b56SAndreas Gohr protected function interpretSyntax($match, &$type) 176be906b56SAndreas Gohr { 177*6c16a3a9Sfiwswe if (str_ends_with($match, '*')) { 178be906b56SAndreas Gohr $type = 'u'; 179be906b56SAndreas Gohr } else { 180be906b56SAndreas Gohr $type = 'o'; 181be906b56SAndreas Gohr } 182be906b56SAndreas Gohr // Is the +1 needed? It used to be count(explode(...)) 183be906b56SAndreas Gohr // but I don't think the number is seen outside this handler 184be906b56SAndreas Gohr return substr_count(str_replace("\t", ' ', $match), ' ') + 1; 185be906b56SAndreas Gohr } 186be906b56SAndreas Gohr} 187