Completed
Push — 1.5 ( 4771f4...6f6b16 )
by Colin
02:30
created

MentionParser::parse()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 17
cts 17
cp 1
rs 9.0008
c 0
b 0
f 0
cc 5
nc 4
nop 1
crap 5
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace League\CommonMark\Extension\Mention;
13
14
use League\CommonMark\Extension\Mention\LinkGenerator\CallbackLinkGenerator;
15
use League\CommonMark\Extension\Mention\LinkGenerator\MentionLinkGeneratorInterface;
16
use League\CommonMark\Extension\Mention\LinkGenerator\StringTemplateLinkGenerator;
17
use League\CommonMark\Inline\Parser\InlineParserInterface;
18
use League\CommonMark\InlineParserContext;
19
20
final class MentionParser implements InlineParserInterface
21
{
22
    /** @var string */
23
    private $symbol;
24
25
    /** @var string */
26
    private $mentionRegex;
27
28
    /** @var MentionLinkGeneratorInterface */
29
    private $linkGenerator;
30
31 27
    public function __construct(string $symbol, string $mentionRegex, MentionLinkGeneratorInterface $linkGenerator)
32
    {
33 27
        $this->symbol = $symbol;
34 27
        $this->mentionRegex = $mentionRegex;
35 27
        $this->linkGenerator = $linkGenerator;
36 27
    }
37
38 27
    public function getCharacters(): array
39
    {
40 27
        return [$this->symbol];
41
    }
42
43 24
    public function parse(InlineParserContext $inlineContext): bool
44
    {
45 24
        $cursor = $inlineContext->getCursor();
46
47
        // The symbol must not have any other characters immediately prior
48 24
        $previousChar = $cursor->peek(-1);
49 24
        if ($previousChar !== null && $previousChar !== ' ') {
50
            // peek() doesn't modify the cursor, so no need to restore state first
51 6
            return false;
52
        }
53
54
        // Save the cursor state in case we need to rewind and bail
55 21
        $previousState = $cursor->saveState();
56
57
        // Advance past the symbol to keep parsing simpler
58 21
        $cursor->advance();
59
60
        // Parse the handle
61 21
        $handle = $cursor->match($this->mentionRegex);
62 21
        if (empty($handle)) {
63
            // Regex failed to match; this isn't a valid mention
64 6
            $cursor->restoreState($previousState);
65
66 6
            return false;
67
        }
68
69 18
        $link = $this->linkGenerator->generateLink($this->symbol, $handle);
70
71 18
        if ($link === null) {
72 3
            $cursor->restoreState($previousState);
73
74 3
            return false;
75
        }
76
77 15
        $inlineContext->getContainer()->appendChild($link);
78
79 15
        return true;
80
    }
81
82 9
    public static function createWithStringTemplate(string $symbol, string $mentionRegex, string $urlTemplate): MentionParser
83
    {
84 9
        return new self($symbol, $mentionRegex, new StringTemplateLinkGenerator($urlTemplate));
85
    }
86
87 3
    public static function createWithCallback(string $symbol, string $mentionRegex, callable $callback): MentionParser
88
    {
89 3
        return new self($symbol, $mentionRegex, new CallbackLinkGenerator($callback));
90
    }
91
92 3
    public static function createTwitterHandleParser(): MentionParser
93
    {
94 3
        return self::createWithStringTemplate('@', '/^[A-Za-z0-9_]{1,15}(?!\w)/', 'https://twitter.com/%s');
95
    }
96
97 3
    public static function createGitHubHandleParser(): MentionParser
98
    {
99
        // RegEx adapted from https://github.com/shinnn/github-username-regex/blob/master/index.js
100 3
        return self::createWithStringTemplate('@', '/^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}(?!\w)/', 'https://github.com/%s');
101
    }
102
103 3
    public static function createGitHubIssueParser(string $project): MentionParser
104
    {
105 3
        return self::createWithStringTemplate('#', '/^\d+/', "https://github.com/$project/issues/%d");
106
    }
107
}
108