Skip to content

Commit e828d69

Browse files
authored
enh(php) Left and right-side of double colon (#3422)
- declare and use `IDENT_RE` for valid labels instead of `w+` - declaration of PHP [constants](https://www.php.net/manual/en/language.oop5.constants.php) were not highlighted. Now they use the same class as variables. - enum and constant reference were not highlighted. Now they use the same class as variables. - Class name references are highlighted as `variable.language`. [class](https://wiki.php.net/rfc/class_name_literal_on_object) is a special case of the constant, it's also a reserved keyword. - left-side class name highlighting
1 parent 0e3735a commit e828d69

File tree

5 files changed

+132
-18
lines changed

5 files changed

+132
-18
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ These changes should be for the better and should not be super noticeable but if
1515

1616
Grammars:
1717

18+
- enh(php) Left and right-side of double colon [Wojciech Kania][]
19+
- enh(php) add PHP constants [Wojciech Kania][]
1820
- enh(php) add PHP 8.1 keywords [Wojciech Kania][]
1921
- fix(cpp) fix `vector<<` template false positive (#3437) [Josh Goebel][]
2022
- enh(php) support First-class Callable Syntax (#3427) [Wojciech Kania][]

src/languages/php.js

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ Category: common
1212
* */
1313
export default function(hljs) {
1414
const regex = hljs.regex;
15+
const IDENT_RE_CORE = '[a-zA-Z0-9_\x7f-\xff]*' +
16+
// negative look-ahead tries to avoid matching patterns that are not
17+
// Perl at all like $ident$, @ident@, etc.
18+
'(?![A-Za-z0-9])(?![$]))';
19+
const IDENT_RE = regex.concat("([a-zA-Z_\\x7f-\\xff]", IDENT_RE_CORE);
20+
// Will not detect camelCase classes
21+
const PASCAL_CASE_CLASS_NAME_RE = regex.concat("([A-Z]", IDENT_RE_CORE);
1522
const VARIABLE = {
1623
scope: 'variable',
17-
match: '\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' +
18-
// negative look-ahead tries to avoid matching patterns that are not
19-
// Perl at all like $ident$, @ident@, etc.
20-
`(?![A-Za-z0-9])(?![$])`
24+
match: '\\$+' + IDENT_RE,
2125
};
2226
const PREPROCESSOR = {
2327
scope: 'meta',
@@ -46,7 +50,7 @@ export default function(hljs) {
4650
end: /[ \t]*(\w+)\b/,
4751
contains: hljs.QUOTE_STRING_MODE.contains.concat(SUBST),
4852
});
49-
// list of valid whitespaces because non-breaking space might be part of a name
53+
// list of valid whitespaces because non-breaking space might be part of a IDENT_RE
5054
const WHITESPACE = '[ \t\n]';
5155
const STRING = {
5256
scope: 'string',
@@ -313,8 +317,8 @@ export default function(hljs) {
313317
regex.concat(WHITESPACE, "+"),
314318
// to prevent built ins from being confused as the class constructor call
315319
regex.concat("(?!", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"),
316-
/\\?\w+/,
317-
regex.concat(WHITESPACE, "*\\("),
320+
regex.concat(/\\?/, IDENT_RE),
321+
regex.concat(WHITESPACE, "*", /\(/),
318322
],
319323
scope: {
320324
1: "keyword",
@@ -330,7 +334,7 @@ export default function(hljs) {
330334
/\b/,
331335
// to prevent keywords from being confused as the function title
332336
regex.concat("(?!fn\\b|function\\b|", normalizeKeywords(KWS).join("\\b|"), "|", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"),
333-
/\w+/,
337+
IDENT_RE,
334338
regex.concat(WHITESPACE, "*"),
335339
regex.lookahead(/(?=\()/)
336340
],
@@ -339,6 +343,57 @@ export default function(hljs) {
339343
}
340344
};
341345

346+
const CONSTANT_REFERENCE = regex.concat(IDENT_RE, "\\b(?!\\()");
347+
348+
const LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON = {
349+
variants: [
350+
{
351+
match: [
352+
regex.concat(
353+
/::/,
354+
regex.lookahead(/(?!class\b)/)
355+
),
356+
CONSTANT_REFERENCE,
357+
],
358+
scope: {
359+
2: "variable.constant",
360+
},
361+
},
362+
{
363+
match: [
364+
/::/,
365+
/class/,
366+
],
367+
scope: {
368+
2: "variable.language",
369+
},
370+
},
371+
{
372+
match: [
373+
PASCAL_CASE_CLASS_NAME_RE,
374+
regex.concat(
375+
"::",
376+
regex.lookahead(/(?!class\b)/)
377+
),
378+
],
379+
scope: {
380+
1: "title.class",
381+
},
382+
},
383+
{
384+
match: [
385+
PASCAL_CASE_CLASS_NAME_RE,
386+
/::/,
387+
/class/,
388+
],
389+
scope: {
390+
1: "title.class",
391+
3: "variable.language",
392+
},
393+
}
394+
]
395+
};
396+
342397
return {
343398
case_insensitive: false,
344399
keywords: KEYWORDS,
@@ -351,8 +406,8 @@ export default function(hljs) {
351406
{
352407
contains: [
353408
{
354-
className: 'doctag',
355-
begin: '@[A-Za-z]+'
409+
scope: 'doctag',
410+
match: '@[A-Za-z]+'
356411
}
357412
]
358413
}
@@ -379,14 +434,18 @@ export default function(hljs) {
379434
},
380435
VARIABLE,
381436
FUNCTION_INVOKE,
437+
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON,
382438
{
383-
// swallow composed identifiers to avoid parsing them as keywords
384-
match: regex.concat(
385-
/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,
386-
regex.concat("(?!", WHITESPACE, "*\\()"),
387-
/(?![a-zA-Z0-9_\x7f-\xff])/
388-
),
389-
// scope:"wrong"
439+
match: [
440+
/const/,
441+
/\s/,
442+
IDENT_RE,
443+
/\s*=/,
444+
],
445+
scope: {
446+
1: "keyword",
447+
3: "variable.constant",
448+
},
390449
},
391450
CONSTRUCTOR_CALL,
392451
{
@@ -412,6 +471,7 @@ export default function(hljs) {
412471
contains: [
413472
'self',
414473
VARIABLE,
474+
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON,
415475
hljs.C_BLOCK_COMMENT_MODE,
416476
STRING,
417477
NUMBER

test/markup/php/functions.expect.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<span class="hljs-variable">$date</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DateTimeImmutable</span> ();
1414
<span class="hljs-variable">$date</span>-&gt;<span class="hljs-title function_ invoke__">format</span>(<span class="hljs-string">&#x27;Y-m-d&#x27;</span>);
1515

16-
DateTimeImmutable::<span class="hljs-title function_ invoke__">createFromMutable</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">\DateTime</span>(<span class="hljs-string">&#x27;now&#x27;</span>));
16+
<span class="hljs-title class_">DateTimeImmutable</span>::<span class="hljs-title function_ invoke__">createFromMutable</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">\DateTime</span>(<span class="hljs-string">&#x27;now&#x27;</span>));
1717

1818
<span class="hljs-title function_ invoke__">str_contains</span> (\<span class="hljs-title function_ invoke__">strtoupper</span>(<span class="hljs-title function_ invoke__">substr</span>(<span class="hljs-string">&#x27;abcdef&#x27;</span>, -<span class="hljs-number">2</span>), <span class="hljs-string">&#x27;f&#x27;</span>));
1919

test/markup/php/titles.expect.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Foo</span> </span>{
2+
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">FOO</span>=<span class="hljs-string">&#x27;foo&#x27;</span>;
3+
4+
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">
5+
<span class="hljs-keyword">public</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> <span class="hljs-variable">$name</span> = <span class="hljs-built_in">self</span>::<span class="hljs-variable constant_">FOO</span>
6+
</span>) </span>{}
7+
8+
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getClass</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
9+
<span class="hljs-keyword">return</span> <span class="hljs-title class_">DateTimeImmutable</span>::<span class="hljs-variable language_">class</span>;
10+
}
11+
12+
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getClassFromSelf</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
13+
<span class="hljs-keyword">return</span> <span class="hljs-built_in">self</span>::<span class="hljs-variable language_">class</span>;
14+
}
15+
16+
<span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getClassFromStatic</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
17+
<span class="hljs-keyword">return</span> <span class="hljs-built_in">static</span>::<span class="hljs-variable language_">class</span>;
18+
}
19+
20+
<span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getParentClass</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
21+
<span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::<span class="hljs-variable language_">class</span>;
22+
}
23+
}
24+
25+
<span class="hljs-variable">$date</span> = <span class="hljs-title class_">DateTimeImmutable</span>::<span class="hljs-title function_ invoke__">createFromMutable</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">\DateTime</span>());
26+
<span class="hljs-keyword">echo</span> <span class="hljs-variable">$date</span>::<span class="hljs-variable language_">class</span>;

test/markup/php/titles.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
final class Example extends Foo {
2+
const FOO='foo';
3+
4+
public function __construct(
5+
public readonly string $name = self::FOO
6+
) {}
7+
8+
public function getClass(): string {
9+
return DateTimeImmutable::class;
10+
}
11+
12+
public function getClassFromSelf(): string {
13+
return self::class;
14+
}
15+
16+
public static function getClassFromStatic(): string {
17+
return static::class;
18+
}
19+
20+
public static function getParentClass(): string {
21+
return parent::class;
22+
}
23+
}
24+
25+
$date = DateTimeImmutable::createFromMutable(new \DateTime());
26+
echo $date::class;

0 commit comments

Comments
 (0)