[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Parsing/Handler/ -> Lists.php (source)

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