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