1<?php
2
3declare(strict_types=1);
4
5/*
6 * This file is part of the league/commonmark package.
7 *
8 * (c) Colin O'Dell <colinodell@gmail.com>
9 *
10 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
11 *  - (c) John MacFarlane
12 *
13 * For the full copyright and license information, please view the LICENSE
14 * file that was distributed with this source code.
15 */
16
17namespace League\CommonMark\Node;
18
19use League\CommonMark\Node\Block\AbstractBlock;
20
21final class NodeWalker
22{
23    /** @psalm-readonly */
24    private Node $root;
25
26    /** @psalm-readonly-allow-private-mutation */
27    private ?Node $current = null;
28
29    /** @psalm-readonly-allow-private-mutation */
30    private bool $entering;
31
32    public function __construct(Node $root)
33    {
34        $this->root     = $root;
35        $this->current  = $this->root;
36        $this->entering = true;
37    }
38
39    /**
40     * Returns an event which contains node and entering flag
41     * (entering is true when we enter a Node from a parent or sibling,
42     * and false when we reenter it from child)
43     */
44    public function next(): ?NodeWalkerEvent
45    {
46        $current  = $this->current;
47        $entering = $this->entering;
48        if ($current === null) {
49            return null;
50        }
51
52        if ($entering && ($current instanceof AbstractBlock || $current->hasChildren())) {
53            if ($current->firstChild()) {
54                $this->current  = $current->firstChild();
55                $this->entering = true;
56            } else {
57                $this->entering = false;
58            }
59        } elseif ($current === $this->root) {
60            $this->current = null;
61        } elseif ($current->next() === null) {
62            $this->current  = $current->parent();
63            $this->entering = false;
64        } else {
65            $this->current  = $current->next();
66            $this->entering = true;
67        }
68
69        return new NodeWalkerEvent($current, $entering);
70    }
71
72    /**
73     * Resets the iterator to resume at the specified node
74     */
75    public function resumeAt(Node $node, bool $entering = true): void
76    {
77        $this->current  = $node;
78        $this->entering = $entering;
79    }
80}
81