AttributesListener::isAttributesNode()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 1
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 * (c) 2015 Martin Hasoň <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
declare(strict_types=1);
14
15
namespace League\CommonMark\Extension\Attributes\Event;
16
17
use League\CommonMark\Event\DocumentParsedEvent;
18
use League\CommonMark\Extension\Attributes\Node\Attributes;
19
use League\CommonMark\Extension\Attributes\Node\AttributesInline;
20
use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
21
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
22
use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
23
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
24
use League\CommonMark\Node\Inline\AbstractInline;
25
use League\CommonMark\Node\Node;
26
27
final class AttributesListener
28
{
29
    private const DIRECTION_PREFIX = 'prefix';
30
    private const DIRECTION_SUFFIX = 'suffix';
31
32 18
    public function processDocument(DocumentParsedEvent $event): void
33
    {
34 18
        $walker = $event->getDocument()->walker();
35 18
        while ($event = $walker->next()) {
36 18
            $node = $event->getNode();
37 18
            if (! $node instanceof AttributesInline && ($event->isEntering() || ! $node instanceof Attributes)) {
38 18
                continue;
39
            }
40
41 18
            [$target, $direction] = self::findTargetAndDirection($node);
42
43 18
            if ($target instanceof Node) {
44 18
                $parent = $target->parent();
45 18
                if ($parent instanceof ListItem && $parent->parent() instanceof ListBlock && $parent->parent()->isTight()) {
46 3
                    $target = $parent;
47
                }
48
49 18
                if ($direction === self::DIRECTION_SUFFIX) {
50 15
                    $attributes = AttributesHelper::mergeAttributes($target, $node->getAttributes());
51
                } else {
52 9
                    $attributes = AttributesHelper::mergeAttributes($node->getAttributes(), $target);
53
                }
54
55 18
                $target->data->set('attributes', $attributes);
56
            }
57
58 18
            $node->detach();
59
        }
60 18
    }
61
62
    /**
63
     * @param Attributes|AttributesInline $node
64
     *
65
     * @return array<Node|string|null>
66
     */
67 18
    private static function findTargetAndDirection($node): array
68
    {
69 18
        $target    = null;
70 18
        $direction = null;
71 18
        $previous  = $next = $node;
72 18
        while (true) {
73 18
            $previous = self::getPrevious($previous);
74 18
            $next     = self::getNext($next);
75
76 18
            if ($previous === null && $next === null) {
77 12
                if (! $node->parent() instanceof FencedCode) {
78 12
                    $target    = $node->parent();
79 12
                    $direction = self::DIRECTION_SUFFIX;
80
                }
81
82 12
                break;
83
            }
84
85 18
            if ($node instanceof AttributesInline && ($previous === null || ($previous instanceof AbstractInline && $node->isBlock()))) {
86 12
                continue;
87
            }
88
89 18
            if ($previous !== null && ! self::isAttributesNode($previous)) {
90 15
                $target    = $previous;
91 15
                $direction = self::DIRECTION_SUFFIX;
92
93 15
                break;
94
            }
95
96 9
            if ($next !== null && ! self::isAttributesNode($next)) {
97 9
                $target    = $next;
98 9
                $direction = self::DIRECTION_PREFIX;
99
100 9
                break;
101
            }
102
        }
103
104 18
        return [$target, $direction];
105
    }
106
107
    /**
108
     * Get any previous block (sibling or parent) this might apply to
109
     */
110 18
    private static function getPrevious(?Node $node = null): ?Node
111
    {
112 18
        if ($node instanceof Attributes) {
113 15
            if ($node->getTarget() === Attributes::TARGET_NEXT) {
114 9
                return null;
115
            }
116
117 12
            if ($node->getTarget() === Attributes::TARGET_PARENT) {
118 3
                return $node->parent();
119
            }
120
        }
121
122 15
        return $node instanceof Node ? $node->previous() : null;
123
    }
124
125
    /**
126
     * Get any previous block (sibling or parent) this might apply to
127
     */
128 18
    private static function getNext(?Node $node = null): ?Node
129
    {
130 18
        if ($node instanceof Attributes && $node->getTarget() !== Attributes::TARGET_NEXT) {
131 12
            return null;
132
        }
133
134 18
        return $node instanceof Node ? $node->next() : null;
135
    }
136
137 18
    private static function isAttributesNode(Node $node): bool
138
    {
139 18
        return $node instanceof Attributes || $node instanceof AttributesInline;
140
    }
141
}
142