Passed
Pull Request — main (#1074)
by
unknown
04:44 queued 02:25
created

TableOfContentsBuilder   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 83
Duplicated Lines 0 %

Test Coverage

Coverage 97.56%

Importance

Changes 0
Metric Value
eloc 43
c 0
b 0
f 0
dl 0
loc 83
ccs 40
cts 41
cp 0.9756
rs 10
wmc 16

4 Methods

Rating   Name   Duplication   Size   Complexity  
A setConfiguration() 0 3 1
B onDocumentParsed() 0 39 7
A insertBeforeFirstLinkedHeading() 0 12 5
A replacePlaceholders() 0 9 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the league/commonmark package.
7
 *
8
 * (c) Colin O'Dell <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace League\CommonMark\Extension\TableOfContents;
15
16
use League\CommonMark\Event\DocumentParsedEvent;
17
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
18
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalink;
19
use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
20
use League\CommonMark\Extension\TableOfContents\Node\TableOfContentsPlaceholder;
21
use League\CommonMark\Extension\TableOfContents\Node\TableOfContentsWrapper;
22
use League\CommonMark\Node\Block\Document;
23
use League\CommonMark\Node\NodeIterator;
24
use League\Config\ConfigurationAwareInterface;
25
use League\Config\ConfigurationInterface;
26
use League\Config\Exception\InvalidConfigurationException;
27
28
final class TableOfContentsBuilder implements ConfigurationAwareInterface
29
{
30
    public const POSITION_TOP             = 'top';
31
    public const POSITION_BEFORE_HEADINGS = 'before-headings';
32
    public const POSITION_PLACEHOLDER     = 'placeholder';
33
34
    /** @psalm-readonly-allow-private-mutation */
35
    private ConfigurationInterface $config;
36
37 70
    public function onDocumentParsed(DocumentParsedEvent $event): void
38
    {
39 70
        $document = $event->getDocument();
40
41 70
        $generator = new TableOfContentsGenerator(
42 70
            (string) $this->config->get('table_of_contents/style'),
43 70
            (string) $this->config->get('table_of_contents/normalize'),
44 70
            (int) $this->config->get('table_of_contents/min_heading_level'),
45 70
            (int) $this->config->get('table_of_contents/max_heading_level'),
46 70
            (string) $this->config->get('heading_permalink/fragment_prefix'),
47 70
            (string) $this->config->get('table_of_contents/label'),
48 70
        );
49
50 70
        $toc = $generator->generate($document);
51 70
        if ($toc === null) {
52
            // No linkable headers exist, so no TOC could be generated
53 8
            return;
54
        }
55
56
        // Add custom CSS class(es), if defined
57 62
        $class = $this->config->get('table_of_contents/html_class');
58 62
        if ($class !== null) {
59 62
            if ($toc instanceof TableOfContentsWrapper) {
60 4
                $toc->getInnerToc()->data->append('attributes/class', $class);
61
            } else {
62 58
                $toc->data->append('attributes/class', $class);
63
            }
64
        }
65
66
        // Add the TOC to the Document
67 62
        $position = $this->config->get('table_of_contents/position');
68 62
        if ($position === self::POSITION_TOP) {
69 50
            $document->prependChild($toc);
70 12
        } elseif ($position === self::POSITION_BEFORE_HEADINGS) {
71 4
            $this->insertBeforeFirstLinkedHeading($document, $toc);
0 ignored issues
show
Bug introduced by
It seems like $toc can also be of type League\CommonMark\Extens...\TableOfContentsWrapper; however, parameter $toc of League\CommonMark\Extens...oreFirstLinkedHeading() does only seem to accept League\CommonMark\Extens...ts\Node\TableOfContents, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
            $this->insertBeforeFirstLinkedHeading($document, /** @scrutinizer ignore-type */ $toc);
Loading history...
72 8
        } elseif ($position === self::POSITION_PLACEHOLDER) {
73 8
            $this->replacePlaceholders($document, $toc);
0 ignored issues
show
Bug introduced by
It seems like $toc can also be of type League\CommonMark\Extens...\TableOfContentsWrapper; however, parameter $toc of League\CommonMark\Extens...::replacePlaceholders() does only seem to accept League\CommonMark\Extens...ts\Node\TableOfContents, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

73
            $this->replacePlaceholders($document, /** @scrutinizer ignore-type */ $toc);
Loading history...
74
        } else {
75
            throw InvalidConfigurationException::forConfigOption('table_of_contents/position', $position);
76
        }
77
    }
78
79 4
    private function insertBeforeFirstLinkedHeading(Document $document, TableOfContents $toc): void
80
    {
81 4
        foreach ($document->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
82 4
            if (! $node instanceof Heading) {
83 4
                continue;
84
            }
85
86 4
            foreach ($node->children() as $child) {
87 4
                if ($child instanceof HeadingPermalink) {
88 4
                    $node->insertBefore($toc);
89
90 4
                    return;
91
                }
92
            }
93
        }
94
    }
95
96 8
    private function replacePlaceholders(Document $document, TableOfContents $toc): void
97
    {
98 8
        foreach ($document->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
99
            // Add the block once we find a placeholder
100 8
            if (! $node instanceof TableOfContentsPlaceholder) {
101 8
                continue;
102
            }
103
104 4
            $node->replaceWith(clone $toc);
105
        }
106
    }
107
108 70
    public function setConfiguration(ConfigurationInterface $configuration): void
109
    {
110 70
        $this->config = $configuration;
111
    }
112
}
113