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