1<?php
2
3/*
4 * This file is part of the league/commonmark package.
5 *
6 * (c) Colin O'Dell <colinodell@gmail.com>
7 *
8 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
9 *  - (c) John MacFarlane
10 *
11 * For the full copyright and license information, please view the LICENSE
12 * file that was distributed with this source code.
13 */
14
15namespace League\CommonMark;
16
17use League\CommonMark\Block\Element\AbstractBlock;
18use League\CommonMark\Block\Element\Document;
19use League\CommonMark\Reference\ReferenceParser;
20
21/**
22 * Maintains the current state of the Markdown parser engine
23 */
24class Context implements ContextInterface
25{
26    /**
27     * @var EnvironmentInterface
28     */
29    protected $environment;
30
31    /**
32     * @var Document
33     */
34    protected $doc;
35
36    /**
37     * @var AbstractBlock|null
38     */
39    protected $tip;
40
41    /**
42     * @var AbstractBlock
43     */
44    protected $container;
45
46    /**
47     * @var int
48     */
49    protected $lineNumber;
50
51    /**
52     * @var string
53     */
54    protected $line;
55
56    /**
57     * @var UnmatchedBlockCloser
58     */
59    protected $blockCloser;
60
61    /**
62     * @var bool
63     */
64    protected $blocksParsed = false;
65
66    /**
67     * @var ReferenceParser
68     */
69    protected $referenceParser;
70
71    public function __construct(Document $document, EnvironmentInterface $environment)
72    {
73        $this->doc = $document;
74        $this->tip = $this->doc;
75        $this->container = $this->doc;
76
77        $this->environment = $environment;
78
79        $this->referenceParser = new ReferenceParser($document->getReferenceMap());
80
81        $this->blockCloser = new UnmatchedBlockCloser($this);
82    }
83
84    /**
85     * @param string $line
86     *
87     * @return void
88     */
89    public function setNextLine(string $line)
90    {
91        ++$this->lineNumber;
92        $this->line = $line;
93    }
94
95    public function getDocument(): Document
96    {
97        return $this->doc;
98    }
99
100    public function getTip(): ?AbstractBlock
101    {
102        return $this->tip;
103    }
104
105    /**
106     * @param AbstractBlock|null $block
107     *
108     * @return $this
109     */
110    public function setTip(?AbstractBlock $block)
111    {
112        $this->tip = $block;
113
114        return $this;
115    }
116
117    public function getLineNumber(): int
118    {
119        return $this->lineNumber;
120    }
121
122    public function getLine(): string
123    {
124        return $this->line;
125    }
126
127    public function getBlockCloser(): UnmatchedBlockCloser
128    {
129        return $this->blockCloser;
130    }
131
132    public function getContainer(): AbstractBlock
133    {
134        return $this->container;
135    }
136
137    /**
138     * @param AbstractBlock $container
139     *
140     * @return $this
141     */
142    public function setContainer(AbstractBlock $container)
143    {
144        $this->container = $container;
145
146        return $this;
147    }
148
149    public function addBlock(AbstractBlock $block)
150    {
151        $this->blockCloser->closeUnmatchedBlocks();
152        $block->setStartLine($this->lineNumber);
153
154        while ($this->tip !== null && !$this->tip->canContain($block)) {
155            $this->tip->finalize($this, $this->lineNumber);
156        }
157
158        // This should always be true
159        if ($this->tip !== null) {
160            $this->tip->appendChild($block);
161        }
162
163        $this->tip = $block;
164        $this->container = $block;
165    }
166
167    public function replaceContainerBlock(AbstractBlock $replacement)
168    {
169        $this->blockCloser->closeUnmatchedBlocks();
170        $replacement->setStartLine($this->container->getStartLine());
171        $this->container->replaceWith($replacement);
172
173        if ($this->tip === $this->container) {
174            $this->tip = $replacement;
175        }
176
177        $this->container = $replacement;
178    }
179
180    public function getBlocksParsed(): bool
181    {
182        return $this->blocksParsed;
183    }
184
185    /**
186     * @param bool $bool
187     *
188     * @return $this
189     */
190    public function setBlocksParsed(bool $bool)
191    {
192        $this->blocksParsed = $bool;
193
194        return $this;
195    }
196
197    public function getReferenceParser(): ReferenceParser
198    {
199        return $this->referenceParser;
200    }
201}
202