Lists.php 6.9 KB

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