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\Block\Element;
16
17use League\CommonMark\ContextInterface;
18use League\CommonMark\Cursor;
19
20/**
21 * @method children() AbstractBlock[]
22 */
23class ListBlock extends AbstractBlock
24{
25    const TYPE_BULLET = 'bullet';
26    const TYPE_ORDERED = 'ordered';
27
28    /**
29     * @deprecated This constant is deprecated in league/commonmark 1.4 and will be removed in 2.0; use TYPE_BULLET instead
30     */
31    const TYPE_UNORDERED = self::TYPE_BULLET;
32
33    /**
34     * @var bool
35     */
36    protected $tight = false;
37
38    /**
39     * @var ListData
40     */
41    protected $listData;
42
43    public function __construct(ListData $listData)
44    {
45        $this->listData = $listData;
46    }
47
48    /**
49     * @return ListData
50     */
51    public function getListData(): ListData
52    {
53        return $this->listData;
54    }
55
56    public function endsWithBlankLine(): bool
57    {
58        if ($this->lastLineBlank) {
59            return true;
60        }
61
62        if ($this->hasChildren()) {
63            return $this->lastChild() instanceof AbstractBlock && $this->lastChild()->endsWithBlankLine();
64        }
65
66        return false;
67    }
68
69    public function canContain(AbstractBlock $block): bool
70    {
71        return $block instanceof ListItem;
72    }
73
74    public function isCode(): bool
75    {
76        return false;
77    }
78
79    public function matchesNextLine(Cursor $cursor): bool
80    {
81        return true;
82    }
83
84    public function finalize(ContextInterface $context, int $endLineNumber)
85    {
86        parent::finalize($context, $endLineNumber);
87
88        $this->tight = true; // tight by default
89
90        foreach ($this->children() as $item) {
91            if (!($item instanceof AbstractBlock)) {
92                continue;
93            }
94
95            // check for non-final list item ending with blank line:
96            if ($item->endsWithBlankLine() && $item !== $this->lastChild()) {
97                $this->tight = false;
98                break;
99            }
100
101            // Recurse into children of list item, to see if there are
102            // spaces between any of them:
103            foreach ($item->children() as $subItem) {
104                if ($subItem instanceof AbstractBlock && $subItem->endsWithBlankLine() && ($item !== $this->lastChild() || $subItem !== $item->lastChild())) {
105                    $this->tight = false;
106                    break;
107                }
108            }
109        }
110    }
111
112    public function isTight(): bool
113    {
114        return $this->tight;
115    }
116
117    public function setTight(bool $tight): self
118    {
119        $this->tight = $tight;
120
121        return $this;
122    }
123}
124