Passed
Pull Request — latest (#598)
by Colin
16:46 queued 13:48
created

mConfigurationIfProvided()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 6
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 10
c 1
b 0
f 1
cc 3
nc 3
nop 0
crap 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\HeadingPermalink;
15
16
use League\CommonMark\Configuration\ConfigurationAwareInterface;
17
use League\CommonMark\Configuration\ConfigurationInterface;
18
use League\CommonMark\Event\DocumentParsedEvent;
19
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
20
use League\CommonMark\Node\Block\Document;
21
use League\CommonMark\Node\StringContainerHelper;
22
use League\CommonMark\Normalizer\TextNormalizerInterface;
23
24
/**
25
 * Searches the Document for Heading elements and adds HeadingPermalinks to each one
26
 */
27
final class HeadingPermalinkProcessor implements ConfigurationAwareInterface
28
{
29
    public const INSERT_BEFORE = 'before';
30
    public const INSERT_AFTER  = 'after';
31
32
    /**
33
     * @var TextNormalizerInterface
34
     *
35
     * @psalm-readonly-allow-private-mutation
36
     */
37
    private $slugNormalizer;
38
39
    /**
40
     * @var ConfigurationInterface
41
     *
42
     * @psalm-readonly-allow-private-mutation
43
     */
44
    private $config;
45
46 84
    public function setConfiguration(ConfigurationInterface $configuration): void
47
    {
48 84
        $this->config = $configuration;
49 84
    }
50
51 84
    public function __invoke(DocumentParsedEvent $e): void
52
    {
53 84
        $this->slugNormalizer = $this->config->get('heading_permalink/slug_normalizer');
54
55 81
        $min = (int) $this->config->get('heading_permalink/min_heading_level');
56 81
        $max = (int) $this->config->get('heading_permalink/max_heading_level');
57
58 81
        $e->getDocument()->data->set('heading_ids', []);
59
60 81
        $walker = $e->getDocument()->walker();
61
62 81
        while ($event = $walker->next()) {
63 81
            $node = $event->getNode();
64 81
            if ($node instanceof Heading && $event->isEntering() && $node->getLevel() >= $min && $node->getLevel() <= $max) {
65 78
                $this->addHeadingLink($node, $e->getDocument());
66
            }
67
        }
68 81
    }
69
70 78
    private function addHeadingLink(Heading $heading, Document $document): void
71
    {
72 78
        $text = StringContainerHelper::getChildText($heading);
73 78
        $slug = $this->slugNormalizer->normalize($text, $heading);
74
75 78
        $slug = $this->ensureUnique($slug, $document);
76
77 78
        $headingLinkAnchor = new HeadingPermalink($slug);
78
79 78
        switch ($this->config->get('heading_permalink/insert')) {
80 78
            case self::INSERT_BEFORE:
81 69
                $heading->prependChild($headingLinkAnchor);
82
83 69
                return;
84 9
            case self::INSERT_AFTER:
85 9
                $heading->appendChild($headingLinkAnchor);
86
87 9
                return;
88
            default:
89
                throw new \RuntimeException("Invalid configuration value for heading_permalink/insert; expected 'before' or 'after'");
90
        }
91
    }
92
93 78
    private function ensureUnique(string $proposed, Document $document): string
94
    {
95 78
        $usedIds = $document->data->get('heading_ids');
96
97
        // Quick path, it's a unique ID
98 78
        if (! isset($usedIds[$proposed])) {
99 78
            $usedIds[$proposed] = true;
100 78
            $document->data->set('heading_ids', $usedIds);
101
102 78
            return $proposed;
103
        }
104
105 9
        $extension = 0;
106
        do {
107 9
            ++$extension;
108 9
            $id = \sprintf('%s-%s', $proposed, $extension);
109 9
        } while (isset($usedIds[$id]));
110
111 9
        $usedIds[$id] = true;
112 9
        $document->data->set('heading_ids', $usedIds);
113
114 9
        return $id;
115
    }
116
}
117