|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Highlighter |
|
4
|
|
|
* |
|
5
|
|
|
* Copyright (C) 2016, Some right reserved. |
|
6
|
|
|
* |
|
7
|
|
|
* @author Kacper "Kadet" Donat <[email protected]> |
|
8
|
|
|
* |
|
9
|
|
|
* Contact with author: |
|
10
|
|
|
* Xmpp: [email protected] |
|
11
|
|
|
* E-mail: [email protected] |
|
12
|
|
|
* |
|
13
|
|
|
* From Kadet with love. |
|
14
|
|
|
*/ |
|
15
|
|
|
|
|
16
|
|
|
namespace Kadet\Highlighter\Language; |
|
17
|
|
|
|
|
18
|
|
|
use Kadet\Highlighter\Matcher\CommentMatcher; |
|
19
|
|
|
use Kadet\Highlighter\Matcher\RegexMatcher; |
|
20
|
|
|
use Kadet\Highlighter\Matcher\SubStringMatcher; |
|
21
|
|
|
use Kadet\Highlighter\Matcher\WordMatcher; |
|
22
|
|
|
use Kadet\Highlighter\Parser\CloseRule; |
|
23
|
|
|
use Kadet\Highlighter\Parser\OpenRule; |
|
24
|
|
|
use Kadet\Highlighter\Parser\Rule; |
|
25
|
|
|
use Kadet\Highlighter\Parser\Token\ContextualToken; |
|
26
|
|
|
use Kadet\Highlighter\Parser\Token\MetaToken; |
|
27
|
|
|
use Kadet\Highlighter\Parser\Token\Token; |
|
28
|
|
|
use Kadet\Highlighter\Parser\TokenFactory; |
|
29
|
|
|
use Kadet\Highlighter\Parser\Validator\Validator; |
|
30
|
|
|
|
|
31
|
|
|
class Css extends Language |
|
32
|
|
|
{ |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Tokenization rules |
|
36
|
|
|
* |
|
37
|
|
|
* @return \Kadet\Highlighter\Parser\Rule[]|\Kadet\Highlighter\Parser\Rule[][] |
|
38
|
|
|
*/ |
|
39
|
|
|
public function setupRules() |
|
40
|
|
|
{ |
|
41
|
|
|
$identifier = '-?[_a-zA-Z]+[_a-zA-Z0-9-]*'; |
|
42
|
|
|
$at = [ |
|
43
|
|
|
'charset', 'import', 'namespace', |
|
44
|
|
|
'media', 'supports', 'document', 'page', 'font-face', 'keyframes', 'viewport', 'counter-style', |
|
45
|
|
|
'font-feature-values', 'swash', 'ornaments', 'annotation', 'stylistic', 'styleset', 'character-variant' |
|
46
|
|
|
]; |
|
47
|
|
|
|
|
48
|
|
|
$this->rules->addMany([ |
|
49
|
|
|
'meta.declaration' => [ |
|
50
|
|
|
new OpenRule(new SubStringMatcher('{'), [ |
|
51
|
|
|
'context' => ['!meta.declaration.media', '!comment'], |
|
52
|
|
|
'factory' => new TokenFactory(MetaToken::class) |
|
53
|
|
|
]), |
|
54
|
|
|
new CloseRule(new SubStringMatcher('}')), |
|
55
|
|
|
], |
|
56
|
|
|
|
|
57
|
|
|
'meta.declaration.media' => [ |
|
58
|
|
|
new Rule(new RegexMatcher('/@media(.*?\{)/'), [ |
|
59
|
|
|
'context' => Validator::everywhere(), |
|
60
|
|
|
'factory' => new TokenFactory(MetaToken::class) |
|
61
|
|
|
]), |
|
62
|
|
|
], |
|
63
|
|
|
|
|
64
|
|
|
'meta.declaration.rule' => [ |
|
65
|
|
|
new OpenRule(new RegexMatcher('/@media.*(\()/'), [ |
|
66
|
|
|
'context' => ['meta.declaration.media'], |
|
67
|
|
|
'factory' => new TokenFactory(MetaToken::class) |
|
68
|
|
|
]), |
|
69
|
|
|
new CloseRule(new SubStringMatcher(')')), |
|
70
|
|
|
], |
|
71
|
|
|
|
|
72
|
|
|
'keyword.at-rule' => new Rule(new RegexMatcher('/(@(?:-[a-z]+-)?(?:'.implode('|', $at).'))/'), [ |
|
73
|
|
|
'priority' => 2 |
|
74
|
|
|
]), |
|
75
|
|
|
|
|
76
|
|
|
'string.single' => new Rule(new SubStringMatcher('\''), [ |
|
77
|
|
|
'context' => $this->everywhere(), |
|
78
|
|
|
'factory' => new TokenFactory(ContextualToken::class), |
|
79
|
|
|
]), |
|
80
|
|
|
|
|
81
|
|
|
'string.double' => new Rule(new SubStringMatcher('"'), [ |
|
82
|
|
|
'context' => $this->everywhere(), |
|
83
|
|
|
'factory' => new TokenFactory(ContextualToken::class), |
|
84
|
|
|
]), |
|
85
|
|
|
|
|
86
|
|
|
'symbol.selector.id' => new Rule(new RegexMatcher("/(#$identifier)/i")), |
|
87
|
|
|
'symbol.selector.tag' => new Rule(new RegexMatcher('/(?>[\s}]|^)(?=(\w+)[^;]*\{)/ms')), |
|
88
|
|
|
'symbol.selector.class' => new Rule(new RegexMatcher("/(\\.$identifier)/i")), |
|
89
|
|
|
|
|
90
|
|
|
'symbol.selector.class.pseudo' => new Rule(new RegexMatcher("/(:{1,2}$identifier)/")), |
|
91
|
|
|
|
|
92
|
|
|
'number' => new Rule(new RegexMatcher("/([-+]?[0-9]*\\.?[0-9]+([\\w%]+)?)/"), [ |
|
93
|
|
|
'context' => ['meta.declaration', '!constant.color', '!comment', '!symbol', '!comment', '!string'], |
|
94
|
|
|
'priority' => 0 |
|
95
|
|
|
]), |
|
96
|
|
|
|
|
97
|
|
|
'symbol.property' => new Rule(new RegexMatcher("/($identifier:)/"), [ |
|
98
|
|
|
'context' => ['meta.declaration', '!symbol', '!comment'], |
|
99
|
|
|
'priority' => 0 |
|
100
|
|
|
]), |
|
101
|
|
|
|
|
102
|
|
|
'call' => new Rule(new RegexMatcher("/($identifier)\\s*\\((?:(?P<string>[a-z].*?)|.*?)\\)/", [ |
|
103
|
|
|
1 => Token::NAME, |
|
104
|
|
|
'string' => 'string.argument' |
|
105
|
|
|
]), [ |
|
106
|
|
|
'context' => ['meta.declaration', '!comment', '!string', '!keyword'] |
|
107
|
|
|
]), |
|
108
|
|
|
|
|
109
|
|
|
'constant.color' => new Rule(new RegexMatcher("/(#[0-9a-f]{3,6})/i"), [ |
|
110
|
|
|
'priority' => 2, |
|
111
|
|
|
'context' => ['meta.declaration', '!symbol.color', '!comment'] |
|
112
|
|
|
]), |
|
113
|
|
|
|
|
114
|
|
|
'operator' => new Rule(new WordMatcher(['>', '+', '*', '!important'], ['separated' => false]), [ |
|
115
|
|
|
'context' => $this->everywhere() |
|
116
|
|
|
]), |
|
117
|
|
|
|
|
118
|
|
|
'operator.punctuation' => new Rule(new SubStringMatcher(';', ['separated' => false]), [ |
|
|
|
|
|
|
119
|
|
|
'context' => $this->everywhere() |
|
120
|
|
|
]), |
|
121
|
|
|
|
|
122
|
|
|
'comment' => new Rule(new CommentMatcher([], [['/*', '*/']]), ['context' => $this->everywhere()]) |
|
123
|
|
|
]); |
|
124
|
|
|
} |
|
125
|
|
|
|
|
126
|
|
|
/** |
|
127
|
|
|
* Unique language identifier, for example 'php' |
|
128
|
|
|
* |
|
129
|
|
|
* @return string |
|
130
|
|
|
*/ |
|
131
|
|
|
public function getIdentifier() |
|
132
|
|
|
{ |
|
133
|
|
|
return 'css'; |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
protected function everywhere() { |
|
137
|
|
|
static $validator; |
|
138
|
|
|
if (!$validator) { |
|
139
|
|
|
$validator = new Validator(['!string', '!comment']); |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
return $validator; |
|
143
|
|
|
} |
|
144
|
|
|
} |
|
145
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.