1<?php 2 3namespace dokuwiki\plugin\prosemirror\parser; 4 5class TextNode extends Node implements InlineNodeInterface 6{ 7 8 /** @var TextNode */ 9 public $previous = null; 10 11 /** @var Node */ 12 protected $parent; 13 14 /** @var Mark[] */ 15 protected $marks = []; 16 17 protected $text = ''; 18 19 public function __construct($data, Node $parent, Node $previous = null) 20 { 21 $this->parent = &$parent; 22 if ($previous !== false) { 23 $this->previous = &$previous; 24 } 25 26 $this->text = $data['text'] ?? ''; 27 if (isset($data['marks'])) { 28 $this->setMarks($data['marks']); 29 } 30 } 31 32 public function getPrefixSyntax() 33 { 34 $doc = ''; 35 36 /** @var Mark[] $openingMarks */ 37 $openingMarks = []; 38 foreach ($this->marks as $mark) { 39 if ($mark->isOpeningMark()) { 40 $previousOpeningMark = end($openingMarks); 41 if ($previousOpeningMark) { 42 $mark->setPrevious($previousOpeningMark); 43 $previousOpeningMark->setNext($mark); 44 } 45 $openingMarks[] = $mark; 46 } 47 } 48 49 if (!empty($openingMarks)) { 50 foreach ($openingMarks as $mark) { 51 while (!$mark->sort()) { 52 } 53 } 54 55 $mark = $openingMarks[0]->getFirst(); 56 $doc .= $mark->getOpeningSyntax(); 57 while ($mark = $mark->getNext()) { 58 $doc .= $mark->getOpeningSyntax(); 59 } 60 61 foreach ($openingMarks as $mark) { 62 $mark->setNext(null); 63 $mark->setPrevious(null); 64 } 65 } 66 return $doc; 67 } 68 69 public function getPostfixSyntax() 70 { 71 $doc = ''; 72 /** @var Mark[] $closingMarks */ 73 $closingMarks = []; 74 foreach ($this->marks as $mark) { 75 if ($mark->isClosingMark()) { 76 $previousClosingMark = end($closingMarks); 77 if ($previousClosingMark) { 78 $mark->setPrevious($previousClosingMark); 79 $previousClosingMark->setNext($mark); 80 } 81 $closingMarks[] = $mark; 82 } 83 } 84 85 if (!empty($closingMarks)) { 86 foreach ($closingMarks as $mark) { 87 while (!$mark->sort()) { 88 } 89 } 90 91 $mark = $closingMarks[0]->getLast(); 92 $doc .= $mark->getClosingSyntax(); 93 while ($mark = $mark->getPrevious()) { 94 $doc .= $mark->getClosingSyntax(); 95 } 96 97 foreach ($closingMarks as $mark) { 98 $mark->setNext(null); 99 $mark->setPrevious(null); 100 } 101 } 102 103 return $doc; 104 } 105 106 public function getInnerSyntax() 107 { 108 return $this->text; 109 } 110 111 112 public function toSyntax() 113 { 114 $prefix = $this->getPrefixSyntax(); 115 $inner = $this->getInnerSyntax(); 116 $postfix = $this->getPostfixSyntax(); 117 return $prefix . $inner . $postfix; 118 } 119 120 /** 121 * @param array $marks 122 * 123 * @return $this 124 * @throws \Exception 125 */ 126 protected function setMarks(array $marks) 127 { 128 foreach ($marks as $markData) { 129 $currentMark = new Mark($markData, $this); 130 $type = $currentMark->getType(); 131 $this->marks[$type] = $currentMark; 132 if ($this->previous !== null) { 133 $this->previous->increaseMark($type); 134 } 135 } 136 return $this; 137 } 138 139 /** 140 * @param string $markType 141 */ 142 public function increaseMark($markType) 143 { 144 if (!isset($this->marks[$markType])) { 145 return; 146 } 147 148 $this->marks[$markType]->incrementTail(); 149 if ($this->previous !== null) { 150 $this->previous->increaseMark($markType); 151 } 152 } 153 154 public function getStartingNodeMarkScore($markType) 155 { 156 if ($this === $this->previous) { 157 throw new \Exception('circular reference: ' . $this->text); 158 } 159 if (!isset($this->marks[$markType])) { 160 // we don't have that mark 161 return null; 162 } 163 if ($this->previous === null) { 164 // we are the first node 165 return $this->marks[$markType]->getTailLength(); 166 } 167 168 $earlierMarkScore = $this->previous->getStartingNodeMarkScore($markType); 169 if ($earlierMarkScore === null) { 170 // the mark begins with us 171 return $this->marks[$markType]->getTailLength(); 172 } 173 return $earlierMarkScore; 174 } 175} 176