ListParser   A
last analyzed

Coupling/Cohesion

Components 0
Dependencies 7

Complexity

Total Complexity 21

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 21
c 2
b 0
f 0
lcom 0
cbo 7
dl 0
loc 94
ccs 48
cts 48
cp 1
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
C parse() 0 55 15
B calculateListMarkerPadding() 0 23 6
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
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
15
namespace League\CommonMark\Block\Parser;
16
17
use League\CommonMark\Block\Element\ListBlock;
18
use League\CommonMark\Block\Element\ListData;
19
use League\CommonMark\Block\Element\ListItem;
20
use League\CommonMark\Block\Element\Paragraph;
21
use League\CommonMark\ContextInterface;
22
use League\CommonMark\Cursor;
23
use League\CommonMark\Util\RegexHelper;
24
25
class ListParser extends AbstractBlockParser
26
{
27
    /**
28
     * @param ContextInterface $context
29
     * @param Cursor           $cursor
30
     *
31
     * @return bool
32
     */
33 1716
    public function parse(ContextInterface $context, Cursor $cursor)
34
    {
35 1716
        if ($cursor->isIndented() && !($context->getContainer() instanceof ListBlock)) {
36 168
            return false;
37
        }
38
39 1653
        $tmpCursor = clone $cursor;
40 1653
        $tmpCursor->advanceToNextNonSpaceOrTab();
41 1653
        $rest = $tmpCursor->getRemainder();
42
43 1653
        $data = new ListData();
44 1653
        $data->markerOffset = $cursor->getIndent();
45
46 1653
        if ($matches = RegexHelper::matchAll('/^[*+-]/', $rest)) {
47 405
            $data->type = ListBlock::TYPE_UNORDERED;
48 405
            $data->delimiter = null;
49 405
            $data->bulletChar = $matches[0][0];
50 1653
        } elseif (($matches = RegexHelper::matchAll('/^(\d{1,9})([.)])/', $rest)) && (!($context->getContainer() instanceof Paragraph) || $matches[1] === '1')) {
51 84
            $data->type = ListBlock::TYPE_ORDERED;
52 84
            $data->start = intval($matches[1]);
53 84
            $data->delimiter = $matches[2];
54 84
            $data->bulletChar = null;
55 84
        } else {
56 1464
            return false;
57
        }
58
59 468
        $markerLength = strlen($matches[0]);
60
61
        // Make sure we have spaces after
62 468
        $nextChar = $tmpCursor->peek($markerLength);
63 468
        if (!($nextChar === null || $nextChar === "\t" || $nextChar === ' ')) {
64 228
            return false;
65
        }
66
67
        // If it interrupts paragraph, make sure first line isn't blank
68 243
        if ($context->getContainer() instanceof Paragraph && !RegexHelper::matchAt(RegexHelper::REGEX_NON_SPACE, $rest, $markerLength)) {
0 ignored issues
show
Bug Best Practice introduced by Colin O'Dell
The expression \League\CommonMark\Util\..., $rest, $markerLength) of type null|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
69 6
            return false;
70
        }
71
72
        // We've got a match! Advance offset and calculate padding
73 237
        $cursor->advanceToNextNonSpaceOrTab(); // to start of marker
74 237
        $cursor->advanceBy($markerLength, true); // to end of marker
75 237
        $data->padding = $this->calculateListMarkerPadding($cursor, $markerLength);
76
77
        // add the list if needed
78 237
        $container = $context->getContainer();
79 237
        if (!$container || !($context->getContainer() instanceof ListBlock) || !$data->equals($container->getListData())) {
0 ignored issues
show
Bug introduced by Colin O'Dell
The method getListData() does not seem to exist on object<League\CommonMark\Block\Element\Paragraph>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
80 237
            $context->addBlock(new ListBlock($data));
81 237
        }
82
83
        // add the list item
84 237
        $context->addBlock(new ListItem($data));
85
86 237
        return true;
87
    }
88
89
    /**
90
     * @param Cursor $cursor
91
     * @param int    $markerLength
92
     *
93
     * @return int
94
     */
95 237
    private function calculateListMarkerPadding(Cursor $cursor, $markerLength)
96
    {
97 237
        $start = $cursor->saveState();
98 237
        $spacesStartCol = $cursor->getColumn();
99
100 237
        while ($cursor->getColumn() - $spacesStartCol < 5) {
101 237
            if (!$cursor->advanceBySpaceOrTab()) {
102 228
                break;
103
            }
104 228
        }
105
106 237
        $blankItem = $cursor->peek() === null;
107 237
        $spacesAfterMarker = $cursor->getColumn() - $spacesStartCol;
108
109 237
        if ($spacesAfterMarker >= 5 || $spacesAfterMarker < 1 || $blankItem) {
110 72
            $cursor->restoreState($start);
111 72
            $cursor->advanceBySpaceOrTab();
112
113 72
            return $markerLength + 1;
114
        }
115
116 180
        return $markerLength + $spacesAfterMarker;
117
    }
118
}
119