From 1d9cdf217283a5fe29d23ab3b5716385d429225e Mon Sep 17 00:00:00 2001 From: Wojciech Kania Date: Sun, 19 Dec 2021 23:29:39 +0100 Subject: [PATCH] enh(php) Left and right-side of double colon --- CHANGES.md | 2 + src/languages/php.js | 70 +++++++++++++++++++++++++--- test/markup/php/functions.expect.txt | 2 +- test/markup/php/titles.expect.txt | 12 +++-- test/markup/php/titles.txt | 6 ++- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0e6f4b1bdf..92f6d7b2f5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,8 @@ These changes should be for the better and should not be super noticeable but if Grammars: +- enh(php) Left and right-side of double colon [Wojciech Kania][] +- enh(php) add PHP constants [Wojciech Kania][] - enh(php) add PHP 8.1 keywords [Wojciech Kania][] - fix(cpp) fix `vector<<` template false positive (#3437) [Josh Goebel][] - enh(php) support First-class Callable Syntax (#3427) [Wojciech Kania][] diff --git a/src/languages/php.js b/src/languages/php.js index a23b6543c2..435ff8ab00 100644 --- a/src/languages/php.js +++ b/src/languages/php.js @@ -12,10 +12,13 @@ Category: common * */ export default function(hljs) { const regex = hljs.regex; - const IDENT_RE = '([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' + + const IDENT_RE_CORE = '[a-zA-Z0-9_\x7f-\xff]*' + // negative look-ahead tries to avoid matching patterns that are not // Perl at all like $ident$, @ident@, etc. '(?![A-Za-z0-9])(?![$]))'; + const IDENT_RE = regex.concat("([a-zA-Z_\\x7f-\\xff]", IDENT_RE_CORE); + // Will not detect camelCase classes + const PASCAL_CASE_CLASS_NAME_RE = regex.concat("([A-Z]", IDENT_RE_CORE); const VARIABLE = { scope: 'variable', match: '\\$+' + IDENT_RE, @@ -47,7 +50,7 @@ export default function(hljs) { end: /[ \t]*(\w+)\b/, contains: hljs.QUOTE_STRING_MODE.contains.concat(SUBST), }); - // list of valid whitespaces because non-breaking space might be part of a name + // list of valid whitespaces because non-breaking space might be part of a IDENT_RE const WHITESPACE = '[ \t\n]'; const STRING = { scope: 'string', @@ -314,7 +317,7 @@ export default function(hljs) { regex.concat(WHITESPACE, "+"), // to prevent built ins from being confused as the class constructor call regex.concat("(?!", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - regex.concat("\\\\?", IDENT_RE, "+"), + regex.concat("\\\\?", IDENT_RE), regex.concat(WHITESPACE, "*\\("), ], scope: { @@ -331,7 +334,7 @@ export default function(hljs) { /\b/, // to prevent keywords from being confused as the function title regex.concat("(?!fn\\b|function\\b|", normalizeKeywords(KWS).join("\\b|"), "|", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"), - regex.concat(IDENT_RE, "+"), + IDENT_RE, regex.concat(WHITESPACE, "*"), regex.lookahead(/(?=\()/) ], @@ -340,6 +343,57 @@ export default function(hljs) { } }; + const CONSTANT_REFERENCE = regex.concat(IDENT_RE, "\\b(?!\\()"); + + const LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON = { + variants: [ + { + match: [ + regex.concat( + /::/, + regex.lookahead(/(?!class\b)/) + ), + CONSTANT_REFERENCE, + ], + scope: { + 2: "variable.constant", + }, + }, + { + match: [ + /::/, + /class/, + ], + scope: { + 2: "variable.language", + }, + }, + { + match: [ + PASCAL_CASE_CLASS_NAME_RE, + regex.concat( + "::", + regex.lookahead(/(?!class\b)/) + ), + ], + scope: { + 1: "title.class", + }, + }, + { + match: [ + PASCAL_CASE_CLASS_NAME_RE, + /::/, + /class/, + ], + scope: { + 1: "title.class", + 3: "variable.language", + }, + } + ] + }; + return { case_insensitive: false, keywords: KEYWORDS, @@ -380,16 +434,17 @@ export default function(hljs) { }, VARIABLE, FUNCTION_INVOKE, + LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON, { match: [ /const/, - regex.concat(WHITESPACE, "+"), + /\s/, IDENT_RE, - regex.concat(WHITESPACE, "*="), + /\s*=/, ], scope: { 1: "keyword", - 3: "variable", + 3: "variable.constant", }, }, { @@ -425,6 +480,7 @@ export default function(hljs) { contains: [ 'self', VARIABLE, + LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON, hljs.C_BLOCK_COMMENT_MODE, STRING, NUMBER diff --git a/test/markup/php/functions.expect.txt b/test/markup/php/functions.expect.txt index f6bc43f49a..88c5d94ebe 100644 --- a/test/markup/php/functions.expect.txt +++ b/test/markup/php/functions.expect.txt @@ -13,7 +13,7 @@ $date = new DateTimeImmutable (); $date->format('Y-m-d'); -DateTimeImmutable::createFromMutable(new \DateTime('now')); +DateTimeImmutable::createFromMutable(new \DateTime('now')); str_contains (\strtoupper(substr('abcdef', -2), 'f')); diff --git a/test/markup/php/titles.expect.txt b/test/markup/php/titles.expect.txt index 03a9daaf19..05763d78a2 100644 --- a/test/markup/php/titles.expect.txt +++ b/test/markup/php/titles.expect.txt @@ -1,8 +1,8 @@ -final class Example { - const FOO='foo'; +final class Example extends Foo { + const FOO='foo'; public function __construct( - public readonly string $name = self::FOO + public readonly string $name = self::FOO ) {} public function getClass(): string { @@ -16,7 +16,11 @@ public static function getClassFromStatic(): string { return static::class; } + + public static function getParentClass(): string { + return parent::class; + } } -$date = DateTimeImmutable::createFromMutable(new \DateTime()); +$date = DateTimeImmutable::createFromMutable(new \DateTime()); echo $date::class; diff --git a/test/markup/php/titles.txt b/test/markup/php/titles.txt index ffa405fb72..6ff19f2fc6 100644 --- a/test/markup/php/titles.txt +++ b/test/markup/php/titles.txt @@ -1,4 +1,4 @@ -final class Example { +final class Example extends Foo { const FOO='foo'; public function __construct( @@ -16,6 +16,10 @@ final class Example { public static function getClassFromStatic(): string { return static::class; } + + public static function getParentClass(): string { + return parent::class; + } } $date = DateTimeImmutable::createFromMutable(new \DateTime());