Completed
Push — master ( 4aabce...15a80b )
by Colin
04:43 queued 14s
created

EmphasisParser   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 94.12%

Importance

Changes 8
Bugs 0 Features 1
Metric Value
wmc 27
c 8
b 0
f 1
lcom 1
cbo 9
dl 0
loc 123
ccs 64
cts 68
cp 0.9412
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setEnvironment() 0 4 1
A __construct() 0 10 1
B getCharacters() 0 16 5
C parse() 0 49 8
C determineCanOpenOrClose() 0 20 12
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 (http://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\Inline\Parser;
16
17
use League\CommonMark\Delimiter\Delimiter;
18
use League\CommonMark\Environment;
19
use League\CommonMark\EnvironmentAwareInterface;
20
use League\CommonMark\Inline\Element\Text;
21
use League\CommonMark\InlineParserContext;
22
use League\CommonMark\Util\Configuration;
23
use League\CommonMark\Util\RegexHelper;
24
25
class EmphasisParser extends AbstractInlineParser implements EnvironmentAwareInterface
26
{
27
    protected $config;
28
29 1854
    public function __construct(array $newConfig = [])
30
    {
31 1854
        $this->config = new Configuration([
32 1854
            'use_asterisk'    => true,
33 1854
            'use_underscore'  => true,
34 1854
            'enable_em'       => true,
35 1854
            'enable_strong'   => true,
36 1854
        ]);
37 1854
        $this->config->mergeConfig($newConfig);
38 1854
    }
39
40 1854
    public function setEnvironment(Environment $environment)
41
    {
42 1854
        $this->config->mergeConfig($environment->getConfig());
43 1854
    }
44
45
    /**
46
     * @return string[]
47
     */
48 1854
    public function getCharacters()
49
    {
50 1854
        if (!$this->config->getConfig('enable_em') && !$this->config->getConfig('enable_strong')) {
51
            return [];
52
        }
53
54 1854
        $chars = [];
55 1854
        if ($this->config->getConfig('use_asterisk')) {
56 1854
            $chars[] = '*';
57 1854
        }
58 1854
        if ($this->config->getConfig('use_underscore')) {
59 1854
            $chars[] = '_';
60 1854
        }
61
62 1854
        return $chars;
63
    }
64
65
    /**
66
     * @param InlineParserContext $inlineContext
67
     *
68
     * @return bool
69
     */
70 507
    public function parse(InlineParserContext $inlineContext)
71
    {
72 507
        $character = $inlineContext->getCursor()->getCharacter();
73 507
        if (!in_array($character, $this->getCharacters())) {
74
            return false;
75
        }
76
77 507
        $numDelims = 0;
78
79 507
        $cursor = $inlineContext->getCursor();
80 507
        $charBefore = $cursor->peek(-1);
81 507
        if ($charBefore === null) {
82 345
            $charBefore = "\n";
83 345
        }
84
85 507
        while ($cursor->peek($numDelims) === $character) {
86 507
            ++$numDelims;
87 507
        }
88
89 507
        if ($numDelims === 0) {
90
            return false;
91
        }
92
93
        // Skip single delims if emphasis is disabled
94 507
        if ($numDelims === 1 && !$this->config->getConfig('enable_em')) {
95
            return false;
96
        }
97
98 507
        $cursor->advanceBy($numDelims);
99
100 507
        $charAfter = $cursor->getCharacter();
101 507
        if ($charAfter === null) {
102 342
            $charAfter = "\n";
103 342
        }
104
105 507
        list($canOpen, $canClose) = $this->determineCanOpenOrClose($charBefore, $charAfter, $character);
106
107 507
        $node = new Text($cursor->getPreviousText(), [
108 507
            'delim'           => true,
109 507
            'emphasis_config' => $this->config,
110 507
        ]);
111 507
        $inlineContext->getContainer()->appendChild($node);
112
113
        // Add entry to stack to this opener
114 507
        $delimiter = new Delimiter($character, $numDelims, $node, $canOpen, $canClose);
115 507
        $inlineContext->getDelimiterStack()->push($delimiter);
116
117 507
        return true;
118
    }
119
120
    /**
121
     * @param string $charBefore
122
     * @param string $charAfter
123
     * @param string $character
124
     *
125
     * @return bool[]
126
     */
127 507
    private function determineCanOpenOrClose($charBefore, $charAfter, $character)
128
    {
129 507
        $afterIsWhitespace = preg_match('/\pZ|\s/u', $charAfter);
130 507
        $afterIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter);
131 507
        $beforeIsWhitespace = preg_match('/\pZ|\s/u', $charBefore);
132 507
        $beforeIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore);
133
134 507
        $leftFlanking = !$afterIsWhitespace && !($afterIsPunctuation && !$beforeIsWhitespace && !$beforeIsPunctuation);
135 507
        $rightFlanking = !$beforeIsWhitespace && !($beforeIsPunctuation && !$afterIsWhitespace && !$afterIsPunctuation);
136
137 507
        if ($character === '_') {
138 204
            $canOpen = $leftFlanking && (!$rightFlanking || $beforeIsPunctuation);
139 204
            $canClose = $rightFlanking && (!$leftFlanking || $afterIsPunctuation);
140 204
        } else {
141 333
            $canOpen = $leftFlanking;
142 333
            $canClose = $rightFlanking;
143
        }
144
145 507
        return [$canOpen, $canClose];
146
    }
147
}
148