AttributesListener::processDocument()   B
last analyzed

Complexity

Conditions 11
Paths 7

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 11

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 16
dl 0
loc 27
ccs 16
cts 16
cp 1
rs 7.3166
c 1
b 0
f 0
cc 11
nc 7
nop 1
crap 11

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Block\AbstractBlock;
25
use League\CommonMark\Node\Inline\AbstractInline;
26
use League\CommonMark\Node\Node;
27
28
final class AttributesListener
29
{
30
    private const DIRECTION_PREFIX = 'prefix';
31
    private const DIRECTION_SUFFIX = 'suffix';
32
33 18
    public function processDocument(DocumentParsedEvent $event): void
34
    {
35 18
        $walker = $event->getDocument()->walker();
36 18
        while ($event = $walker->next()) {
37 18
            $node = $event->getNode();
38 18
            if (! $node instanceof AttributesInline && ($event->isEntering() || ! $node instanceof Attributes)) {
39 18
                continue;
40
            }
41
42 18
            [$target, $direction] = self::findTargetAndDirection($node);
43
44 18
            if ($target instanceof AbstractBlock || $target instanceof AbstractInline) {
45 18
                $parent = $target->parent();
46 18
                if ($parent instanceof ListItem && $parent->parent() instanceof ListBlock && $parent->parent()->isTight()) {
47 3
                    $target = $parent;
48
                }
49
50 18
                if ($direction === self::DIRECTION_SUFFIX) {
51 15
                    $attributes = AttributesHelper::mergeAttributes($target, $node->getAttributes());
52
                } else {
53 9
                    $attributes = AttributesHelper::mergeAttributes($node->getAttributes(), $target);
54
                }
55
56 18
                $target->data['attributes'] = $attributes;
57
            }
58
59 18
            $node->detach();
60
        }
61 18
    }
62
63
    /**
64
     * @param Attributes|AttributesInline $node
65
     *
66
     * @return array<Node|string|null>
67
     */
68 18
    private static function findTargetAndDirection($node): array
69
    {
70 18
        $target    = null;
71 18
        $direction = null;
72 18
        $previous  = $next = $node;
73 18
        while (true) {
74 18
            $previous = self::getPrevious($previous);
75 18
            $next     = self::getNext($next);
76
77 18
            if ($previous === null && $next === null) {
78 12
                if (! $node->parent() instanceof FencedCode) {
79 12
                    $target    = $node->parent();
80 12
                    $direction = self::DIRECTION_SUFFIX;
81
                }
82
83 12
                break;
84
            }
85
86 18
            if ($node instanceof AttributesInline && ($previous === null || ($previous instanceof AbstractInline && $node->isBlock()))) {
87 12
                continue;
88
            }
89
90 18
            if ($previous !== null && ! self::isAttributesNode($previous)) {
91 15
                $target    = $previous;
92 15
                $direction = self::DIRECTION_SUFFIX;
93
94 15
                break;
95
            }
96
97 9
            if ($next !== null && ! self::isAttributesNode($next)) {
98 9
                $target    = $next;
99 9
                $direction = self::DIRECTION_PREFIX;
100
101 9
                break;
102
            }
103
        }
104
105 18
        return [$target, $direction];
106
    }
107
108
    /**
109
     * Get any previous block (sibling or parent) this might apply to
110
     */
111 18
    private static function getPrevious(?Node $node = null): ?Node
112
    {
113 18
        if ($node instanceof Attributes) {
114 15
            if ($node->getTarget() === Attributes::TARGET_NEXT) {
115 9
                return null;
116
            }
117
118 12
            if ($node->getTarget() === Attributes::TARGET_PARENT) {
119 3
                return $node->parent();
120
            }
121
        }
122
123 15
        return $node instanceof Node ? $node->previous() : null;
124
    }
125
126
    /**
127
     * Get any previous block (sibling or parent) this might apply to
128
     */
129 18
    private static function getNext(?Node $node = null): ?Node
130
    {
131 18
        if ($node instanceof Attributes && $node->getTarget() !== Attributes::TARGET_NEXT) {
132 12
            return null;
133
        }
134
135 18
        return $node instanceof Node ? $node->next() : null;
136
    }
137
138 18
    private static function isAttributesNode(Node $node): bool
139
    {
140 18
        return $node instanceof Attributes || $node instanceof AttributesInline;
141
    }
142
}
143