Passed
Push — master ( 1d0ea2...0f9d68 )
by Kacper
05:34
created

Markdown   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Test Coverage

Coverage 25%

Importance

Changes 6
Bugs 2 Features 0
Metric Value
eloc 56
c 6
b 2
f 0
dl 0
loc 106
ccs 12
cts 48
cp 0.25
rs 10
wmc 3

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getMetadata() 0 6 1
A getIdentifier() 0 3 1
A setupRules() 0 77 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Highlighter
7
 *
8
 * Copyright (C) 2016, Some right reserved.
9
 *
10
 * @author Kacper "Kadet" Donat <[email protected]>
11
 *
12
 * Contact with author:
13
 * Xmpp: [email protected]
14
 * E-mail: [email protected]
15
 *
16
 * From Kadet with love.
17
 */
18
19
namespace Kadet\Highlighter\Language;
20
21
use Kadet\Highlighter\KeyLighter;
22
use Kadet\Highlighter\Matcher\DelegateRegexMatcher;
23
use Kadet\Highlighter\Matcher\RegexMatcher;
24
use Kadet\Highlighter\Parser\Rule;
25
use Kadet\Highlighter\Parser\Token\LanguageToken;
26
use Kadet\Highlighter\Parser\Token\Token;
27
use Kadet\Highlighter\Parser\TokenFactoryInterface;
28
use Kadet\Highlighter\Parser\Validator\Validator;
29
30
class Markdown extends Html
31
{
32
    protected $_options = [
33
        'variables' => false,
34
    ];
35
36
    /**
37
     * Tokenization rules setup
38
     */
39 2
    public function setupRules()
40
    {
41
        parent::setupRules();
42
43
        $this->rules->validator = new Validator(
44
            ['!format.block.code', '!format.monospace', '!operator.escape', '!operator', '!tag']
45
        );
46
47
        $this->rules->addMany([
48
            'format.header' => [
49
                new Rule(new RegexMatcher('/^\s{0,3}(#+.+?)\r?$/m')),
50
                new Rule(new RegexMatcher('/^([^\r\n]+?)^(?:-+|=+)\R?$/m'))
51
            ],
52
            'format.bold'  => new Rule(
53
                new RegexMatcher('/(?P<bold>(?P<b>\*\*|__)(?>[^*_\r\n]|(?:(?P<i>[*_]{2})(?>[^*_\r\n]|(?&bold))*?\g{i}))+\g{b})/', [
54
                    'bold' => Token::NAME
55
                ])
56
            ),
57
            'format.italics'   => new Rule(
58
                new RegexMatcher('/(?:^|[^*_])(?P<italics>(?P<i>[*_])(?>[^*_\r\n]|(?:(?P<b>[*_]{2})(?>[^*_\r\n]|(?&italics))*?\g{b}))+\g{i})/', [
59
                    'italics' => Token::NAME
60
                ])
61
            ),
62
            'format.strike'    => new Rule(new RegexMatcher('/(~~.+?~~)/')),
63
            'format.monospace' => [
64
                new Rule(new RegexMatcher('/(?:[^`]|^)(`.*?`)/')),
65
                new Rule(new RegexMatcher('/(``.*?``)/')),
66
                new Rule(new RegexMatcher('/^((?:(?: {4,}|\t).*?(?>\R|$)+?)+)/m')),
67
            ],
68
69
            'operator.list.ordered'   => new Rule(new RegexMatcher('/^\s*(\d+[.)])/m')),
70
            'operator.list.unordered' => new Rule(new RegexMatcher('/^\s*([-+*])\s/m'), [
71
                'context' => ['none'],
72
                'priority' => 2
73
            ]),
74
75
            'string.quote'       => new Rule(new RegexMatcher('/((?:^>.*?\R)+)/m')),
76
            'format.block.code'  => [
77
                new Rule(
78
                    new DelegateRegexMatcher(
79
                        '/^```(.*?)\R(.*?)\R^```/ms',
80 2
                        function ($match, TokenFactoryInterface $factory) {
81 2
                            $lang = KeyLighter::get()->getLanguage($match[1][0]);
82 2
                            yield $factory->create(Token::NAME, ['pos' => $match[0][1], 'length' => strlen($match[0][0])]);
83 2
                            yield $factory->create(
84 2
                                "language.{$lang->getIdentifier()}",
85
                                [
86 2
                                    'pos'    => $match[2][1],
87 2
                                    'length' => strlen($match[2][0]),
88 2
                                    'inject' => $lang,
89
                                    'class'  => LanguageToken::class,
90
                                ]
91
                            );
92 2
                        }
93
                    ),
94
                    [
95
                        'context'     => Validator::everywhere(),
96
                        'postProcess' => true,
97
                        'priority'    => 1000
98
                    ]
99
                ),
100
            ],
101
102
            'operator.escape' => new Rule(new RegexMatcher('/(\\\.)/')),
103
104
            'operator.horizontal' => new Rule(new RegexMatcher('/^\s{,3}(([-*_])( ?\2)+)\R/m'), [
105
                'priority' => 2
106
            ]),
107
108
            'symbol.link'  => new Rule(new RegexMatcher('/[^!](\[(?:[^\[\]]|!\[.*?\]\(.*?\))*\])/i')),
109
            'symbol.url'   => new Rule(new RegexMatcher('#(<(https?://|[^/])[-A-Za-z0-9+&@\#/%?=~_|!:,.;]+[-A-Za-z0-9+&@\#/%=~_|]>|https?://[-A-Za-z0-9+&@\#/%?=~_|!:,.;]+[-A-Za-z0-9+&@\#/%=~_|])#i'), [
110
                'priority' => 0,
111
            ]),
112
113
            'symbol.image' => new Rule(new RegexMatcher('/(!)(\[.*?\])\(.*?\)/i', [
114
                1 => 'operator.image',
115
                2 => '$.title',
116
            ])),
117
        ]);
118
    }
119
120
    /**
121
     * Unique language identifier, for example 'php'
122
     *
123
     * @return string
124
     */
125 3
    public function getIdentifier()
126
    {
127 3
        return 'markdown';
128
    }
129
130
    public static function getMetadata()
131
    {
132
        return [
133
            'name'      => ['markdown', 'md'],
134
            'mime'      => ['text/markdown'],
135
            'extension' => ['*.markdown', '*.md']
136
        ];
137
    }
138
}
139